[C#] C # 엔터티 프레임 워크 : .Find와 .Include를 모델 개체에 결합하려면 어떻게해야합니까?

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의 기본 키를 몰라도 일반적으로 필터링 할 수 있습니다

  1. 찾기는 엔터티가 데이터베이스를 쿼리하기 전에 추적에있는 경우 엔터티를 가져 오기 때문에 근본적으로 다릅니다.

  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);


답변