EF Core Find方法等效于多个记录?

moh*_*eli 7 c# entity-framework entity-framework-core

EF Core DbSet有一个方法叫做Find:

查找具有给定主键值的实体.如果上下文正在跟踪具有给定主键值的实体,则会立即返回该实体而不向数据库发出请求.否则,将对具有给定主键值的实体的数据库进行查询,并且如果找到该实体,则将该实体附加到上下文并返回.如果未找到任何实体,则返回null.

我需要根据给定的主键值数组返回多个项目,当然所有这些都在一个请求中.有没有办法在EF Core中做到这一点?

更新:我知道我可以Where在正常情况下使用子句.但我正在创建一个通用的辅助工具,在其中我无法访问我的模型的强类型属性.因此我不能使用Where(x => ids.Contains(x.Id))条款.

更新2:理想的方法可以有一个简单的签名,获取long值列表,并返回一个列表T.public static List<T> FindSet(List<long> ids)可以像这样使用:

var foundRecords = dbset.FindSet(new List<long> { 5, 17, 93, 178, 15400 });
Run Code Online (Sandbox Code Playgroud)

pok*_*oke 5

正如评论中提到的那样Find,以幼稚的方式使用(例如,遍历所有键值)将最终针对每个单个值运行查询,因此这不是您想要的。正确的解决方案是使用一次Where获取所有项目的查询。这里的问题只是您需要动态地请求主键。

当然,数据库上下文本身确实知道给定实体类型的主键是什么。Find内部工作的方式是它使用该信息来构建动态查询,在该查询中检查主键是否相等。因此,要拥有一些FindAll,我们将必须做同样的事情。

以下是对此的快速解决方案。基本上,这会dbSet.Where(e => keyValues.Contains(e.<PrimaryKey>))为您建立一个查询。

请注意,按照我的构建方式,它仅适用于每种实体类型的单个主键。如果尝试将其与复合键一起使用,它将抛出一个NotSupportedException。您绝对可以扩展它,以增加对复合键的支持;我之所以没有这样做,是因为它使一切变得更加复杂(特别是因为您无法使用它Contains)。

public static class DbContextFindAllExtensions
{
    private static readonly MethodInfo ContainsMethod = typeof(Enumerable).GetMethods()
        .FirstOrDefault(m => m.Name == "Contains" && m.GetParameters().Length == 2)
        .MakeGenericMethod(typeof(object));

    public static Task<T[]> FindAllAsync<T>(this DbContext dbContext, params object[] keyValues)
        where T : class
    {
        var entityType = dbContext.Model.FindEntityType(typeof(T));
        var primaryKey = entityType.FindPrimaryKey();
        if (primaryKey.Properties.Count != 1)
            throw new NotSupportedException("Only a single primary key is supported");

        var pkProperty = primaryKey.Properties[0];
        var pkPropertyType = pkProperty.ClrType;

        // validate passed key values
        foreach (var keyValue in keyValues)
        {
            if (!pkPropertyType.IsAssignableFrom(keyValue.GetType()))
                throw new ArgumentException($"Key value '{keyValue}' is not of the right type");
        }

        // retrieve member info for primary key
        var pkMemberInfo = typeof(T).GetProperty(pkProperty.Name);
        if (pkMemberInfo == null)
            throw new ArgumentException("Type does not contain the primary key as an accessible property");

        // build lambda expression
        var parameter = Expression.Parameter(typeof(T), "e");
        var body = Expression.Call(null, ContainsMethod,
            Expression.Constant(keyValues),
            Expression.Convert(Expression.MakeMemberAccess(parameter, pkMemberInfo), typeof(object)));
        var predicateExpression = Expression.Lambda<Func<T, bool>>(body, parameter);

        // run query
        return dbContext.Set<T>().Where(predicateExpression).ToArrayAsync();
    }
}
Run Code Online (Sandbox Code Playgroud)

用法是这样的:

// pass in params
var result = await dbContext.FindAllAsync<MyEntity>(1, 2, 3, 4);

// or an object array
var result = await dbContext.FindAllAsync<MyEntity>(new object[] { 1, 2, 3, 4 });
Run Code Online (Sandbox Code Playgroud)

我还添加了一些基本的验证,所以类似的事情context.FindAllAsync<MyEntity>(1, 2, "foo")将尽早失败。


Sae*_*eid 2

如果要创建一个通用查找方法来查找与主键列表匹配的所有行,则可以通过从基类继承这些实体类型来实现此目的,在基类中它们共享主键列的相同名称。

这样想:如果您的实体(数据库表)具有组合键,该方法将如何表现?因此,如果您能够符合这种类型的设计,以下实现展示了使用 .NET Core 实现此目的的简单逻辑。(实际上,您也可以使用 EF6 实现相同的行为)

public class MyBaseEntity
{
    public int Id { get; set; }
}

public class MyTable : MyBaseEntity
{
    public string MyProperty { get; set; }
}

public static class RepositoryExtensions
{
    public static IQueryable<T> FindMatches<T>(this DbContext db, IEnumerable<int> keys)
        where T : MyBaseEntity
        => db.Set<T>().Where(x => keys.Contains(x.Id));

}

class Program
{
    static void Main(string[] args)
    {
        // Initialize your own DbContext.
        var db = new DbContext(null);
        // Usage:
        var lookupKeys = new[] { 1, 2, 3 };
        var results = db.FindMatches<MyTable>(lookupKeys).ToList();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 在你放弃之前还有一个建议!查看带有 EF Core 的动态 Linq。也许它可以帮助您构建一些相对接近您正在寻找的东西。https://github.com/StefH/System.Linq.Dynamic.Core/ (2认同)