简体   繁体   English

实体框架可以查询数据但不能保存

[英]Entity Framework can query data but can't save

I am using EF5 and I have some entities that I wrote myself and also wrote a function that adds all the mappings to the modelbuilder configurations. 我正在使用EF5,我有一些实体是我自己写的,并且还写了一个函数,该函数将所有映射添加到模型构建器配置中。

Running a simple test query, I can query items from a table successfully but when I try to add a new item and save, I get an exception that the primary key of my entity is null, even though I gave it a value. 运行一个简单的测试查询,我可以成功地从表中查询项目,但是当我尝试添加新项目并保存时,我得到了一个例外,即我的实体的主键为null,即使我给了它一个值。

It's quite possible that I messed up the mappings, but I don't know why it would work for a query and not a save. 我很可能弄乱了映射,但是我不知道为什么它适用于查询而不是保存。

public class User : IMappedEntity 
{
    [Key]
    [Column("USER_ID")]
    public int UserID { get; set; }

    [Column("FIRST_NAME")]
    public String FirstName { get; set; }

    [Column("LAST_NAME")]
    public String LastName { get; set; }

}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
        modelBuilder.Conventions.Remove<System.Data.Entity.ModelConfiguration.Conventions.PluralizingEntitySetNameConvention>();
        modelBuilder.Conventions.Remove<System.Data.Entity.ModelConfiguration.Conventions.PluralizingTableNameConvention>();

    var addMethod = (from m in (modelBuilder.Configurations).GetType().GetMethods()
                     where m.Name == "Add" 
                        && m.GetParameters().Count() == 1
                        && m.GetParameters()[0].ParameterType.Name == typeof(EntityTypeConfiguration<>).Name
                     select m).First();

    if(mappings != null)
    {
        foreach(var map in mappings)
        {
            if(map != null && !mappedTypes.Contains(map.GetType()))
            {
                var thisType = map.GetType();

                if (thisType.IsGenericType)
                {
                    thisType = map.GetType().GenericTypeArguments[0];
                }

                var thisAddMethod = addMethod.MakeGenericMethod(new[] {thisType});
                thisAddMethod.Invoke(modelBuilder.Configurations, new[] { map });
                mappedTypes.Add(map.GetType());
            }
        }
    }
}

private List<Object> BuildMappings(IEnumerable<Type> types)
{
    List<Object> results = new List<Object>();

    var pkType = typeof(KeyAttribute);
    var dbGenType = typeof(DatabaseGeneratedAttribute);

    foreach (Type t in types)
    {
        String tableName = GetTableName(t);
        String schemaName = GetSchema(t);
        var mappingType = typeof(EntityTypeConfiguration<>).MakeGenericType(t);
        dynamic mapping = Activator.CreateInstance(mappingType);

        if (!String.IsNullOrWhiteSpace(schemaName))
           mapping.ToTable(tableName, SchemaName.ToUpper());
        else
           mapping.ToTable(tableName);

        var keys = new List<PropertyInfo>();

        foreach (PropertyInfo prop in t.GetProperties())
        {
            String columnName = prop.Name;

            if(Attribute.IsDefined(prop, typeof(ColumnAttribute)))
            {
                columnName  = (prop.GetCustomAttribute(typeof(ColumnAttribute)) as ColumnAttribute).Name;
            }

            if(Attribute.IsDefined(prop, pkType))
               keys.Add(prop);

            var genFunc = (typeof(Func<,>)).MakeGenericType(t, prop.PropertyType);
            var param = Expression.Parameter(t, "t");
            var body = Expression.PropertyOrField(param, prop.Name);
            dynamic lambda = Expression.Lambda(genFunc, body, new ParameterExpression[] { param });

            //if (prop.PropertyType == typeof(Guid) || prop.PropertyType == typeof(Nullable<Guid>))
            //{
            //    mapping.Property(lambda).HasColumnType("Guid");
            //}
            //else
            mapping.Property(lambda).HasColumnName(columnName);

            if (Attribute.IsDefined(prop, dbGenType))
               mapping.Property(lambda).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed);
        }

        if (keys.Count == 0)
           throw new InvalidOperationException("Entity must have a primary key");
        dynamic entKey = null;

        if(keys.Count == 1)
        {
            var genFunc = (typeof(Func<,>)).MakeGenericType(t, keys[0].PropertyType);
            var param = Expression.Parameter(t, "t");
            var body = Expression.PropertyOrField(param, keys[0].Name);
            entKey = Expression.Lambda(genFunc, body, new ParameterExpression[] { param });
        }
        else
        {
            //if entity uses a compound key, it must have a function named "GetPrimaryKey()" which returns Expression<Func<EntityType,Object>>
            //this is because I can't create an expression tree that creates an anonymous type
            entKey = t.GetMethod("GetPrimaryKey");
        }

        mapping.HasKey(entKey);

        results.Add(mapping);
    }

    return results;
}

static void Main(string[] args)
{
    using (var ctx = new DQSA.Data.DBContext("DQSATEST"))
    {
        var xxx = (from u in ctx.Query<DQSA.Data.Entities.User>()
                   select u).ToList(); //this works, I can see my user

        ctx.Set<DQSA.Data.Entities.User>().Add(new DQSA.Data.Entities.User()
            { UserID = 0,
              FirstName="Sam",
              LastName="Sam"
            });

        ctx.SaveChanges(); //get an exception here

        xxx = (from u in ctx.Query<DQSA.Data.Entities.User>()
               select u).ToList();
    }
}

It looks like your UserID property is being mapped as an Identity column by convention so the EF query provider thinks it doesn't need to insert that value and the database complains because the field is not nullable. 看来您的UserID属性已按照约定映射为Identity列,因此EF查询提供程序认为它不需要插入该值,并且数据库抱怨该字段不可为空。

You can override the convention in your model by using the DatabaseGeneratedAttribute ... 您可以使用DatabaseGeneratedAttribute在模型中覆盖约定。

public class User : IMappedEntity 
{
    [Key]
    [Column("USER_ID")]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int UserID { get; set; }

    ...
}

or by removing the convention globally (in your DbContext's OnModelCreating() method) ... 或通过全局删除约定(在DbContext的OnModelCreating()方法中)...

modelBuilder.Conventions.Remove<StoreGeneratedIdentityKeyConvention>();

我认为您需要尝试使用一个或几个记录然后使用context.SaveChanges()作为种子。

by default, entity framework should mark the primary key column of a new table created with Code First as an identity column. 默认情况下,实体框架应将使用Code First创建的新表的主键列标记为标识列。 Did the database exist prior to your code, or did you use code first to create it? 数据库是在代码之前存在的,还是您首先使用代码创建的?

Can you verify in Management Studio that the column has identity turned on for that field? 您可以在Management Studio中验证该列的字段身份已打开吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM