3

我在“项目”和“模板”之间有多对一的关联。

项目具有“模板”类型的属性。

该关联不是双向的(“模板”不知道“项目”)。

我对“项目”关联的实体映射是:

this.HasOptional(p => p.Template);

如果我在未指定模板的情况下创建“项目”,则 null 将正确插入“项目”表的“模板 ID”列中。

如果我指定了一个模板,那么模板的 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 没有更新。

这对我来说没有意义。我什至尝试在我的代码中将“项目”的“模板”属性显式设置为 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。

4

2 回答 2

4

好的,您只需要在对其执行任何操作之前加载导航属性。通过加载它,您基本上将它注册到 EF 查找的 ObjectStateManager,以生成作为 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'
于 2011-01-16T22:15:34.443 回答
2

如果这是 EF CTP5 中的错误(而不是我的代码:p),那么我想出了两种解决方法。

1)使关联双向。就我而言,这意味着将以下内容添加到我的 ProjectTemplate 类中:

public virtual ICollection<Project> Projects {get;set;}

完成此操作后,为了将项目的“模板”属性设置为 null,您可以从模板中删除项目 - 有点落后,但它可以工作:

    var project = repo.GetById(id);
var template = project.Template;
template.Projects.Remove(project);
// save changes

2)第二个选项(我更喜欢但它仍然闻起来)是在您的域对象上添加外键。就我而言,我必须将以下内容添加到项目中:

    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;
    }

我不能说我很高兴用外键污染我的域模型,但我找不到任何替代方案。

希望这可以帮助。本

于 2011-01-16T17:01:43.960 回答