DbSet doesn't have a Find method in EF7

后端 未结 11 1720
醉话见心
醉话见心 2020-12-03 04:28

I am trying to create a generic repository to access my database. In EF6 I was able to do that in order to get a specific entity:

protected IDbSet d         


        
相关标签:
11条回答
  • 2020-12-03 04:43

    Let me contribute a revision that includes building the expression. I'll confess I didn't actually test this ;-)

        public static TEntity Find<TEntity>(this DbSet<TEntity> dbSet, params object[] keyValues) where TEntity : class
        {
            // Find DbContext, entity type, and primary key.
            var context = ((IInfrastructure<IServiceProvider>)dbSet).GetService<DbContext>();
            var entityType = context.Model.FindEntityType(typeof(TEntity));
            var key = entityType.FindPrimaryKey();
    
            // Build the lambda expression for the query: (TEntity entity) => AND( entity.keyProperty[i] == keyValues[i])
            var entityParameter = Expression.Parameter(typeof(TEntity), "entity");
            Expression whereClause = Expression.Constant(true, typeof(bool));
            uint i = 0;
    
            foreach (var keyProperty in key.Properties) {
                var keyMatch = Expression.Equal(
                    Expression.Property(entityParameter, keyProperty.Name),
                    Expression.Constant(keyValues[i++])
                );
    
                whereClause = Expression.And(whereClause, keyMatch);
            }
    
            var lambdaExpression = (Expression<Func<TEntity,bool>>)Expression.Lambda(whereClause, entityParameter);
    
            // Execute against the in-memory entities, which we get from ChangeTracker (but not filtering the state of the entities).
            var entries = context.ChangeTracker.Entries<TEntity>().Select((EntityEntry e) => (TEntity)e.Entity);
            TEntity entity = entries.AsQueryable().Where(lambdaExpression).First(); // First is what triggers the query execution.
    
            // If found in memory then we're done.
            if (entity != null) { return entity; }
    
            // Otherwise execute the query against the database.
            return dbSet.Where(lambdaExpression).First();
        }
    
    0 讨论(0)
  • 2020-12-03 04:45

    I've taken some of the previously provided answers and tweaked them to fix a couple of problems:

    • Implicitly captured closure
    • Key shouldn't be hard coded to "Id"

      public static TEntity Find<TEntity>(this DbSet<TEntity> set, params object[] keyValues) where TEntity : class
      {
          var context = set.GetService<DbContext>();
      
          var entityType = context.Model.FindEntityType(typeof(TEntity));
          var key = entityType.FindPrimaryKey();
      
          var entries = context.ChangeTracker.Entries<TEntity>();
      
          var i = 0;
          foreach (var property in key.Properties)
          {
              var i1 = i;
              entries = entries.Where(e => e.Property(property.Name).CurrentValue == keyValues[i1]);
              i++;
          }
      
          var entry = entries.FirstOrDefault();
          if (entry != null)
          {
              // Return the local object if it exists.
              return entry.Entity;
          }
      
          var parameter = Expression.Parameter(typeof(TEntity), "x");
          var query = set.AsQueryable();
          i = 0;
          foreach (var property in key.Properties)
          {
              var i1 = i;
              query = query.Where((Expression<Func<TEntity, bool>>)
               Expression.Lambda(
                   Expression.Equal(
                       Expression.Property(parameter, property.Name),
                       Expression.Constant(keyValues[i1])),
                   parameter));
              i++;
          }
      
          // Look in the database
          return query.FirstOrDefault();
      }
      
    0 讨论(0)
  • 2020-12-03 04:46

    Can't comment because of reputation, but if you use RC2 (or later?) you should use

    var context = set.GetService<ICurrentDbContext>().Context;
    

    instead of

    var context = set.GetService<DbContext>();
    
    0 讨论(0)
  • 2020-12-03 04:47

    An edit was proposed to change ".First()" to ".FirstOrDefault()" in the very last line of my earlier post. The edit was voted down, but I agree with it. I would expect the function to return null if the key was not found. I would not want it to throw an exception. In most cases I would want to know if the key existed in the set, and handling an exception is a very slow way of figuring that out.

    0 讨论(0)
  • 2020-12-03 04:48

    here is what I use. Not a find method, but works like a charm

    var professionalf = from m in _context.Professionals select m;
    professionalf = professionalf.Where(s => s.ProfessionalId == id);
    Professional professional = professionalf.First();
    
    0 讨论(0)
  • 2020-12-03 04:53

    Here's a very crude, incomplete, and untested implementation of .Find() as an extension method. If nothing else, it should get you pointed in the right direction.

    The real implementation is tracked by #797.

    static TEntity Find<TEntity>(this DbSet<TEntity> set, params object[] keyValues)
        where TEntity : class
    {
        var context = ((IAccessor<IServiceProvider>)set).Service.GetService<DbContext>();
    
        var entityType = context.Model.GetEntityType(typeof(TEntity));
        var key = entityType.GetPrimaryKey();
    
        var entries = context.ChangeTracker.Entries<TEntity>();
    
        var i = 0;
        foreach (var property in key.Properties)
        {
            var keyValue = keyValues[i];
            entries = entries.Where(e => e.Property(property.Name).CurrentValue == keyValue);
            i++;
        }
    
        var entry = entries.FirstOrDefault();
        if (entry != null)
        {
            // Return the local object if it exists.
            return entry.Entity;
        }
    
        // TODO: Build the real LINQ Expression
        // set.Where(x => x.Id == keyValues[0]);
        var parameter = Expression.Parameter(typeof(TEntity), "x");
        var query = set.Where((Expression<Func<TEntity, bool>>)
            Expression.Lambda(
                Expression.Equal(
                    Expression.Property(parameter, "Id"),
                    Expression.Constant(keyValues[0])),
                parameter));
    
        // Look in the database
        return query.FirstOrDefault();
    }
    
    0 讨论(0)
提交回复
热议问题