I want my application to verify database consitency at startup time.
Is there an easy way to verify whether or not the database schema is exactly what I expect it to be using Entity Framework?
EF4 itself does some verification. If a mapped type contains a column that does not exist in the target table, when EF4 is materializing it triggers an exception. Fine. However there are some things it does not do: It does not verify the entire database at first. It does not trigger an exception when the target table contains a column that is not mapped.
Is there an easy way I can accomplish that?
As to your specific example, EF can't know what you don't tell it about the schema. If there's a field that isn't mapped, as long as SQL statements (especially inserts) succeed against the table EF really doesn't care. Maybe that field is deprecated, but it still has to be kept for some legacy app, or because it's a pain and a half to remove a field from an active DB.
I don't know any easy ways, but one way you could do it (at least for SqlServer) is having EF generating a create script for you (I'm not sure it is able to do it for you, but NHibernate is so maybe there is a way) and parse the string against what you have in the server using the Smo library that Sql Server has.
I have been using a specific solution for this in a code review question with this related Github repository.
I opted not to use the MetadataWorkspace, but the code can be modified to use it instead of reflection.
EDIT
Here's the relevant code sample:
public Validation(ADbContext db)
{
_connectionString = db.Database.Connection.ConnectionString;
}
private readonly string _connectionString;
public ILookup<string, List<string>> Run()
{
// A tolerance to deal with Entity Framework renaming
var modelValidation = GetModelProperties<ADbContext>(tolerance);
var isValid = !modelValidation.Any(v => v.Any(x => x.Count > 0));
if (!isValid)
Logger.Activity(BuildMessage(modelValidation));
return modelValidation;
}
public string BuildMessage(ILookup<string, List<string>> modelValidation)
{
// build a message to be logged
}
public List<string> GetMissingColumns(IEnumerable<string> props, IEnumerable<string> columns, int tolerance)
{
// compare whether the entity properties have corresponding columns in the database
var missing = props.Where(p => !columns.Any(c => p.StartsWith(c) && Math.Abs(c.Length - p.Length) <= tolerance)).ToList();
return missing;
}
public string[] GetSQLColumnNames(Type t)
{
SqlConnection connection = new SqlConnection(_connectionString);
var table = t.Name;
DynamicParameters dparams = new DynamicParameters();
dparams.Add("Table", table);
var query = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @Table ";
// Using dapper to retrieve list of columns from that table
List<string> columns = connection.Query<string>(query, dparams).ToList();
return columns.ToArray();
}
static string[] GetEntityPropertyNames(Type t)
{
var properties = t.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.CanRead && !p.PropertyType.FullName.Contains("My.Namespace") && !p.PropertyType.FullName.Contains("Collection"))
.Select(p => p.Name)
.ToArray();
// these conditions excludes navigation properties: !p.PropertyType.FullName.Contains("My.Namespace") && !p.PropertyType.FullName.Contains("Collection")
return properties;
}
ILookup<string, List<string>> GetModelProperties<T>(int tolerance, T source = default(T))
{
var properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.PropertyType.IsGenericType)
.Select(p => p.PropertyType.GetGenericArguments()[0])
.Select(p => new
{
Entity = p.Name,
Properties = GetEntityPropertyNames(p),
Columns = GetSQLColumnNames(p),
})
.ToArray();
return properties.ToLookup(p => p.Entity, p => GetMissingColumns(p.Properties, p.Columns, tolerance));
}
来源:https://stackoverflow.com/questions/5465190/is-there-an-easy-way-to-verify-whether-or-not-the-database-schema-is-exactly-wha