Как упомянуто в комментариях, использование 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();
}
}
Использование выглядит так:
// 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 });
Я также добавил некоторую базовую проверку, чтобы такие вещи, как context.FindAllAsync<MyEntity>(1, 2, "foo")
, не сработали раньше.