如何确定数据库中哪些字段被指定为NOT NULL?

时间:2015-06-22 11:37:37

标签: c# entity-framework entity-framework-6

我的解决方案中有一个数据库优先,代码生成的EF数据项目。

我需要做的是确定我的模型中的哪些字段在数据库中定义为NOT NULL(即必需)。这对于值类型(整数,浮点数,小数等)并不困难,因为如果DB允许空值,则它们被EF代码生成器定义为可空类型。

但是,当字段属于引用类型时,我无法弄清楚如何确定字段是否在数据库中定义为NULL / NOT NULL

这似乎是一个显而易见的问题,但我无法通过Google或其他方式找到解决方案。使用EF模型时,如何在保存操作成功之前查询模型中是否需要填充哪些字段?

一种不同的方法

重新访问此问题后,我想我会做进一步的研究。首先,通过Visual Studio添加实体,在单步执行EF向导后,生成了.edmx文件。我之前已多次访问此文件,并且我已更新并将表添加到此文件中。

今天,我决定在设计师中打开它并查看我的某些领域的一些属性。果然,我的nvarchar NOT NULL字段中有一个名为Nullable的属性。

那么,我如何使用EF并获取Nullable属性设置为false的实体上的字段列表?

2 个答案:

答案 0 :(得分:5)

You can get this information from Entity Framework's the meta data:

var metadata = ((IObjectContextAdapter)db).ObjectContext.MetadataWorkspace;
var tables = metadata.GetItemCollection(DataSpace.SSpace)
                     .GetItems<EntityContainer>().Single()
                     .BaseEntitySets
                     .OfType<EntitySet>()
                     .Where(s => !s.MetadataProperties.Contains("Type") || s.MetadataProperties["Type"].ToString() == "Tables");

foreach (var table in tables)
{
    Console.WriteLine(string.Format("{0}.{1}", table.Schema, table.Name));
    foreach (var member in table.ElementType.Members)
    {
        var column = string.Format("    {0}, Nullable: {1}",
            member.Name,
            ((TypeUsage)member.MetadataProperties["TypeUsage"].Value).Facets["Nullable"].Value);
        Console.WriteLine(column);
    }
}

(where db is a DbContext)

This will give you an output like:

dbo.Category
    CategoryId, Nullable: False
    CategoryName, Nullable: False
    Description, Nullable: True
    RowVersion, Nullable: False
dbo.Product
    ProductId, Nullable: False
    ProductName, Nullable: False
    QuantityPerUnit, Nullable: False
    UnitPrice, Nullable: True
    StartDate, Nullable: False
    RowVersion, Nullable: False
    Image, Nullable: True
dbo.CategoryProduct
    CategoryID, Nullable: False
    ProductID, Nullable: False

I borrowed the first part from Rowan Miller.

答案 1 :(得分:0)

我不确定这是否是最佳方法,但我为DbContext类创建了一个扩展方法,该方法采用数据表的字符串名称,然后在sys表中查询此元数据信息。具体来说,我创建了以下2个类和扩展方法。

<强> TableSchema表

这是用于存储相关架构详细信息的高级表类:

public class TableSchema
{
    public string Database {  get; internal set; }
    public string TableName { get; internal set; }
    public List<ColumnSchema> Columns { get; internal set; }
}

<强> ColumnSchema

与TableSchema非常相似,这个类将包含每列的所有与架构相关的详细信息。

public class ColumnSchema
{
    public string ColumnName { get; internal set; }
    public int ColumnPosition { get; internal set; }
    public string Collation { get; internal set; }
    public string TypeName { get; internal set; }
    public short Size { get; internal set; }
    public byte Precision { get; internal set; }
    public byte Scale { get; internal set; }
    internal int _PK { get; set; }
    public bool IsIdentity { get; internal set; }
    public bool IsNullable { get; internal set; }

    public bool IsPrimaryKey
    {
        get { return _PK == 1; }
    }
}

扩展方法(GetDbTableSchema)

此方法扩展了DbContext类。这使得获取基础表详细信息就像将名称传递给方法一样简单,就在实例化的上下文之外。

public static class DbContextExtensions
{
    public static TableSchema GetDbTableSchema(this DbContext ctx, string tableName)
    {
        string qry = string.Format(
    @"SELECT * FROM (SELECT DISTINCT 
        c.name AS ColumnName, 
        c.column_id AS ColumnPosition,
        ty.name AS TypeName,
        c.max_length AS Size,
        c.precision AS Precision,
        c.scale AS Scale,
        CASE WHEN ic.column_id IS NOT NULL THEN 1 ELSE 0 END AS [_PK], 
        c.is_identity AS [IsIdentity],
        c.is_nullable AS [IsNullable]
    FROM sys.columns c 
        INNER JOIN sys.tables t ON c.object_id = t.object_id 
        INNER JOIN sys.types ty ON c.system_type_id = ty.system_type_id
        LEFT OUTER JOIN sys.indexes i ON c.object_id = i.object_id AND i.is_primary_key = 1
            LEFT OUTER JOIN sys.index_columns ic ON i.object_id = ic.object_id 
                AND i.index_id = ic.index_id 
                AND c.column_id = ic.column_id
    WHERE t.name = '{0}') t
    ORDER BY _PK DESC, ColumnPosition", tableName);", tableName);
        return new TableSchema
        {
            Columns = ctx.Database.SqlQuery<ColumnSchema>(qry).ToList(),
            Database = ctx.Database.Connection.Database,
            TableName = tableName
        };
    }
}

用法非常简单。假设您拥有数据表的名称,请将其传递给您的上下文。

using (var ctx = new MyEntityContext()
{
    TableSchema ts = ctx.GetDbTableSchema("MyTable");

    foreach (ColumnSchema cs in ts.Columns)
    {
        Debug.WriteLine("Column: {0}, {1}", cs.ColumnName, cs.IsNullable ? "NULL" : "NOT NULL");
    }
}