2011-12-21 57 views
3

我一直在尝试使用EF 4.1代码优先的方式来映射一对多,单向的关系,例如,用户有地址但地址对用户一无所知。使用ForeignKey属性或流利的api(显示在包含的代码中),这很简单。如何在EF 4.1代码中首次创建新对象时防止引用对象的版本列更新?

在两个映射的类上添加具有Timestamp属性的Version(byte [])列时,问题就出现了。如果我们现在创建一个具有对现有(在数据库)地址中的引用并将其添加到上下文的用户实例,则在调用SaveChanges时,数据库概要分析器将显示两个数据库查询,一个是用户插入,另一个是用户插入,另一个是更新地址表以更改版本。不是我想要的。如果我在我的域中没有建立任何关系,那么我也不希望任何版本更改。如果我更改地址实例,我只想在地址上更改版本。

我怀疑因为映射使用HasMany(),因此在内部EF DbContext认为有一个需要满足的集合,并且在集合发生更改(通过添加新用户)时,它会自动更新Address的版本。所有这些尽管事实上地址没有收集财产类型ICollection<User>

所以对我的问题。在添加新用户时,我需要为关系维护类结构的映射,因为它们没有更改地址版本?

编辑: 我发现,我可以防止地址版本更新的唯一方法是减少映射到一个HasRequired(a => a.Address)只有和不再有AddressId外键在User类上。看起来如果外键“属性”是在用户显式映射或约定映射将确保地址的版本更新。

我宁愿将一些扩展应用于HasRequired来告诉上下文如何处理关系,而不是完全删除外键属性。

这是我用来证明该问题的代码:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.ComponentModel.DataAnnotations; 
using System.Data.Entity; 

namespace DbTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Address address = null; 

      // Make sure we have one address to test with 
      using (var context = new DemoContext()) 
      { 
       address = context.Addresses.FirstOrDefault(); 
       if (address == null) 
       { 
        address = new Address { Street = "My Street" }; 
        context.Addresses.Add(address); 
        context.SaveChanges(); 
       } 
      } 

      byte[] version = address.Version; 

      using (var context = new DemoContext()) 
      { 
       // Uncomment to test attaching 
       // context.Addresses.Attach(address); 
       address = context.Addresses.FirstOrDefault(); 

       var user = new User { Name = "Mark", Address = address }; 
       context.Users.Add(user); 
       context.SaveChanges(); // Results in new user inserted and a version update to the Address referenced object 
      } 

      using (var context = new DemoContext()) 
      { 
       var address2 = context.Addresses.FirstOrDefault(); 
       Console.WriteLine("Versions: {0}, {1}", BitConverter.ToString(version), BitConverter.ToString(address2.Version)); 
      } 

      Console.ReadLine(); 
     } 
    } 

    public class User 
    { 
     [Key] 
     public int UserId { get; set; } 
     public string Name { get; set; } 
     public int AddressId { get; set; } 

//  [ForeignKey("AddressId")] 
     public Address Address { get; set; } 

     [Timestamp] 
     public byte[] Version { get; set; } 
    } 

    public class Address 
    { 
     [Key] 
     public int AddressId { get; set; } 
     public string Street { get; set; } 
     [Timestamp] 
     public byte[] Version { get; set; } 
    } 

    public class DemoContext : DbContext 
    { 
     public DbSet<User> Users { get; set; } 
     public DbSet<Address> Addresses { get; set; } 

     protected override void OnModelCreating(DbModelBuilder modelBuilder) 
     { 
      modelBuilder.Entity<User>() 
         .HasRequired(a => a.Address) 
         .WithMany() 
         .HasForeignKey(u => u.AddressId); 
     } 
    } 
} 

回答

0

一种解决方法:

杠杆的暴露外键和替换此代码...

address = context.Addresses.FirstOrDefault(); 
var user = new User { Name = "Mark", Address = address }; 

...通过:

var addressId = context.Addresses.Select(a => a.AddressId).FirstOrDefault(); 
var user = new User { Name = "Mark", AddressId = addressId }; 

这不会更改地址实体的时间戳。

我不认为有任何映射选项可以避免原始代码中地址的UPDATE语句。我会按照你的假设,即EF认为你的代码是用户和地址之间关系的改变,因此更新地址,而不管用户集合是否暴露在Address模型中。

+1

虽然它的作品只有当地址,如果不是已经在上下文中才有用。如果它在上下文中,版本将被更新。 – Mark 2011-12-22 00:37:07

+0

...因此请检查它并首先从上下文中分配地址。 – nathanchere 2015-04-08 13:44:45

0

我发现唯一可以防止地址版本更新的方法是仅将映射减少到HasRequired(a => a.Address),并且不再在User类中具有AddressId外键。看起来如果外键“属性”是在用户显式映射或约定将确保地址的版本更新。

我宁愿一些extensino适用于HasRequired告诉背景下如何看待的关系,而不是完全删除外键属性。

相关问题