18

ここ数日、リポジトリからエンティティを取得するのに苦労しています(DbContext)。

アトミックアクションですべてのエンティティを保存しようとしています。したがって、さまざまなエンティティが一緒になって、私にとって価値のあるものを表しています。すべてのエンティティが「有効」である場合、それらすべてをデータベースに保存できます。エンティティ'a'はすでにリポジトリに保存されており、'エンティティ'b'を検証するために取得する必要があります。

ここで問題が発生します。DbSet<TEntity>私のリポジトリは、Linq2Sql(Include()ナビゲーションプロパティなど)でうまく機能するクラスに依存しています。ただし、にDbSet<TEntity>は「追加」状態のエンティティは含まれていません。

したがって、(私が知る限り)2つのオプションがあります。

  • を使用して、使用ChangeTracker可能なエンティティを確認し、それらに基づいてセットにクエリを実行しますEntityState
  • プロパティを使用しDbSet<TEntity>.Localます。

Linq2Sqlを使用してプロパティをナビゲーションChangeTrackerできるような方法で動作させるには、余分な作業が必要になるようです。Include()

私には少し奇妙にDbSet<TEntity>.Local思えます。名前だけかもしれません。パフォーマンスがあまり良くない(DbSet <>自体より遅い)ものを読んだだけです。それが虚偽の陳述であるかどうかわからない。

EntityFrameworkの経験が豊富な人は、これに光を当てることができますか?たどる「賢い」道は何ですか?または、幽霊が表示されているので、常に.Localプロパティを使用する必要がありますか?

コード例で更新


何がうまくいかないかの例

    public void AddAndRetrieveUncommittedTenant()
    {
        _tenantRepository = new TenantRepository(new TenantApplicationTestContext());

        const string tenantName = "testtenant";

        // Create the tenant, but not call `SaveChanges` yet until all entities are validated 
        _tenantRepository.Create(tenantName);

        //
        // Some other code
        //

        var tenant = _tenantRepository.GetTenants().FirstOrDefault(entity => entity.Name.Equals(tenantName));

        // The tenant will be null, because I did not call save changes yet,
        // and the implementation of the Repository uses a DbSet<TEntity>
        // instead of the DbSet<TEntity>.Local.
        Assert.IsNotNull(tenant);

        // Can I safely use DbSet<TEntity>.Local ? Or should I play 
        // around with DbContext.ChangeTracker instead?
    }

私の使い方の例Repository

Repositoryにはこの方法があります:

    public IQueryable<TEntity> GetAll()
    {
        return Context.Set<TEntity>().AsQueryable();
    }

私がこの方法でビジネスコードで使用するもの:

    public List<Case> GetCasesForUser(User user)
    {
        return _repository.GetAll().
            Where(@case => @case.Owner.EmailAddress.Equals(user.EmailAddress)).
            Include(@case => @case.Type).
            Include(@case => @case.Owner).
            ToList();
    }

DbSetそれが主に私が同じような変数に固執することを好む理由です。Includeナビゲーションプロパティに柔軟性が必要です。Iを使用するChangeTrackerと、でエンティティを取得しますList。これにより、後で関連するエンティティを遅延ロードすることができなくなります。

これが理解できない強気*tに近い場合は、質問を改善できるようにお知らせください。どうしても答えが必要です。

事前にたくさんThx!

4

3 に答える 3

16

DbSetに対して「簡単に」クエリを発行し、新しく作成されたアイテムを検出できるようにする場合は、各エンティティの作成後にSaveChanges()を呼び出す必要があります。永続エンティティを操作するために「作業単位」スタイルのアプローチを使用している場合、作業単位でUoW内のすべてのアクションをDBトランザクションとしてラップできるため(つまり、UoWのときに新しいTransactionScopeを作成できるため)、これは実際には問題ありません。が作成され、UoWが完了したときにCommit()を呼び出します)。この構造では、変更はDBに送信され、DbSetには表示されますが、他のUoWには表示されません(使用する分離レベルを法として)。

このオーバーヘッドが必要ない場合は、適切なタイミングでLocalを使用するようにコードを変更する必要があります(Localを調べて、見つからなかった場合はDbSetに対してクエリを発行する必要があります)。探していた)。DbSetのFind()メソッドも、このような状況で非常に役立ちます。ローカルまたはDBのいずれかで主キーによってエンティティを検索します。したがって、主キーでアイテムを検索するだけでよい場合、これは非常に便利です(パフォーマンス上の利点もあります)。

于 2013-12-06T18:34:17.913 に答える
7

Terry Coattaが述べたように、最初にレコードを保存したくない場合の最善のアプローチは、両方のソースをチェックすることです。

例えば:

public Person LookupPerson(string emailAddress, DateTime effectiveDate)
{
    Expression<Func<Person, bool>> criteria = 
        p =>
            p.EmailAddress == emailAddress &&
            p.EffectiveDate == effectiveDate;

    return LookupPerson(_context.ObjectSet<Person>.Local.AsQueryable(), criteria) ?? // Search local
           LookupPerson(_context.ObjectSet<Person>.AsQueryable(), criteria); // Search database
}

private Person LookupPerson(IQueryable<Person> source, Expression<Func<Person, bool>> predicate)
{
    return source.FirstOrDefault(predicate);
}
于 2016-09-22T23:06:16.273 に答える
0

後に来る人のために、私はいくつかの同様の問題に遭遇し、.Concatメソッドを試してみることにしました。私は大規模なパフォーマンステストを行ったことがないので、私よりも多くの知識を持っている人が気軽にチャイムを鳴らしてください。

基本的に、機能をより小さなチャンクに適切に分割するために、現在のUoWで同じメソッドへの連続または以前の呼び出しについて知らないメソッドがあるという状況に陥りました。だから私はこれをしました:

var context = new MyDbContextClass();
var emp = context.Employees.Concat(context.Employees.Local).FirstOrDefault(e => e.Name.Contains("some name"));
于 2014-05-15T18:29:14.653 に答える