我在“项目”和“模板”之间有多对一的关联。
Project具有“Template”类型的属性。
关联不是双向的(“模板”不知道“项目”)。
我在“项目”上关联的实体映射是:
this.HasOptional(p => p.Template);
如果我在未指定模板的情况下创建“项目”,则将null正确插入“项目”表的“TemplateId”列中。
如果我指定模板,则会正确插入模板的ID。生成的SQL:
update [Projects]
set [Description] = '' /* @0 */,
[UpdatedOn] = '2011-01-16T14:30:58.00' /* @1 */,
[ProjectTemplateId] = '5d2df249-7ac7-46f4-8e11-ad085c127e10' /* @2 */
where (([Id] = '8c1b2d30-b83e-4229-b0c3-fed2e36bf396' /* @3 */)
and [ProjectTemplateId] is null)
但是,如果我尝试更改模板甚至将其设置为null,则不会更新templateId。生成的SQL:
update [Projects]
set [UpdatedOn] = '2011-01-16T14:32:14.00' /* @0 */
where ([Id] = '8c1b2d30-b83e-4229-b0c3-fed2e36bf396' /* @1 */)
如您所见,TemplateId未更新。
这对我来说没有意义。我甚至尝试在我的代码中明确地将“Project”的“Template”属性设置为null,并且在单步执行代码时你可以看到它完全没有效果!
谢谢, 本
[更新]
最初我以为这是因为忘记在我的DbContext上添加IDbSet属性引起的。但是,现在我已经进一步测试了,我不太确定。以下是一个完整的测试用例:
public class PortfolioContext : DbContext, IDbContext
{
public PortfolioContext(string connectionStringName) : base(connectionStringName) { }
public IDbSet<Foo> Foos { get; set; }
public IDbSet<Bar> Bars { get; set; }
protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder) {
modelBuilder.Configurations.Add(new FooMap());
modelBuilder.Configurations.Add(new BarMap());
base.OnModelCreating(modelBuilder);
}
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class {
return base.Set<TEntity>();
}
}
public class Foo {
public Guid Id { get; set; }
public string Name { get; set; }
public virtual Bar Bar { get; set; }
public Foo()
{
this.Id = Guid.NewGuid();
}
}
public class Bar
{
public Guid Id { get; set; }
public string Name { get; set; }
public Bar()
{
this.Id = Guid.NewGuid();
}
}
public class FooMap : EntityTypeConfiguration<Foo>
{
public FooMap()
{
this.ToTable("Foos");
this.HasKey(f => f.Id);
this.HasOptional(f => f.Bar);
}
}
public class BarMap : EntityTypeConfiguration<Bar>
{
public BarMap()
{
this.ToTable("Bars");
this.HasKey(b => b.Id);
}
}
测试:
[Test]
public void Template_Test()
{
var ctx = new PortfolioContext("Portfolio");
var foo = new Foo { Name = "Foo" };
var bar = new Bar { Name = "Bar" };
foo.Bar = bar;
ctx.Set<Foo>().Add(foo);
ctx.SaveChanges();
object fooId = foo.Id;
object barId = bar.Id;
ctx.Dispose();
var ctx2 = new PortfolioContext("Portfolio");
var dbFoo = ctx2.Set<Foo>().Find(fooId);
dbFoo.Bar = null; // does not update
ctx2.SaveChanges();
}
请注意,这是使用SQL CE 4。
答案 0 :(得分:4)
好的,您只需要在执行任何操作之前加载导航属性。通过加载它,你基本上用ObjectStateManager注册它,EF调查以生成更新语句作为SaveChanges()的结果。
using (var context = new Context())
{
var dbFoo = context.Foos.Find(fooId);
((IObjectContextAdapter)context).ObjectContext.LoadProperty(dbFoo, f => f.Bar);
dbFoo.Bar = null;
context.SaveChanges();
}
此代码将导致:
exec sp_executesql N'update [dbo].[Foos]
set [BarId] = null
where (([Id] = @0) and ([BarId] = @1))
',N'@0 uniqueidentifier,@1 uniqueidentifier',@0='A0B9E718-DA54-4DB0-80DA-C7C004189EF8',@1='28525F74-5108-447F-8881-EB67CCA1E97F'
答案 1 :(得分:2)
如果这是EF CTP5中的错误(而不是我的代码:p),我提出了两种解决方法。
1)使关联双向。在我的例子中,这意味着将以下内容添加到我的ProjectTemplate类中:
public virtual ICollection<Project> Projects {get;set;}
完成此操作后,为了将项目的“Template”属性设置为null,您只需从模板中删除项目 - 稍微向后但它可以工作:
var project = repo.GetById(id);
var template = project.Template;
template.Projects.Remove(project);
// save changes
2)第二个选项(我更喜欢但仍然有气味)是在域对象上添加外键。在我的情况下,我不得不将以下内容添加到Project:
public Guid? TemplateId { get; set; }
public virtual ProjectTemplate Template { get; set; }
确保外键是可以为空的类型。
然后我必须改变我的映射:
this.HasOptional(p => p.Template)
.WithMany()
.HasForeignKey(p => p.TemplateId);
然后,为了将Template设置为null,我向Project添加了一个辅助方法(它实际上只通过将外键设置为null来实现):
public virtual void RemoveTemplate() {
this.TemplateId = null;
this.Template = null;
}
我不能说我很高兴用外键污染我的域模型,但我找不到任何其他选择。
希望这会有所帮助。 本