DbSet在EF7中没有Find方法

时间:2015-03-13 10:57:16

标签: c# entity-framework-core

我正在尝试创建一个通用存储库来访问我的数据库。在EF6中,我能够做到这一点,以获得一个特定的实体:

protected IDbSet<T> dbset;

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

EF7中的DbSet缺少Find方法。有没有办法实现上面的代码?

11 个答案:

答案 0 :(得分:23)

这是.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();
}

答案 1 :(得分:19)

如果您使用的是 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();
        }
    }
}

答案 2 :(得分:9)

由于声誉无法发表评论,但如果您使用RC2(或更高版本?),则应使用

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

而不是

var context = set.GetService<DbContext>();

答案 3 :(得分:8)

我已经采取了一些先前提供的答案并进行了调整以解决一些问题:

  • 隐式捕捉封闭
  • 密钥不应硬编码为“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 :(得分:6)

在实体框架核心中找到finally arrives

答案 5 :(得分:5)

所以...上面的查找方法效果很好,但如果你的模型中没有名为“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设置并且你正在查找的键不是第一个,那么这个STILL很可能会失败,但这应该是相当有点的。

答案 6 :(得分:5)

评论的声誉不够,但@ Roger-Santana在控制台app / seperate程序集中使用时会出现错误:

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();

&#39; i&#39;的价值在foreach中捕获,以便在调用entries.FirstOrDefault()时,keyValues [i]具有(至少)keyValues [i ++]的值,在我的情况下,它会因索引错误而崩溃。 修复方法是复制&#39; i&#39;的值。通过循环:

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 :(得分:4)

我使用linq;而不是Find方法,您可以使用:

var record = dbSet.SingleOrDefault(m => m.Id == id)

答案 8 :(得分:1)

让我提供一个包含构建表达式的修订版。 我承认我实际上没有测试过这个; - )

    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();
    }

答案 9 :(得分:0)

这是我使用的。 不是一种寻找方法,而是像魅力一样工作

var professionalf = from m in _context.Professionals select m;
professionalf = professionalf.Where(s => s.ProfessionalId == id);
Professional professional = professionalf.First();

答案 10 :(得分:0)

建议编辑改变&#34; .First()&#34; to&#34; .FirstOrDefault()&#34;在我之前的帖子的最后一行。编辑被否决了,但我同意。如果找不到密钥,我希望函数返回null。我不希望它抛出异常。在大多数情况下,我想知道密钥是否存在于集合中,处理异常是一种非常缓慢的方法。

相关问题