I have this simple EF Core query which throws an exception, to my surprise:
var test = await _context.GroupMessages
.Select(m => new MinimalChannelMessage()
{
id = m.Id,
er = m.Emojis.ToDictionary(e => e.Emoji, e => e.Users.Select(u => u.UserId))
})
.ToListAsync();
As you can see from the query, the tables are GroupMessages -> Emojis -> N-to-M table with UserId
The exception I get is:
When called from 'VisitLambda', rewriting a node of type 'System.Linq.Expressions.ParameterExpression' must return a non-null value of the same type. Alternatively, override 'VisitLambda' and change it to not visit children of this type.
I haven't been able to figure out why this simple query fails, or what the exception message is trying to tell me. I'm happy for any help!
Thanks
While the suggested bug report https://github.com/dotnet/efcore/issues/18440 might be related, I had trouble finding an alternative amongst those listed. I tried...
er = new Dictionary<string, string[]>(m.Emojis.AsEnumerable().Select(e => new KeyValuePair<string, string[]>(e.Emoji, e.Users.Select(u => u.UserId).ToArray())))
er = m.Emojis.AsEnumerable().ToDictionary(e => e.Emoji, e => e.Users.Select(u => u.UserId).ToArray())
None of those two worked for me. So I ultimately had to just include the whole tree, then do the ToDictionary in memory...
Related
var test = await Db.Tests.Include(test => test.Questions)
.ThenInclude(ques => ques.Choices.Select(
ch => new {
ch.Id, ch.OptionName,
ch.OptionText, ch.OptionDetails,
ch.IsAnswer, ch.QuestionId
})).AsNoTracking()
.FirstOrDefaultAsync(z => z.TestId == testId);
Without Select clause it works fine. But I don't need all the Choices properties, so I try to select some of the properties by using Select clause.
It throughs this error:
System.InvalidOperationException: Lambda expression used inside
Include is not valid.
Can anybody tell me what's wrong here?
I think you want something like:
var test = await Db.Tests
.Where(test => test.TestId == testId)
.Include(test => test.Questions) // Shouldn't need this
.ThenInclude(ques => ques.Choices) // Shouldn't need this
.SelectMany(test => test.Questions.SelectMany(ques => ques.Choices.Select(ch => new {
ch.Id,
ch.OptionName,
ch.OptionText,
ch.OptionDetails,
ch.IsAnswer,
ch.QuestionId
}))))
.AsNoTracking();
This will give you all the choices for a particular test.
But... since you're materializing the results as a lambda, you actually shouldn't need either Include. (EF will give warnings saying that it ignored your Include in the console anyway).
Having been comfortably using Entity Framework for many years, I've just been thrown in the deep end with a project that uses NHibernate and I'm really struggling. The documentation is sparse and unhelpful if you're working with it for the first time, and most tutorial and example sites are out of date - I understand it changed significantly in v3?
Normally, I learn things best when trying to work with them, so I jumped in and tried to see what I could do. But I've hit a bug in this pre-existing function (none of this code is mine):
public IDictionary<long, string> GetSeriesFilterData(string userId)
{
Series seriesAlias = null;
Event eventAlias = null;
Session sessionAlias = null;
Dealership dealershipAlias = null;
var query = _repository.Session.QueryOver(() => seriesAlias)
.Where(() => !seriesAlias.IsArchived);
var dealershipIds = QueryOver.Of<ApplicationUserDealership>()
.Where(x => x.ApplicationUser_Key == userId)
.SelectList(list => list.SelectGroup(x => x.Dealership_Id));
dealershipIds.Where(x => x.Dealership_Id == dealershipAlias.Id);
query
.JoinAlias(() => sessionAlias.Dealership, () => dealershipAlias, JoinType.LeftOuterJoin)
.WithSubquery.WhereExists(dealershipIds);
var results = query.SelectList(x => x
.SelectGroup(() => seriesAlias.Id)
.SelectGroup(() => seriesAlias.Name))
.List<object[]>()
.ToDictionary(x => (long) x[0], x => (string) x[1]);
return results;
}
The exception, thrown when collecting the result, is:
An exception of type 'NHibernate.QueryException' occurred in NHibernate.dll but was not handled in user code
Additional information: could not resolve property: sessionAlias
My suspicion is that this is because dealershipIds is empty, but I'm struggling to prove that this is the case. Working with Entity Framework, it's possible to see the results of a query during debugging by unpacking its object graph. However, I can't seem to do that in NHibernate.
Is it possible to see the results of a query fragment via debugging, or do I have to pull it out with a Select statement?
The QueryOver you've posted seems to be missing the part where the
sessionAlias alias is assigned to something.
As it's being done for the others (except eventAlias which seems completely unused) the alias needs to be assigned to a property/path on the entity class being queried via JoinAlias/JoinQueryOver or to the entity itself as it's done on the QueryOver creation with seriesAlias.
Then, that alias (variable) can be used in Where, OrderBy, etc.
For example, supposing that Session is an entity referenced from a property of Series (no clue regarding your actual entity model) the following would fix your problem as it will bind sessionAlias to that property:
// This binds the alias to the property.
query.JoinAlias(s => s.Session, () => sessionAlias);
// This is the same as above, but uses the previously defined alias for the main entity,
// just to show how aliases can be used.
query.JoinAlias(() => seriesAlias.Session, () => sessionAlias);
Consider following LINQ query:
var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
select new
{
ItemProp1 = obj,
ItemProp2 = obj.NavProp2.Any(n => n.Active)
}).SingleOrDefault();
This runs as expected, but item.ItemProp1.NavProp1 is NULL.
As it explains here this is because of the query actually changes after using Include(). but the question is what is the solution with this situation?
Edit:
When I change the query like this, every things works fine:
var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
select obj).SingleOrDefault();
Regarding to this article I guess what the problem is... but the solution provided by author not working in my situation (because of using anonymous type in final select rather than entity type).
As you mentioned, Include is only effective when the final result of the query consists of the entities that should include the Include-d navigation properties.
So in this case Include has effect:
var list = _db.SampleEntity.Include(s => s.NavProp1).ToList();
The SQL query will contain a JOIN and each SampleEntity will have its NavProp1 loaded.
In this case it has no effect:
var list = _db.SampleEntity.Include(s => s.NavProp1)
.Select(s => new { s })
.ToList();
The SQL query won't even contain a JOIN, EF completely ignores the Include.
If in the latter query you want the SampleEntitys to contain their NavProp1s you can do:
var list = _db.SampleEntity
.Select(s => new { s, s.NavProp1 })
.ToList();
Now Entity Framework has fetched SampleEntitys and NavProp1 entities from the database separately, but it glues them together by a process called relationship fixup. As you see, the Include is not necessary to make this happen.
However, if Navprop1 is a collection, you'll notice that...
var navprop1 = list.First().s.Navprop1;
...will still execute a query to fetch Navprop1 by lazy loading. Why is that?
While relationship fixup does fill Navprop1 properties, it doesn't mark them as loaded. This only happens when Include loaded the properties. So now we have SampleEntity all having their Navprop1s, but you can't access them without triggering lazy loading. The only thing you can do to prevent this is
_db.Configuration.LazyLoadingEnabled = false;
var navprop1 = list.First().s.Navprop1;
(or by preventing lazy loading by disabling proxy creation or by not making Navprop1 virtual.)
Now you'll get Navprop1 without a new query.
For reference navigation properties this doesn't apply, lazy loading isn't triggered when it's enabled.
In Entity Framework core, things have changed drastically in this area. A query like _db.SampleEntity.Include(s => s.NavProp1).Select(s => new { s }) will now include NavProp1 in the end result. EF-core is smarter in looking for "Includable" entities in the end result. Therefore, we won't feel inclined to shape a query like Select(s => new { s, s.NavProp1 }) in order to populate the navigation property. Be aware though, that if we use such a query without Include, lazy loading will still be triggered when s.NavProp1 is accessed.
I know this will probably get a few laughs, but don't forget the obvious like i just did. The row in the database didn't actually have a foreign key reference! I should have checked the dam data first before thinking EF Include wasn't working! Grrr. 30 minutes of my life I won't get back.
If your model is defined properly it should work without any problems.
using System.Data.Entity;
var item = _db.SampleEntity
.Include(p => p.NavigationProperty)
.Select(p => new YourModel{
PropertyOne = p.Something,
PropertyTwo = p.NavigationProperty.Any(x => x.Active)
})
.SingleOrDefault(p => p.Something == true);
How did you find that item.ItemProp1.NavProp1 is null. EF uses proxies to load all required properties when you try to access it.
What about
var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
select obj).SingleOrDefault();
Assert.IsNotNull(obj.NavProp1);
Assert.IsNotNull(obj.NavProp2);
You can also try with
var item = (from obj in _db.SampleEntity.Include(s => s.NavProp1)
select new
{
ItemProp1 = obj,
NavProp1 = obj.NavProp1,
ItemProp2 = obj.NavProp2.Any(n => n.Active)
}).SingleOrDefault();
Assert.IsNotNull(item.NavProp1)
Of course I assume that you don't have any problems with EF navigation property mappings.
I'm writing an ASP.NET Web Pages application and in it, I have a massive LINQ to Entities query. This query pulls data from a table in the database, filters it, groups the data twice, and adds extra properties to the result set. I then loop through the table, outputting the rows.
The query is quite big, sorry:
accountOrders = db.EventOrders
.Where(order => order.EventID == eventID)
.OrderBy(order => order.ProductCode)
.GroupBy(order => new { order.AccountNum, order.Exhibitor, order.Booth })
.Select(orders =>
new {
Key = orders.Key,
ProductOrders = orders
.GroupBy(order => new { order.ProductCode, order.Product, order.Price })
.Select(productOrders =>
new {
Key = productOrders.Key,
Quantity = productOrders.Sum(item => item.Quantity),
HtmlID = String.Join(",", productOrders.Select(o => (o.OrderNum + "-" + o.OrderLine))),
AssignedLines = productOrders.SelectMany(order => order.LineAssignments)
})
})
.Select(account =>
new {
Key = account.Key,
// Property to see whether a booth number should be displayed
HasBooth = !String.IsNullOrWhiteSpace(account.Key.Booth),
HasAssignedDigitalLines = account.ProductOrders.Any(order => order.AssignedLines.Any(line => line.Type == "digital")),
// Dividing the orders into their respective product group
PhoneOrders = account.ProductOrders.Where(prod => ProductCodes.PHONE_CODES.Contains(prod.Key.ProductCode)),
InternetOrders = account.ProductOrders.Where(prod => ProductCodes.INTERNET_CODES.Contains(prod.Key.ProductCode)),
AdditionalOrders = account.ProductOrders.Where(prod => ProductCodes.ADDITIONAL_CODES.Contains(prod.Key.ProductCode))
})
.ToList();
I use the added properties to help style the output. For example, I use HasBooth property to check whether or not I should output the booth location in brackets beside the exhibitor name. The problem is I have to save this big query as an IEnumerable, meaning I get the error: Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type. Should I even be manipulating the query this way?
Any advice is much appreciated!
At some point, you are passing in a dynamic datatype to the method, which in turn changes the return type to simply dynamic. You can either cast the dynamic type to a type that is recognised at compile time or explicitly set the return type instead of using var.
You can read more about this issue here: http://www.mikesdotnetting.com/Article/198/Cannot-use-a-lambda-expression-as-an-argument-to-a-dynamically-dispatched-operation
I have made myself an ExpressionBuilder class that helps me put together expressions that can be used as a predicate when doing Linq to Sql queries. It has worked great. However, I just discovered Expressions can only be used to filter on Tables, and not on EntitySets??Why on earth is this the case?
For example if I have Company and an Employee with a Salary. I could create these two expressions:
Expression<Func<Company, bool>> cp = x => x.Name.StartsWith("Micro");
Expression<Func<Employee, bool>> ep = x => x.Name.StartsWith("John");
I would then expect to be able to do the following, however it only partially works:
var companies = dataContext.Companies
.Where(cp) // Goes fine
.Select(x => new
{
x.Name,
SumOfSalaries = x.Employees
.Where(ep) // Causes compile-time error
.Sum(y => y.Salary),
}
.ToList();
Also, if I do a ep.Compile() it compiles, but then I get an error when running the query.
Why is this the case? Am I missing something? I don't find this logical. Can I fix this somehow? Or do you have a good workaround?
I know that I in this case could just use Where(x => x.Name.StartsWith("John")) instead, but the problem is that the expressions I need are not that trivial. They are longer strings of AndAlsos and OrElses.
If you are going to pass a lambda expression to a LINQ to SQL provider don't create it as an Expression<T> - let the provider do that for you.
The following works for me - note both are compiled:
var companies = dataContext.Companies.Where(cp.Compile())
.Select(x => new
{
x.Name,
SumOfSalaries = x.Employees
.Where( ep.Compile() )
.Sum(y => y.Salary),
}
).ToList();
The expression parser seems to be losing type information in there somewhere after the first where clause when you put in the second. To be honest, I'm not sure why yet.
Edit: To be clear, I do understand that EntitySet doesn't support passing an expression into the where clause. What I don't completely understand is why it fails when you add the Where(ep.Compile()).
My theory is that in compiling the first where (Where(cp.Compile()), Linq2Sql quits parsing the expression - that it can't parse the ep.Compile() against an entityset, and is unable to decide to break up the query into two until you compile the first where clause.
I think you need to rewrite your query. Another way to ask for the details you want, is: "Give me the sum of the salaries for the selected employees in the selected companies, organized by the company name".
So, with the employee salaries in focus, we can write:
//Expression<Func<Employee, bool>> ep = x => x.Name.StartsWith("John");
//Expression<Func<Company, bool>> cp = x => x.Name.StartsWith("Micro");
Expression<Func<Employee, bool>> ep = x => x.Name.StartsWith("John");
Expression<Func<Employee, bool>> cp = x => x.Company.Name.StartsWith("Micro");
var salaryByCompany = dataContext.Employees
.Where(ep)
.Where(cp)
.GroupBy(employee => employee.Company.Name)
.Select(companyEmployees => new
{
Name = companyEmployees.Key,
SumOfSalaries = companyEmployees.Sum(employee => employee.Salary)
});
var companies = salaryByCompany.ToList();