2016-01-20 2 views
1

я был первоначально с помощью цикла Еогеасп, а затем для каждого элемента в петле, я выполнить запрос LINQ следующим образом:Правильный способ использования LINQ для этого типа запросов?

foreach (MyObject identifier in identifiers.Where(i => i.IsMarkedForDeletion == false)) 
{ 
    if (this.MyEntities.Identifiers.Where(pi => identifier.Field1 == pi.Field1 && identifier.Field2 == pi.Field2 && identifier.Field3 == pi.Field3).Any()) 
    { 
     return false; 
    } 
} 

return true; 

Затем я изменил его следующим образом:

if (identifiers.Any(i => !i.IsMarkedForDeletion && this.MyEntities.Identifiers.Where(pi => i.Field1 == pi.Field1 && i.Field2 == pi.Field2 && i.Field3 == pi.Field3).Any())) 
{ 
    return false; 
} 

return true; 

Мой вопрос это по-прежнему неправильный способ использования LINQ? В принципе, я хочу исключить необходимость цикла foreach (который, похоже, я должен быть в состоянии избавиться от него), а также сделать запрос БД быстрее, не выполняя отдельные запросы БД для каждого элемента списка. Вместо этого я хочу выполнить один запрос для всех элементов. Благодаря!

+0

Что такое ** тип ** идентификаторов, 'MyEntities' и' MyEntities.Identifiers'? Являются ли они в коллекциях памяти или объектах базы данных? –

+0

В базе данных находится только MyEntities.Identifiers. Все остальное в памяти. – Andrew

+0

Другими словами, 'идентификаторы' находятся в коллекции памяти,' MyEntities.Identifiers' является 'DbSet '. –

ответ

0

К сожалению, ваша модифицированная версия будет выполнена точно так же, как (например, несколько запросов к базе данных), как и в оригинальных foreach подход, поскольку EF не поддерживает запрос к базе данных с соединениями в сборе памяти (для примитивных и перечисления коллекций типа, за исключением), так что если вы попробуете самый логичный способ

bool result = this.MyEntities.Identifiers.Any(pi => identifiers.Any(i => 
    !i.IsMarkedForDeletion && 
    i.Field1 == pi.Field1 && i.Field2 == pi.Field2 && i.Field3 == pi.Field3)); 

вы получите

NotSupportedException: Невозможно создать постоянное значение типа «YourType». В этом контексте поддерживаются только примитивные типы или типы перечислений.

Единственный способ позволить EF выполнить один запрос к базе данных, чтобы вручную создать запрос LINQ с Concat по каждому пункту из в коллекции памяти, как этот

IQueryable<Identifier> query = null; 
foreach (var item in identifiers.Where(i => !i.IsMarkedForDeletion)) 
{ 
    var i = item; 
    var subquery = this.MyEntities.Identifiers.Where(pi => 
     pi.Field1 == i.Field1 && pi.Field2 == i.Field2 && pi.Field3 == i.Field3); 
    query = query != null ? query.Concat(subquery) : subquery; 
} 
bool result = query != null && query.Any(); 

См Logging and Intercepting Database Operations как к монитор EF действия.

1

Вы можете изменить свой код таким образом, и он будет преобразован в инструкцию SQL, как ожидалось. Чтобы предотвратить ошибки во время выполнения во время преобразования, лучше сохранить DBSet в переменной IQueryable; identifiers должен быть IQueryable, так что вы должны изменить свой код во что-то вроде этого (честно говоря, Resharper конвертируется ваш foreach в этом коротком labda):

IQueryable<MyObject2> identifiers = MyEntities.Identifiers.Where(i => i.IsMarkedForDeletion == false); 
IQueryable<MyObject2> ids = MyEntities.Identifiers.AsQueryable(); 
return identifiers.All(identifier => !ids.Any(pi => identifier.Field1 == pi.Field1 && identifier.Field2 == pi.Field2 && identifier.Field3 == pi.Field3)); 

Если identifiers в сборе памяти вы можете изменить код в этом способ (надеюсь, что поля string):

IQueryable<MyObject2> ids = MyEntities.Identifiers.AsQueryable(); 
string[] values = identifiers.Where(i => i.IsMarkedForDeletion == false).Select(i => String.Concat(i.Field1, i.Field2, i.Field3)).ToArray(); 
return !ids.Any(i => values.Contains(i.Field1 + i.Field2 + i.Field3)); 
+0

Нет. Он ** не будет ** преобразован в SQL, что эквивалентно исходному подходу 'foreach' –

+0

Не могли бы вы предоставить информацию, почему вы так думаете? Потому что у меня был практический опыт, и он отлично работает – Michael

+0

Потому что просто проверил - EF6. –

0

Я хотел бы использовать его следующим образом:

if (identifiers.Where(i => !i.IsMarkedForDeletion &&  
this.MyEntities.Identifiers.Field1 == i.Field1 && 
this.MyEntities.Identifiers.Field2 == i.Field2 && 
this.MyEntities.Identifiers.Field3 == i.Field3).Any())) 
{ 
    return false; 
} 

return true; 

Надеюсь, это поможет. Даже при том, что это больше, чтобы напечатать, это более понятно и читаемо, тогда используя несколько операторов «где».

+0

Я должен был быть более конкретным. MyEntities.Identifiers - это набор объектов DB, ​​а не только один объект. – Andrew

Смежные вопросы