DbSet не имеет метода поиска в EF7
Я пытаюсь создать общий репозиторий для доступа к моей базе данных. В EF6 я смог сделать это, чтобы получить конкретный объект:
protected IDbSet<T> dbset;
public T Get(object id)
{
return this.dbset.Find(id);
}
DbSet в EF7 отсутствует метод Find. Есть ли способ реализовать вышеуказанный фрагмент кода?
Ответы
Ответ 1
Здесь очень грубая, неполная и непроверенная реализация .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();
}
Ответ 2
Если вы используете 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();
}
}
}
Ответ 3
Я взял некоторые из ранее предоставленных ответов и подстроил их, чтобы исправить пару проблем:
- Неявно зафиксированное закрытие
-
Ключ не должен быть жестко закодирован для "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();
}
Ответ 4
Нельзя комментировать из-за репутации, но если вы используете RC2 (или позже?), вы должны использовать
var context = set.GetService<ICurrentDbContext>().Context;
вместо
var context = set.GetService<DbContext>();
Ответ 5
Недостаточно репутации для комментариев, но есть ошибка в ответ @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, так что когда вызывается entry.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();
Ответ 6
Найти наконец-то прибывает в ядро Entity Framework.
Ответ 7
Итак... вышеприведенные методы поиска отлично поработали, но если в вашей модели нет столбца с именем "Id", все это провалится в следующей строке. Я не уверен, почему OP поставил бы твердое значение в это место
Expression.Property(parameter, "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();
}
Этот STILL очень хорошо может выйти из строя, если у вас есть более одной установки ключа на объекте сущности, и ключ, который вы просматриваете, не является первым, но это должно быть довольно немного btter таким образом.
Ответ 8
Я использую linq
; вместо метода Find
вы можете использовать:
var record = dbSet.SingleOrDefault(m => m.Id == id)
Ответ 9
Позвольте мне внести изменения, которые включают в себя создание выражения.
Признаюсь, я на самом деле не проверял это; -)
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();
}
Ответ 10
вот что я использую.
Не метод поиска, но работает как шарм
var professionalf = from m in _context.Professionals select m;
professionalf = professionalf.Where(s => s.ProfessionalId == id);
Professional professional = professionalf.First();
Ответ 11
Было предложено изменить ".First()" на ".FirstOrDefault()" в самой последней строке моего предыдущего сообщения. Редактирование было отклонено, но я согласен с этим. Я бы ожидал, что функция вернет значение null, если ключ не был найден. Я бы не хотел, чтобы он выдавал исключение. В большинстве случаев я хотел бы знать, существовал ли ключ в наборе, а обработка исключения - очень медленный способ понять это.