3

「プロジェクト」と「テンプレート」の間に多対1の関連付けがあります。

プロジェクトには「テンプレート」タイプのプロパティがあります。

関連付けは双方向ではありません (「テンプレート」は「プロジェクト」を認識しません)。

「プロジェクト」の関連付けのエンティティ マッピングは次のとおりです。

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 を使用していることに注意してください。

4

2 に答える 2

4

わかりました。何かを行う前に、ナビゲーション プロパティをロードする必要があります。それをロードすることで、基本的に、SaveChanges() の結果として更新ステートメントを生成するために EF が調べる ObjectStateManager に登録します。

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)、私が思いついた 2 つの回避策があります。

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) 2 番目のオプション (私はこれを好みましたが、まだにおいがします) は、ドメイン オブジェクトに外部キーを追加することです。私の場合、次をプロジェクトに追加する必要がありました。

    public Guid? TemplateId { get; set; }
    public virtual ProjectTemplate Template { get; set; }

外部キーが null 許容型であることを確認してください。

次に、マッピングを次のように変更する必要がありました。

this.HasOptional(p => p.Template)
.WithMany()
.HasForeignKey(p => p.TemplateId);

次に、テンプレートを null に設定するために、Project にヘルパー メソッドを追加しました (外部キーを null に設定するだけで実際に機能します)。

    public virtual void RemoveTemplate() {
        this.TemplateId = null;
        this.Template = null;
    }

ドメイン モデルを外部キーで汚染することに満足しているとは言えませんが、代替手段が見つかりませんでした。

お役に立てれば。ベン

于 2011-01-16T17:01:43.960 に答える