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);
最初にInclude()
を使用してから、結果のクエリから単一のオブジェクトを取得する必要があります。
Item item = db.Items
.Include(i => i.Category)
.Include(i => i.Brand)
.SingleOrDefault(x => x.ItemId == id);
デニスの答えは、Include
とSingleOrDefault
を使用しています。後者は、データベースに往復します。
別の方法として、Find
をLoad
と組み合わせて使用し、関連するエンティティの明示的な読み込みを行います...
以下 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
は、そのエンティティが既にコンテキストによってロードされている場合、ストアへのリクエストを行わずにすぐに戻ります。
私には役に立たなかった。しかし、私はこのようにして解決しました。
var item = db.Items
.Include(i => i.Category)
.Include(i => i.Brand)
.Where(x => x.ItemId == id)
.First();
それが大丈夫かどうかわからない。しかし、デニスがくれたもう1つは.SingleOrDefault(x => x.ItemId = id);
でブールエラーを与えました
IQueryableをDbSetにキャストする必要があります
var dbSet = (DbSet<Item>) db.Set<Item>().Include("");
return dbSet.Find(id);
検索でフィルタリングする簡単な方法はありません。しかし、私は機能を複製するための近い方法を思いつきましたが、私のソリューションのためにいくつかのことに注意してください。
このソリューションでは、.net-coreの主キーを知らなくても一般的にフィルタリングできます
Findは、データベースを照会する前に追跡に存在するエンティティを取得するため、根本的に異なります。
さらに、オブジェクトでフィルタリングできるため、ユーザーは主キーを知る必要がありません。
このソリューションは、EntityFramework Core用です。
主キーでフィルタリングするのに役立つ追加の拡張メソッドを次に示します。
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);