web-dev-qa-db-ja.com

C#Entity-Framework:モデルオブジェクトで.Findと.Includeを組み合わせるにはどうすればよいですか?

Mvcmusicstoreの練習チュートリアルを行っています。アルバムマネージャーの足場を作成しているときに何かに気づきました(追加、削除、編集)。

エレガントにコードを書きたいので、これをきれいに書く方法を探しています。

参考までに、ストアをより一般的にしています:

アルバム=アイテム

ジャンル=カテゴリー

アーティスト=ブランド

インデックスの取得方法は次のとおりです(MVCによって生成されます)。

var items = db.Items.Include(i => i.Category).Include(i => i.Brand);

削除するアイテムを取得する方法は次のとおりです。

Item item = db.Items.Find(id);

1つ目は、すべてのアイテムを戻し、アイテムモデル内のカテゴリモデルとブランドモデルを設定します。 2つ目は、カテゴリとブランドを設定しません。

どうすれば2番目のものを書いて検索を実行し、内部に何を入れるか(できれば1行で)...理論的には次のようになります:

Item item = db.Items.Find(id).Include(i => i.Category).Include(i => i.Brand);
130
Ralph N

最初にInclude()を使用してから、結果のクエリから単一のオブジェクトを取得する必要があります。

Item item = db.Items
              .Include(i => i.Category)
              .Include(i => i.Brand)
              .SingleOrDefault(x => x.ItemId == id);
142
Dennis Traub

デニスの答えは、IncludeSingleOrDefaultを使用しています。後者は、データベースに往復します。

別の方法として、FindLoadと組み合わせて使用​​し、関連するエンティティの明示的な読み込みを行います...

以下 MSDNの例

using (var context = new BloggingContext()) 
{ 
  var post = context.Posts.Find(2); 

  // Load the blog related to a given post 
  context.Entry(post).Reference(p => p.Blog).Load(); 

  // Load the blog related to a given post using a string  
  context.Entry(post).Reference("Blog").Load(); 

  var blog = context.Blogs.Find(1); 

  // Load the posts related to a given blog 
  context.Entry(blog).Collection(p => p.Posts).Load(); 

  // Load the posts related to a given blog  
  // using a string to specify the relationship 
  context.Entry(blog).Collection("Posts").Load(); 
}

もちろん、Findは、そのエンティティが既にコンテキストによってロードされている場合、ストアへのリクエストを行わずにすぐに戻ります。

61
Learner

私には役に立たなかった。しかし、私はこのようにして解決しました。

var item = db.Items
             .Include(i => i.Category)
             .Include(i => i.Brand)
             .Where(x => x.ItemId == id)
             .First();

それが大丈夫かどうかわからない。しかし、デニスがくれたもう1つは.SingleOrDefault(x => x.ItemId = id);でブールエラーを与えました

0
Johan

IQueryableをDbSetにキャストする必要があります

var dbSet = (DbSet<Item>) db.Set<Item>().Include("");

return dbSet.Find(id);

0
Rafael R. Souza

検索でフィルタリングする簡単な方法はありません。しかし、私は機能を複製するための近い方法を思いつきましたが、私のソリューションのためにいくつかのことに注意してください。

このソリューションでは、.net-coreの主キーを知らなくても一般的にフィルタリングできます

  1. Findは、データベースを照会する前に追跡に存在するエンティティを取得するため、根本的に異なります。

  2. さらに、オブジェクトでフィルタリングできるため、ユーザーは主キーを知る必要がありません。

  3. このソリューションは、EntityFramework Core用です。

  4. これには、コンテキストへのアクセスが必要です

主キーでフィルタリングするのに役立つ追加の拡張メソッドを次に示します。

    public static IReadOnlyList<IProperty> GetPrimaryKeyProperties<T>(this DbContext dbContext)
    {
        return dbContext.Model.FindEntityType(typeof(T)).FindPrimaryKey().Properties;
    }

    //TODO Precompile expression so this doesn't happen everytime
    public static Expression<Func<T, bool>> FilterByPrimaryKeyPredicate<T>(this DbContext dbContext, object[] id)
    {
        var keyProperties = dbContext.GetPrimaryKeyProperties<T>();
        var parameter = Expression.Parameter(typeof(T), "e");
        var body = keyProperties
            // e => e.PK[i] == id[i]
            .Select((p, i) => Expression.Equal(
                Expression.Property(parameter, p.Name),
                Expression.Convert(
                    Expression.PropertyOrField(Expression.Constant(new { id = id[i] }), "id"),
                    p.ClrType)))
            .Aggregate(Expression.AndAlso);
        return Expression.Lambda<Func<T, bool>>(body, parameter);
    }

    public static Expression<Func<T, object[]>> GetPrimaryKeyExpression<T>(this DbContext context)
    {
        var keyProperties = context.GetPrimaryKeyProperties<T>();
        var parameter = Expression.Parameter(typeof(T), "e");
        var keyPropertyAccessExpression = keyProperties.Select((p, i) => Expression.Convert(Expression.Property(parameter, p.Name), typeof(object))).ToArray();
        var selectPrimaryKeyExpressionBody = Expression.NewArrayInit(typeof(object), keyPropertyAccessExpression);

        return Expression.Lambda<Func<T, object[]>>(selectPrimaryKeyExpressionBody, parameter);
    }

    public static IQueryable<TEntity> FilterByPrimaryKey<TEntity>(this DbSet<TEntity> dbSet, DbContext context, object[] id)
        where TEntity : class
    {
        return FilterByPrimaryKey(dbSet.AsQueryable(), context, id);
    }

    public static IQueryable<TEntity> FilterByPrimaryKey<TEntity>(this IQueryable<TEntity> queryable, DbContext context, object[] id)
        where TEntity : class
    {
        return queryable.Where(context.FilterByPrimaryKeyPredicate<TEntity>(id));
    }

これらの拡張メソッドを取得したら、次のようにフィルタリングできます。

query.FilterByPrimaryKey(this._context, id);
0
johnny 5