Подтвердить что ты не робот

DbSet не имеет метода поиска в EF7

Я пытаюсь создать общий репозиторий для доступа к моей базе данных. В EF6 я смог сделать это, чтобы получить конкретный объект:

protected IDbSet<T> dbset;

public T Get(object id)
{
    return this.dbset.Find(id);
}

DbSet в EF7 отсутствует метод Find. Есть ли способ реализовать вышеуказанный фрагмент кода?

4b9b3361

Ответ 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();

Ответ 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, если ключ не был найден. Я бы не хотел, чтобы он выдавал исключение. В большинстве случаев я хотел бы знать, существовал ли ключ в наборе, а обработка исключения - очень медленный способ понять это.