DbSet在EF7中没有Find方法
我正在尝试创build一个通用的存储库来访问我的数据库。 在EF6中,我能够做到这一点,以获得一个特定的实体:
protected IDbSet<T> dbset; public T Get(object id) { return this.dbset.Find(id); }
EF7中的DbSet缺lessFind方法。 有没有办法实现上面的代码?
下面是.Find()
作为扩展方法的一个非常粗糙,不完整和未经testing的实现。 如果没有别的,它应该让你指出正确的方向。
真正的实现被#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(); }
如果您使用的是EF 7.0.0-rc1-final ,则可以在下面的代码中find@bricelam在上一个答案中提供的代码的小小更新。 顺便说一句,非常感谢@bricelam – 你的代码对我来说非常有用。
这里是我的“project.config”下的依赖关系 :
"dependencies": { "EntityFramework.Commands": "7.0.0-rc1-final", "EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final", "Microsoft.Framework.Configuration.Json": "1.0.0-beta8", "Microsoft.Framework.ConfigurationModel": "1.0.0-beta4", "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-beta4", "Microsoft.Framework.DependencyInjection": "1.0.0-beta8" }
下面是DbSet.Find(TEntity)的扩展方法:
using Microsoft.Data.Entity; using Microsoft.Data.Entity.Infrastructure; using System; using System.Linq; using System.Linq.Expressions; namespace Microsoft.Data.Entity.Extensions { public static class Extensions { public static TEntity Find<TEntity>(this DbSet<TEntity> set, params object[] keyValues) where TEntity : class { var context = ((IInfrastructure<IServiceProvider>)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) { entries = entries.Where(e => e.Property(property.Name).CurrentValue == keyValues[i]); 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(); } } }
我已经采取了一些以前提供的答案,并调整他们来解决一些问题:
- 隐式捕获闭包
-
密钥不应该被硬编码为“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(); }
由于声誉不能评论,但如果你使用RC2(或更高版本?),你应该使用
var context = set.GetService<ICurrentDbContext>().Context;
代替
var context = set.GetService<DbContext>();
没有足够的声望发表评论,但是在@ Roger-Santana中,在控制台应用程序/独立程序集中使用它时出现了一个错误:
var i = 0; foreach (var property in key.Properties) { entries = entries.Where(e => e.Property(property.Name).CurrentValue == keyValues[i]); i++; } var entry = entries.FirstOrDefault();
“i”的值在foreach中被捕获,所以当调用entries.FirstOrDefault()时,keyValues [i]的值为(至less)keyValues [i ++],在我的情况下,它的索引错误。 解决办法是通过循环复制'我'的值:
var i = 0; foreach (var property in key.Properties) { var idx =i; entries = entries.Where(e => e.Property(property.Name).CurrentValue == keyValues[idx]); i++; } var entry = entries.FirstOrDefault();
查找终于到达entity framework核心。
所以…上面的查找方法工作的很好,但是如果你的模型中没有一个名为“Id”的列,整个事情就会在下面一行失败。 我不确定为什么OP会把硬编码的值放到这个位置
Expression.Property(parameter, "Id"),
这里有一个修订版,它将为那些适当地命名我们Id列的人修复它。 🙂
var keyCompare = key.Properties[0].Name; // 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, keyCompare), //Expression.Property(parameter, "Id"), Expression.Constant(keyValues[0])), parameter)); // Look in the database return query.FirstOrDefault(); }
如果您在实体对象上有多个Key设置,并且您正在查找的关键字不是第一个,那么这仍然可能会失败,但是这应该是相当复杂的。
我用linq
; 而不是Find
方法,您可以使用:
var record = dbSet.SingleOrDefault(m => m.Id == id)
让我提供一个包括build立expression的修改。 我会承认我没有真的testing这个;-)
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(); }
这是我使用的。 不是一个find的方法,但像一个魅力
var professionalf = from m in _context.Professionals select m; professionalf = professionalf.Where(s => s.ProfessionalId == id); Professional professional = professionalf.First();
提出了一个编辑,将我的早期文章的最后一行中的“.First()”更改为“.OrderOrDefault()”。 编辑被拒绝了,但我同意它。 我期望函数返回null,如果没有find密钥。 我不希望它抛出exception。 在大多数情况下,我想知道密钥是否存在于集合中,处理exception是解决这个问题的一个非常缓慢的方法。