【发布时间】:2011-12-21 12:10:26
【问题描述】:
我一直在尝试使用 EF 4.1 代码优先映射一对多、单向关系,例如用户有地址,但地址对用户一无所知。使用 ForeignKey 属性或 fluent api(显示在包含的代码中)很容易实现。
在两个映射类上添加带有 Timestamp 属性的 Version (byte[]) 列时会出现问题。如果我们现在创建一个引用现有(在数据库中)地址的用户实例并将其添加到上下文中,则在调用 SaveChanges 时,数据库分析器将显示两个数据库查询,一个是用户插入,另一个是是对地址表的更新以更改版本。不是我想要的。如果我在我的域中没有建模任何关系,那么我也不希望任何版本更改。如果我更改了 Address 实例,我只想更改 Address 的版本。
我怀疑由于映射使用HasMany(),EF DbContext 在内部认为存在一个需要满足的集合,并且随着集合已更改(通过添加新用户),它会自动更新地址的版本。尽管 Address 没有 ICollection<User> 类型的集合属性,但所有这一切。
所以我的问题。当我添加新用户时,我需要为关系设置什么映射以保持类结构不变而不更改地址版本?
编辑: 我发现,我可以阻止更新 Address 版本的唯一方法是将映射减少到 HasRequired(a => a.Address) 并且不再具有 User 类上的 AddressId 外键。如果外键“属性”在用户上,则显式映射或约定映射将确保地址的版本得到更新。
我更愿意对 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);
}
}
}
【问题讨论】: