C# Entity-Framework: How can I combine a .Find and .Include on a Model Object?

前端 未结 5 834
感情败类
感情败类 2020-11-27 11:36

I\'m doing the mvcmusicstore practice tutorial. I noticed something when creating the scaffold for the album manager (add delete edit).

I want to write code elegantl

相关标签:
5条回答
  • 2020-11-27 12:17

    Didnt work for me. But I solved it by doing like this.

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

    Dont know if thats a ok solution. But the other one Dennis gave gave me a bool error in .SingleOrDefault(x => x.ItemId = id);

    0 讨论(0)
  • 2020-11-27 12:24

    You have to cast IQueryable to DbSet

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

    return dbSet.Find(id);

    0 讨论(0)
  • 2020-11-27 12:26

    Dennis' answer is using Include and SingleOrDefault. The latter goes round-tripping to database.

    An alternative, is to use Find, in combination with Load, for explicit loading of related entities...

    Below an MSDN example:

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

    Of course, Find returns immediately without making a request to the store, if that entity is already loaded by the context.

    0 讨论(0)
  • 2020-11-27 12:33

    You can use Include() first, then retrieve a single object from the resulting query:

    Item item = db.Items
                  .Include(i => i.Category)
                  .Include(i => i.Brand)
                  .FirstOrDefault(x => x.ItemId == id);
    
    0 讨论(0)
  • 2020-11-27 12:35

    There's no real easy way to filter with a find. But I've come up with a close way to replicate the functionality but please take note of a few things for my solution.

    This Solutions allows you to filter generically without knowning the primary key in .net-core

    1. Find is fundamentally different because it obtains the the entity if it's present in the tracking before Querying the database.

    2. Additionally It can filter by an Object so the user does not have to know the primary key.

    3. This solution is for EntityFramework Core.

    4. This requires access to the context

    Here are some extension methods to add which will help you filter by primary key so

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

    Once you have these extension methods you can filter like so:

    query.FilterByPrimaryKey(this._context, id);
    
    0 讨论(0)
提交回复
热议问题