Problem:
Could I perform the left join and verify the nulled element without
terminating the DB link? If yes, how do I do it?
Trial Code
var transactions1 = (from tran in ctx.transactions
group tran.Amount by new { tran.UserID, tran.LeaveID } into leave
select new { UserID = leave.Key.UserID, LeaveID = leave.Key.LeaveID, Balance = leave.Sum() });
var transactions2 = (from tran in ctx.transactions
where tran.Type == type && tran.FiscalYear == fiscal
group tran.Amount by new { tran.UserID, tran.LeaveID } into leave
select new { UserID = leave.Key.UserID, LeaveID = leave.Key.LeaveID, Rollout = leave.Sum() });
var computed = (from balance in transactions1
join join_rollout in transactions2 on new { balance.UserID, balance.LeaveID } equals new { join_rollout.UserID, join_rollout.LeaveID } into rolls
from rollout in rolls.DefaultIfEmpty()
select new
{
UserID = balance.UserID,
LeaveID = balance.LeaveID,
computed = rollout.Rollout
}).ToList();
Goal:
I am trying to left join two tables using linq. As one could guess, some values of the second table could result in null. If I use a ternary operator to verify nulled values the application throws the following exception
The argument to DbIsNullExpression must refer to a primitive, enumeration or reference type.
Find the stack trace at PastBin
If I remove the ternary verification, the application throws the following exception
The cast to value type 'System.Decimal' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.
Find the stack trace at PasteBin
Working solution:
I am able to avoid these exceptions by terminating the DB link (using .ToList() as suggested by slawek) before the final join (that is, transactions1.ToList() and transactions2.ToList(). However I wish to avoid using .ToList() because I need to perform another join with a DB table which cannot be done on a List.
My Experiments Since the SO post
I've tried using UseDatabaseNullSemantics which I did not expect to resolve it but atleast aid in a workaround, but the result is Failed
The error message that you quoted:
The cast to value type 'System.Decimal' failed because the
materialized value is null. Either the result type's generic parameter
or the query must use a nullable type.
indicates that you are attempting to assing the value of null to a field of System.Decimal type. This is illegal in .Net. System.Decimal is a value type. Unlike a reference type these cannot be null. A good treatment on reference type vs value type can be found here.
When .NET first came out that basically was it. Later on, it became obvious that this can be very inconvenient, so .net designers tucked on a new feature called nullable types. These allow getting around this limitation above. In c# you refer to a nullable type, by specifying a value type and then adding a question mark to it, like this: System.Decimal? or simply decimal?. A field of decimal? type will be able to hold null.
You need to tell the compiler the type you are using, it can't figure it out correctly on its own. Change this line of your code:
computed = rollout.Rollout
to read like this:
computed = (decimal?)rollout.Rollout
This will indicated that null is a valid value, and your code will work.
In general, it helps taking some time to learning the basics, before diving into more complicated stuff like ORM usage. If you do not know the basics, when something breaks it's very difficult to identify what broke - since you don't have all the pieces of the puzzle, but once you know them, often the answer is obvious.
Related
I want to find objects that match a certain requirement. In this case, I want the className attribute of the Course class to be equal to "course". The statement I wanted to use was:
Course matchingCourse = collection.FindAsync(x => x.className == "course");
The className attribute is not the primary key, so I cannot simply put the key in.
From this I get the error "Cannot convert lambda expression to type 'object' because it is not a delegate type". I have looked around and I cannot figure out how to solve the issue in this context.
How can I use a lambda to do what I'm trying to do in FindAsync(), or is there another way?
You can use a Where followed by a FirstOrDefault or SingleOrDefault or just pass the predicate to those, unless you need the cache that Find gives you. This will call the database each time. Find only works on primary keys.
For example:
Course matchingCourse = collection.FirstOrDefault(x => x.className == "course");
For Async:
Course matchingCourse = await collection.FirstOrDefaultAsync(x => x.className == "course");
The main difference between Find and Select is that Find will first check whether the object that you want has been fetched before since you created the DbContext. Select will always fetch the data from the database.
In fact, Find is used, if you fetched an object, lost the reference to it, and want the fetched object again, not the original.
Usually it is not a good idea to keep the DbContext alive for a long time, so chances are very small that you loose the reference to the fetched object.
Therefore my advice would be to use Select to fetch the item:
(leaving out the async, not part of your problem)
var result = collection
.Where(collectionElement => collectionElement.ClassName == "course")
.FirstOrDefault();
If you really need to get the earlier fetched item, you can use DbContext.ChangeTracker.Entries<...>() and do a Where(...) on the already fetched objects
I am trying to save data in 10 entity tables at once. For this reason I am loading the data in the related entity objects and saving it in the end. I am trying to check if the record exist in the database by
var bankContacts = entity.BankContacts.Where(bc => bc.Bank.Equals(bankObj)).FirstOrDefault();
But I am getting the following error: 'Only primitive types or enumeration types are supported in this context.'
I don't have the value of the Bank.BankID to be able to use
var bankContacts = entity.BankContacts.Where(bc => bc.BankID == bankObj.BankID).FirstOrDefault();
How can I solve this issue?
The current bank object is:
This line ...
var bankContacts = entity.BankContacts.Where(bc => bc.Bank.Equals(bankObj)).FirstOrDefault();
... is comparing the two bank objects with .Equals and EF doesn't know how to convert that to a query expression. Remember EF is not going to run your methods like stand linq but will convert them to SQL queries for operation. You need to compare them in another way such as bc.Bank.Id == etc. If you need to make sure all values are equal then you can write custom comparer for EF or just add multiple .where clauses or multiple || operators to test them all.
'Only primitive types or enumeration types are supported in this context'
it means that only built-in data types or enums types which is converted to int are supported.
EF deosn't know how to translate your query to SQL statements to run it against your database.
I am using LINQ-to-Entities (EF 6.1.3) to perform the following query:
var users = msgList.Select(m => m.From)
.Union(msgList.Select(m => m.To))
.Distinct()
.Where(u => u.ID != userId) //userId is an assigned local var.
.ToList();
msgList is a List (already fetched, not a queryable and lazy loading is off) of Messages which consists of some fields like From and To which are guaranteed to be non-null. Both From and To were Included in the original query, so they are guaranteed to be non-null.
My User object is also guaranteed to be non-null, so there's nothing that can actually be null.
However, this line is sometimes throwing a null pointer exception, and sometimes executing perfectly with the exact same user, exact same database, exactly same data (nothing altered). Load is not an issue as it's a code not yet in production and I'm the only one testing it.
The exception seems to be thrown at the Where call:
at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
How can this happen?
UPDATE: This is of course not a duplicate of What is a NullReferenceException, and how do I fix it?. Any sane developer with even a little knowledge in .NET/C#/OOP knows what that error is and that this question has nothing to do with it, even though it involves that exception as a part of it.
UPDATE 2: I've switched it to assigning to a list each line, as suggested below:
var msgListSelection = msgList.Select(m => m.From).ToList();
var union = msgListSelection.Union(msgList.Select(m => m.To)).ToList();
var distinct = union.Distinct().ToList();
var where = distinct.Where(u => u.ID != userId).ToList();
var users = where;
The exception occurs at the where line:
var where = distinct.Where(u => u.ID != User.ID).ToList();
If distinct returned null, it would have been thrown on ToList call of var distinct = union.Distinct().ToList(); on the line above.
Am I missing something?
UPDATE 2: My User class is a POCO C# type mapped to an Entity type in my database which has an ID property of long, and my Message class is again a POCO type mapped in Entity Framework, with navigation properties From and To to some User instances guaranteed to be non-null. They are annotated as Required and I've also checked them at the database level just to be sure.
UPDATE 3: My EF context lives from the beginning of the request (set at a delegating handler in the beginning of the request) to the end. I don't think the problem is related to the lifespan of the DbContext as there are many controllers with the same mechanism with tens of methods that access the context, and I'm only having such problem with this particular method.
UPDATE 4: I've added a null check on distincts:
var distinct = union.Distinct().ToList();
if(distinct == null)
{
throw new Exception("distinct was null");
}
var where = distinct.Where(u => u.ID != userId).ToList();
It seems to pass that point with no problem, but throw the null pointer exception at the last line var where = distinct.Where(u => u.ID != userId).ToList(); which sorts out the possibility that distinct may be null.
UPDATE 5: I've wrote an API testing tool and sent about 250 requests to the same endpoint with the same user. The first one failed with this error, and all the rest succeeded successfully. There seems to be a problem with the first request.
You may be experiencing what is caused by the closure principle. You reference the User property in your LINQ query. Because the LINQ query in itself is executed as an (anonymous) method delegate, the closure principle applies.
Quoting the above link:
In essence, a closure is a block of code which can be executed at a
later time, but which maintains the environment in which it was first
created - i.e. it can still use the local variables etc of the method
which created it, even after that method has finished executing.
The usage of the User property is subject to this principle. Its value can have changed upon the execution of the LINQ query. To protect against this, the User property should be copied to a local variable and that referenced in the LINQ query. Like so:
var user = User;
var users = msgList.Select(m => m.From)
.Union(msgList.Select(m => m.To))
.Distinct()
.Where(u => u.ID != user.ID)
.ToList();
Update
When using a local reference copy to the user property, another possibility for the NullReferenceException may lie with the Select-Union-Distinct methods. When calling ToList, the Where clause is executed on all items in the union of the two Select clauses. By default, Distinct executes the Equals method from the IQuality interface, which would be called on the elements from Select(m => m.From) . If this element is null, it would cause the NullReferenceException.
I've been playing with Dapper and I have a question. Is there a way for me to be able to have an enumerator type property in my POCO class and use Dapper? Seems like every time I add a property which is of type Enumerator I get the following exception:
System.NotSupportedException: The type : *my_enum_type* is not
supported by dapper
Am I missing something here? Is there an attribute that I can attach to these properties to specify which of them are mapping to database table columns?
This is an old bug in dapper, ensure you use the latest version. Dapper used to perform no filtering on the input types properties.
Ok I figured this out and it was my fault for not seeing this in the 1st place. This is what I was doing initially:
Dim result = conn.Query("SELECT * FROM Users WHERE UserID = #UserID", New User With {.UserID = userID})
But what needed to be done is:
Dim result = conn.Query("SELECT * FROM Users WHERE UserID = #UserID", New With {.UserID = userID})
In other words declaring param as anonymous type is essential. If you declare param as a specific type and that type contains a property of type which is not covered by dapper code (such as Enum) then the code crashes with the above mentioned error. My User class had an Enum type property and it was causing the problem.
I have two rateable objects E and S, S is contained within E so you say E.S and they both use the same object R to be rated; R will always have ONLY ONE rated object at a time so if R.E.HasValue == true then R.S.HasValue will never be true.
I have all the R objects from the database and I have E. What would be the lambda expression to get all Rs Where R.S in E.S??
UPDATE
I found this MSDN Documentation where it says:
Referencing a non-scalar variables,
such as an entity, in a query is not
supported. When such a query executes,
a NotSupportedException exception is
thrown with a message that states
"Unable to create a constant value of
type EntityType. Only primitive types
('such as Int32, String, and Guid')
are supported in this context."
So... I can't use A-Dubb's answer since the query reads
R.Where(r => r.SID.HasValue).Where(r => E.S.Contains(r.S))
Which results in a NotSupportedException... I still have the property R.SID which is of type int? but how could I then query all E.S to get their IDs?
P.S. E.S. is of type EntityCollection.
I will say that IN clauses are normally represented with call to Contains. For example
public class R {
public int Id {get; set;}
}
public IEnumerable<R> InClause(IEnumerable<int> ids) {
var subset = ids.ToList();
return dbContext.Query<R>.Where(r => subset.Contains(r.Id));
}
I know that's not the exact API call in EF, but it should help set the basis for your solution. Maybe you already know how to generate IN clauses but I figure if you did then you presumably wouldn't be stuck. If the IN clause isn't the difficult part of the query then what is? Maybe the piece of business logic you mentioned? Again, a C# object model would help.
The problem with using contains though, is that its closer to sql "like" than "in". So if you have user entry on say 8 digit ID's and someone
enters 0 then contains will bring you back ALL id's with zero somewhere in it.