web-dev-qa-db-ja.com

挿入後にエンティティナビゲーションプロパティを取得する

次の2つのクラスがあります。

public class Reward 
{
    public int Id { get; set; }
    public int CampaignId { get; set;
    public virtual Campaign Campaign { get; set; }
}

public class Campaign 
{
    public int Id { get; set; }
    public virtual ICollection<Reward> Rewards { get; set; }
}

これにより、DbContextやマッピングなど、明らかに必要なものがすべて揃っています。

ここで、Rewardエンティティを作成して、次のように挿入するとします。

var reward = new Reward { CampaignId = 1 };
context.Set<Reward>().Add(reward);
context.SaveChanges();

reward = context.Set<Reward>().SingleOrDefault(a => a.Id == reward.Id);
//reward.Campaign is null

Id 1のキャンペーンがあるのは明らかなので、FK制約は満足です。この挿入後、報酬エンティティには新しいIDが設定されます。問題は、報酬がまだ私が作成した報酬エンティティに過ぎないことです。これにより、reward.Campaignプロパティはnullになります。 EFは挿入されたエンティティをメモリに保持しているようで、.SingleOrDefault(a => a.Id ==報酬.Id)を実行すると、新しいプロキシではなく、単にメモリ内のエンティティを返します。これはおそらく良いことです。

したがって、質問は次のとおりです。挿入した後、またはナビゲーションプロパティをプロキシとして持つ新しいプロキシを取得した後に、ナビゲーションプロパティにアクセスまたはロードする方法。

私はおそらく間違った方法で挿入していますか?

41
Justus Burger

私があなたを正しく理解していれば、外部キープロパティを介して関係を確立した後、複雑なプロパティを熱心にロードしようとしています。

SaveChanges()は、複雑なプロパティをロードする方法では何もしません。新しいオブジェクトを追加する場合、多くても主キープロパティを設定します。

reward = context.Set<Reward>().SingleOrDefault(a => a.Id == reward.Id);行もCampaignをロードする方法では何もしません。これは、報酬オブジェクトがコンテキストに関連付けられていないためです。 explicitlyEFにその複雑なオブジェクトをロードするかアタッチして、遅延ロードに魔法をかける必要があります。

したがって、context.SaveChanges();の後、_reward.Campaign_をロードするための3つのオプションがあります。

  1. Attach()コンテキストに報酬を与え、Campaignを遅延ロード(アクセス時にロード)できるようにする

    _context.Rewards.Attach(reward);
    _

    注:コンテキストのスコープ内で_reward.Campaign_のみを遅延ロードできるため、コンテキストライフスパン内のプロパティにアクセスしない場合は、オプション2または3を使用します。

  2. 手動でLoad()Campaignプロパティ

    _context.Entry(reward).Reference(c => c.Campaign).Load();
    _
  3. 手動でInclude()Campaignプロパティ

    _reward = context.Rewards.Include("Campaigns")
        .SingleOrDefault(r => r.Id == reward.Id);
    _

    ただし、Loadが既にメモリにあるので、rewardをお勧めします。

詳細については、 this msdn doc の関連オブジェクトのロードセクションをご覧ください。

65
Carrie Kendall

rewardオブジェクトをnew Reward()として作成しているため、EFにはプロキシがありません。代わりに、次のようにDbSet.Createを使用して作成します。

var reward = context.Set<Reward>().Create();
reward.CampaignId = 5;
context.SaveChanges();

次に、DbSetに添付します。

context.Rewards.Attach(reward);

最後に、遅延読み込みを使用して関連エンティティを取得できるようになりました。

var campaign = reward.Campaign;
9
DavidG

私は問題の簡単な解決策を持っています。

キャンペーンIDを報酬に追加する代わりに、キャンペーンオブジェクトを追加します。

var _campaign = context.Campaign.First(c=>c.Id == 1);//how ever you get the '1'
var reward = new Reward { Campaign = _campaign };
context.Set<Reward>().Add(reward);
context.SaveChanges();

//reward.Campaign is not null

ここでは、エンティティフレームワークがすべての面倒な作業を行います。

おそらく、Campaignオブジェクト全体をロードするのは無駄だと考えているのでしょうが、それを使用する場合(見た目はあなたのようです)、なぜそうなのかわかりません。キャンペーンオブジェクトからナビゲーションプロパティにアクセスする必要がある場合は、上記のフェッチ時にincludeステートメントを使用することもできます...

var _campaign = context.Campaign.include(/*what ever you may require*/).First(c=>c.Id = 1);
1
Steven

Include()を使用してみましたか?このようなもの:

reward = context.Set<Reward>().Include("Campaigns").SingleOrDefault(a => a.Id == reward.Id);
1
Floremin

Carrie KendallおよびDavidGに加えて(VB.NETの場合):

Dim db As New MyEntities
Dim r As Reward = = db.Set(Of Reward)().Create()
r.CampaignId = 5
db.Reward.Add(r) ' Here was my problem, I was not adding to database and child did not load
db.SaveChanges()

次に、プロパティr.Campaign 利用可能です

0
Dani