2015-08-13 93 views
1

我正在使用EF5,並且我有一些自己編寫的實體,並且還編寫了一個將所有映射添加到模型構建器配置的函數。實體框架可以查詢數據但不能保存

運行一個簡單的測試查詢,我可以成功查詢表中的項目,但是當我嘗試添加一個新項目並保存時,我得到一個異常,即我的實體的主鍵爲null,即使我給了它一個值。

這很可能是我搞砸了映射,但我不知道它爲什麼會用於查詢而不是保存。

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(); 
    } 
} 
+0

你能嘗試一種簡化的測試方法?只需創建一個新用戶並嘗試添加它,然後保存上下文? – SpaceSteak

+0

這就是問題所在。我在數據庫中有一條記錄,我可以很好地閱讀它。我嘗試添加一個新用戶,並在保存更改時引發異常。它說User_ID不能爲空。 – Sam

+1

您的User_ID是標識列嗎? –

回答

2

它看起來像您的UserID屬性按照約定映射爲Identity列,所以EF查詢提供程序認爲它不需要插入該值,並且數據庫抱怨,因爲該字段不可爲空。

您可以通過使用DatabaseGeneratedAttribute覆蓋模型中的約定......

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

    ... 
} 

或消除全球公約(在你的DbContext的OnModelCreating()法)...

modelBuilder.Conventions.Remove<StoreGeneratedIdentityKeyConvention>(); 
1

我想你需要嘗試與一個或幾個記錄種子然後context.SaveChanges()

+0

這就是問題所在。我在數據庫中有一條記錄,我可以很好地閱讀它。我嘗試添加一個新用戶,並在保存更改時引發異常。它說User_ID不能爲空。 – Sam

0

默認情況下,實體框架應標示使用代碼首先爲創建新表的主鍵列一個身份專欄。數據庫是否存在於代碼之前,還是先使用代碼創建它?

您是否可以在Management Studio中驗證該列是否爲該字段打開了標識?

+0

我用一個語句創建了表。我確實證實這不是一個身份專欄。 – Sam