DbSet在EF7中没有Find方法

Lyu*_*rov 47 c# entity-framework-core

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

protected IDbSet<T> dbset;

public T Get(object id)
{
    return this.dbset.Find(id);
}
Run Code Online (Sandbox Code Playgroud)

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

bri*_*lam 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();
}
Run Code Online (Sandbox Code Playgroud)

  • @LynnCrumbling我无法在`Microsoft.Data.Entity.Infrastructure`或其他任何地方找到`IAccessor`泛型.我错过了一些参考吗? (6认同)

小智 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"
}
Run Code Online (Sandbox Code Playgroud)

以下是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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 9

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

var context = set.GetService<ICurrentDbContext>().Context;
Run Code Online (Sandbox Code Playgroud)

代替

var context = set.GetService<DbContext>();
Run Code Online (Sandbox Code Playgroud)


Sha*_*ica 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();
    }
    
    Run Code Online (Sandbox Code Playgroud)


Seb*_*sek 7

查找最终到达 Entity Framework核心.


小智 5

所以...上面的查找方法效果很好,但是如果你的模型中没有名为"Id"的列,那么整个过程将在下一行失败.我不确定为什么OP会把硬编码的值放到这个位置

  Expression.Property(parameter, "Id"),
Run Code Online (Sandbox Code Playgroud)

这是一个修订版,它将为那些正确命名我们的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();
    }
Run Code Online (Sandbox Code Playgroud)

如果你在实体对象上有多个Key设置并且你正在查找的键不是第一个,那么这个STILL很可能会失败,但是这样做应该有点笨拙.


Pau*_*nce 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();
Run Code Online (Sandbox Code Playgroud)

在foreach中捕获'i'的值,这样当调用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();
Run Code Online (Sandbox Code Playgroud)