Отлично работает следующее:
IQueryable<Property> PropertyQuery = PropertyDAO.SearchWithAdditionalParameters(/* elided */);
IQueryable<long> propertyIdQuery = PropertyQuery.Select(p => p.PropertyId);
var relevantFMVs = PropertyDAO.db.FMVHistories.Where(f => propertyIdQuery.Contains(f.PropertyId)).ToList();
Но взрывается следующее:
IQueryable<Property> PropertyQuery = PropertyDAO.SearchWithAdditionalParameters(/* elided */);
var relevantFMVs = PropertyDAO.db.FMVHistories.Where(f => PropertyQuery.Select(p => p.PropertyId).Contains(f.PropertyId)).ToList();
(обратите внимание, что вместо создания propertyIdQuery отдельно, я просто подставил сам запрос в том месте, где была переменная)
Исключение составляет
Невозможно привести тип
'System.Linq.IQueryable 1' to type
'System.Linq.IQueryable
1'. LINQ to
Сущность поддерживает только приведение сущностей
Типы примитивов модели данных.
Может ли кто-нибудь пролить свет на то, что EF (4) делает под прикрытием, чтобы заставить работать только первый запрос, даже если они якобы эквивалентны?
Я знаю, IQueryable<T>
и деревья выражений много чего делают под покровом, но как получается, что сохранение промежуточного шага в локальную переменную может повлиять на результат?
EDIT
По запросу вот полный вызываемый метод и методы, вызываемые этим методом:
public IQueryable<Property> BasicSearchFromConstraints(PropertyInvoiceConstraints constraints) {
return ExecuteSearchFromConstraints((dynamic)constraints.PropertyInst, constraints.CompanyNumber, constraints.TaxSubType, constraints.PhaseID, constraints.State, constraints.County, constraints.City, constraints.Jurisdiction);
}
private IQueryable<T> ExecuteSearchFromConstraints<T>(T property, int CompanyNumber, byte SubType, byte PhaseID, string State, string County, string City, string Jurisdiction) where T : Property {
IQueryable<T> result = base.db.Properties.OfType<T>();
if (SubType > 0)
result = result.Where(p => p.TaxSubTypeId == SubType);
if (CompanyNumber > 0)
result = result.Where(p => p.CompanyNum == CompanyNumber);
if (!String.IsNullOrEmpty(State))
result = result.Where(p => p.State == State);
if (!String.IsNullOrEmpty(County))
result = result.Where(p => p.County == County);
if (!String.IsNullOrEmpty(City))
result = result.Where(p => p.City == City);
if (!String.IsNullOrEmpty(Jurisdiction))
result = result.Where(p => p.Jurisdiction == Jurisdiction);
if (PhaseID > 0)
result = result.Where(p => p.PhaseId == PhaseID);
return result;
}
public virtual IQueryable<Property> SearchWithAdditionalParameters(DataLayer.DAO.PropertyInvoiceConstraints constraints, string propertyNumber = "", string altDesc = "", string countyAcctNumber = "", string City = "", string Jurisdiction = "", string secondaryStateID = "", string LegalDesc = "", string status = "", int? TaxYear = null) {
IQueryable<Property> result = BasicSearchFromConstraints(constraints);
if (!String.IsNullOrEmpty(status))
result = result.Where(p => p.Status == status);
if (!String.IsNullOrEmpty(propertyNumber))
result = result.Where(p => p.PropertyNum.Contains(propertyNumber));
if (!String.IsNullOrEmpty(altDesc))
result = result.Where(p => p.AltDescription.Contains(altDesc));
if (!String.IsNullOrEmpty(countyAcctNumber))
result = result.Where(p => p.CountyAccountNum.Contains(countyAcctNumber));
if (!String.IsNullOrEmpty(City))
result = result.Where(p => p.City.Contains(City));
if (!String.IsNullOrEmpty(Jurisdiction))
result = result.Where(p => p.Jurisdiction.Contains(Jurisdiction));
if (TaxYear.HasValue)
result = result.Where(p => p.FMVHistories.Any(f => f.TaxYear == TaxYear));
if (constraints.FMVPhaseID > 0)
result = result.Where(p => p.FMVHistories.Any(f => f.PhaseId == constraints.FMVPhaseID));
if (!String.IsNullOrEmpty(secondaryStateID))
if (constraints.PropertyInst is WellDetail)
result = result.OfType<WellDetail>().Where(w => w.SecondaryStateId == secondaryStateID);
else
throw new ApplicationException("Invalid use -> Secondary State ID can only be set when searching for Well property types");
if (!String.IsNullOrEmpty(LegalDesc))
if (constraints.PropertyInst is RealEstateDetail)
result = result.OfType<RealEstateDetail>().Where(r => r.LegalDescr.Contains(LegalDesc));
else if (constraints.PropertyInst is RealEstateServicingDetail)
result = result.OfType<RealEstateServicingDetail>().Where(r => r.LegalDescr.Contains(LegalDesc));
else throw new ApplicationException("Invalid use -> Legal Description can only be set when searching for either real estate or real estate servicing property types");
return result;
}
EDIT
Я действительно хотел, чтобы ответ Акаша был правильным, но если бы это было так, я бы ожидал, что средний запрос здесь взорвется, но на самом деле все три работают просто отлично.
Я начинаю подозревать, что структура наследования, которую я имею для типа Property
(из исходного примера), может быть как-то связана с этим.
DummyBookModelEntities db = new DummyBookModelEntities();
IQueryable<int> BookIds = db.Books.Where(b => b.id < 4).Select(b => b.id);
IQueryable<Book> BooksFromIdQuery = db.Books.Where(b => b.id < 4);
try {
var l1 = db.Books.Where(b => BookIds.Contains(b.id)).ToList();
Console.WriteLine("ID Query With ID Local Var Worked: count = {0}", l1.Count);
} catch (Exception E) {
Console.WriteLine("ID Query Failed:");
Console.WriteLine(E.ToString());
Console.WriteLine();
}
try {
var l1 = db.Books.Where(b => BooksFromIdQuery.Select(b_inner => b_inner.id).Contains(b.id)).ToList();
Console.WriteLine("ID Query With Whole Book Local Var Worked: count = {0}", l1.Count);
} catch (Exception E) {
Console.WriteLine("ID Query With Whole Book Local Var Failed:");
Console.WriteLine(E.ToString());
Console.WriteLine();
}
try {
var l1 = db.Books.Where(b => BooksFromIdQuery.Contains(b)).ToList();
Console.WriteLine("Whole Book sub query without select worked: count = {0}", l1.Count);
} catch (Exception E) {
Console.WriteLine("Whole Book sub query without select:");
Console.WriteLine(E.ToString());
Console.WriteLine();
}
EDIT
Я добавил некоторое наследование, и теперь два нижних запроса не выполняются. Похоже, что в любой момент, когда у вас есть OfType()
в запросе, EF просто не хочет анализировать полные запросы в запросах; Вы должны разбить свои подэтапы на локальные переменные.
Сегодня вечером я назначу награду Акашу, если у кого-то еще не будет чего добавить.
DummyBookModelEntities db = new DummyBookModelEntities();
IQueryable<int> BookIds = db.Books.OfType<SciFiBook>().Where(b => b.id < 4).Select(b => b.id);
IQueryable<Book> BooksFromIdQuery = db.Books.OfType<SciFiBook>().Where(b => b.id < 4);
try {
var l1 = db.Books.Where(b => BookIds.Contains(b.id)).ToList();
Console.WriteLine("ID Query With ID Local Var Worked: count = {0}", l1.Count);
} catch (Exception E) {
Console.WriteLine("ID Query Failed:");
Console.WriteLine(E.Message);
Console.WriteLine();
}
try {
var l1 = db.Books.Where(b => BooksFromIdQuery.Select(b_inner => b_inner.id).Contains(b.id)).ToList();
Console.WriteLine("ID Query With Whole Book Local Var Worked: count = {0}", l1.Count);
} catch (Exception E) {
Console.WriteLine("ID Query With Whole Book Local Var Failed:");
Console.WriteLine(E.Message);
Console.WriteLine();
}
try {
var l1 = db.Books.Where(b => BooksFromIdQuery.Contains(b)).ToList();
Console.WriteLine("Whole Book sub query without select worked: count = {0}", l1.Count);
} catch (Exception E) {
Console.WriteLine("Whole Book sub query without select:");
Console.WriteLine(E.Message);
Console.WriteLine();
}
Console.WriteLine();