我正在尝试创建一个通用存储库来访问我的数据库。在 EF6 中,我能够这样做以获得特定实体:
protected IDbSet<T> dbset;
public T Get(object id)
{
return this.dbset.Find(id);
}
EF7 中的 DbSet 缺少 Find 方法。有没有办法实现上面的代码?
我正在尝试创建一个通用存储库来访问我的数据库。在 EF6 中,我能够这样做以获得特定实体:
protected IDbSet<T> dbset;
public T Get(object id)
{
return this.dbset.Find(id);
}
EF7 中的 DbSet 缺少 Find 方法。有没有办法实现上面的代码?
.Find()
这是作为扩展方法的一个非常粗略、不完整且未经测试的实现。如果没有别的,它应该让你指向正确的方向。
真正的实现由#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,您可以在下面找到@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();
}
}
}
由于声誉而无法发表评论,但如果您使用 RC2(或更高版本?),您应该使用
var context = set.GetService<ICurrentDbContext>().Context;
代替
var context = set.GetService<DbContext>();
我采用了一些先前提供的答案并对其进行了调整以解决几个问题:
密钥不应硬编码为“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();
}
Find最终到达Entity Framework 核心。
所以...上面的 find 方法效果很好,但是如果您的模型中没有名为“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 并且您正在查找的键不是第一个,那么这仍然很可能会失败,但这种方式应该会更好一些。
没有足够的声誉来发表评论,但是在控制台应用程序/单独的程序集中使用@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] 的值(至少)为 keyValues[i++],在我的情况下,它因超出索引错误而崩溃. 解决方法是通过循环复制“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();
我用linq
; 而不是Find
您可以使用的方法:
var record = dbSet.SingleOrDefault(m => m.Id == id)
让我贡献一个包含构建表达式的修订。我承认我实际上并没有测试这个;-)
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();
}
在我之前的帖子的最后一行中,有人建议将“.First()”更改为“.FirstOrDefault()”。编辑被否决,但我同意。如果找不到密钥,我希望该函数返回 null 。我不希望它抛出异常。在大多数情况下,我想知道密钥是否存在于集合中,而处理异常是一种非常缓慢的解决方法。
这是我使用的。不是一种查找方法,但就像一个魅力
var professionalf = from m in _context.Professionals select m;
professionalf = professionalf.Where(s => s.ProfessionalId == id);
Professional professional = professionalf.First();