mvcmusicstore 연습 자습서를하고 있습니다. 앨범 관리자 용 스캐 폴드를 만들 때 무언가를 발견했습니다 (삭제 편집 추가).
코드를 우아하게 작성하고 싶기 때문에 이것을 작성하는 깔끔한 방법을 찾고 있습니다.
참고로 나는 상점을보다 일반적인 것으로 만들고 있습니다.
앨범 = 항목
장르 = 카테고리
아티스트 = 브랜드
MVC에서 생성 된 인덱스를 검색하는 방법은 다음과 같습니다.
var items = db.Items.Include(i => i.Category).Include(i => i.Brand);
삭제 항목을 검색하는 방법은 다음과 같습니다.
Item item = db.Items.Find(id);
첫 번째 항목은 모든 항목을 다시 가져 와서 항목 모델 내부의 카테고리 및 브랜드 모델을 채 웁니다. 두 번째는 카테고리와 브랜드를 채우지 않습니다.
두 번째 것을 작성하여 내부에서 (바람직하게는 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);
답변
Dennis의 답변은 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
해당 엔티티가 컨텍스트에 의해 이미로드 된 경우 상점에 요청하지 않고 즉시 리턴합니다.
답변
IQueryable을 DbSet으로 캐스트해야합니다.
var dbSet = (DbSet<Item>) db.Set<Item>().Include("");
return dbSet.Find(id);
답변
나를 위해 일하지 않았다. 그러나 나는 이런 식으로 해결했습니다.
var item = db.Items
.Include(i => i.Category)
.Include(i => i.Brand)
.Where(x => x.ItemId == id)
.First();
그게 괜찮은 해결책인지 모르겠다. 하지만 데니스가 준 다른 하나는 .SingleOrDefault(x => x.ItemId = id);
답변
찾기로 필터링하는 쉬운 방법은 없습니다. 그러나 기능을 복제 할 수있는 가까운 방법을 찾았지만 솔루션에 대한 몇 가지 사항에 유의하십시오.
이 솔루션을 사용하면 .net-core의 기본 키를 몰라도 일반적으로 필터링 할 수 있습니다
-
찾기는 엔터티가 데이터베이스를 쿼리하기 전에 추적에있는 경우 엔터티를 가져 오기 때문에 근본적으로 다릅니다.
-
또한 사용자가 기본 키를 몰라도 개체별로 필터링 할 수 있습니다.
-
이 솔루션은 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);