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)
正如评论中提到的那样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")
将尽早失败。
如果要创建一个通用查找方法来查找与主键列表匹配的所有行,则可以通过从基类继承这些实体类型来实现此目的,在基类中它们共享主键列的相同名称。
这样想:如果您的实体(数据库表)具有组合键,该方法将如何表现?因此,如果您能够符合这种类型的设计,以下实现展示了使用 .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)
归档时间: |
|
查看次数: |
4096 次 |
最近记录: |