有没有一种简单的方法来验证数据库架构是否正是我所期望的使用实体框架?

发布于 2024-10-28 04:05:09 字数 203 浏览 2 评论 0原文

我希望我的应用程序在启动时验证数据库一致性。

有没有一种简单的方法来验证数据库架构是否正是我所期望的使用实体框架?

EF4本身做了一些验证。如果映射类型包含目标表中不存在的列,则在 EF4 实现时会触发异常。美好的。然而,它不做一些事情:它首先不验证整个数据库。当目标表包含未映射的列时,它不会触发异常。

有没有一种简单的方法可以实现这一点?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

猫腻 2024-11-04 04:05:09

至于您的具体示例,EF 无法知道您未告诉它的有关架构的内容。如果有一个字段没有映射,只要 SQL 语句(尤其是插入)对表成功,EF 并不关心。也许该字段已被弃用,但对于某些遗留应用程序仍然需要保留它,或者因为从活动数据库中删除字段非常麻烦。

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.

牵强ㄟ 2024-11-04 04:05:09

我不知道任何简单的方法,但你可以做到这一点的一种方法(至少对于 SqlServer)是让 EF 为你生成一个创建脚本(我不确定它是否能够为你做到这一点,但 NHibernate 是如此)也许有办法)并使用 Sql Server 具有的 Smo 库根据服务器中的内容解析字符串。

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.

冷情妓 2024-11-04 04:05:09

我一直在 代码审查问题与此相关的Github存储库

我选择不使用 MetadataWorkspace,但可以修改代码以使用它而不是反射。

编辑

这是相关的代码示例:

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));
}

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));
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文