Performing SQL "in" equivalent in EF - c#

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.

Related

How can I use a lambda function in the FindAsync() method?

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

Save only once by loading everything in the memory

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.

Using a Projection in place of Find() in LINQ

Relatively new to MVC and LINQ.
Using built-in methods with LINQ/EF do not generally work for me because I' need to use multiple tables and create a projection to a new type to get the data I need.
When returning an iEnumerable, I am able to do this without a problem. My view is expecting an iEnumerable and my LINQ query provided just that.
However, when I try to replace the Find() method to return a single record, I'm running into a problem.
My projection is identical to the one that returns multiple records but I have added a where clause to limit the return to a single record (using a unique ID).
I've changed my view to expect and object of the type I'm projecting to but that is as far as I can get.
If I just return the query as is, deferred execution causes a problem in that I'm actually passing the query to my view, not the results (as expected).
So I'm trying to figure out how to (I guess) execute the query and get the object in my controller and then pass that to the view. I've tried SingleOrDefault() and the like to 'force' execution but that generates a new exception (Unable to create a constant value of type 'System.Object'. Only primitive types or enumeration types are supported in this context.)
Clearly I don't understand something that is going on here.
var model =
(from p in db.t_Protocol
join pt in db.t_ProtocolType on p.ProtocolTypeId equals pt.ProtocolTypeID
where p.ProtocolId.Equals(id)
select new ProtocolView
{
ProtocolId = p.ProtocolId,
Protocol = p.Protocol,
ProtocolType = pt.ProtocolType,
IsAdmission = p.IsAdmission,
IsReleased = p.IsReleased
})
;
My view is expecting:
#model ECM.Models.ProtocolView
First of all by stating your view is expecting #model ECM.Models.ProtocolView states that the view is only expecting a single record. If when setting your Var model the child table has more than 1 record then your projection will force n number of records into model regardless of the parent only having 1 record. for your view to expect an IEnumerable you need to change your view declaration to be #model IEnmerable<ECM.Models.ProtocolView> Also if you really need to perform a Find use IQueryable first them return the result as IEnumerable since the payload on Ienumerable is smaller than IQueryable

Left Outer Join in Entity Framework (Linq). Member materializing to Null

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.

Linq to SQL Expression Properties That are Translatable to SQL

I have a LINQ to SQL class, we'll call it Test, and I want to be able to access properties with LINQ queries but I get the famed "No Supported Translation to SQL" runtime error. I'm interested in the conceptual problem. Here is my simplified class:
public class Test
{
public int ID {get; set;} // Stored in Database
public int NonForeignKeyValue {get; set;} // Stored in Database
}
Here is sort of an example of what I'm trying to accomplish, but I don't want the overhead of always explicitly writing the join in LINQ:
var db = (new DataContext()).GetTable<Test>();
var q = (from t in db.GetTable<Test>()
join o in db.GetTable<OtherTable>() on o.ID equals t.ID
where t.OtherStuff
select t)
I'd like to be able to add a property to Test that tells me if there are any rows in OtherTable that could be joined with Test:
public bool IsInOtherTable
{
get
{
return (new DataContext())
.GetTable<OtherTabke>()
.Any(x => x.NonForeignKeyValue == this.NonForeignKeyValue));
}
}
Ultimately this is what I want my code to look like, but it errors. I basically want to return all entries that contain some database computed value:
using (DataContext db = new DataContext())
{
var q = db.GetTable<Test>()
.Where(x => x.IsInOtherTable && x.OtherStuff); //Error
}
I'm basically trying to save myself from writing this code every single time I want to check if Test has certain information in another table. I'm not that interested in the exact problem I described, I'm more interested in how to conceptually add the join part to the SQL and still use LINQ. I'm guessing I use Linq.Expression, but I really don't know and I'm not aware of how to do it.
As an aside, I could just write the actual SQL, as its not that complicated, but I'd like to know how to get around this and still use LINQ.
Edit: I tried this property, but I get the same error. Its more complicated that just changing the return type to Expression...
public System.Linq.Expressions.Expression<Func<Article3, bool>> Exists
{
get
{
using (DataContext db = new DataContext())
{
return i => db.GetTable<OtherTable>()
.Any(x => x.NonForeignKeyValue == i.NonForeignKeyValue));
}
}
}
Each time the linq generator is to translate a code into a query, it has to process an expression tree.
In your examples, you are not passing around expression but rather - properties, delegates, i.e. stuff which the expression visitor is unable to "step into".
In general, try to rethink your conditions so that instead of bool you have Expression<T, bool> etc.
http://netpl.blogspot.com/2008/02/linq-to-object-vs-linq-to-sql-and.html
Firstly, I think you may be overestimating "the overhead of always explicitly writing the join in LINQ". It's an extra line of code which has the advantage of being relatively self-documenting as to just what you are doing (always a nice thing), and any other approach is going to be turned first into SQL and then into a query plan that will be at least as expensive to execute, possibly more expensive (SQLServer is good a joins!)
Still, there are two alternatives I can thinkof.
One is to have an EntityRef property on the class that defines this relationship with the other table. You can then test if it is null in your query (or EntitySet if it's on the other side of a one-to-many relationship).
The other is to define a SQL function that returns a bit result indicating whether an id refers to a row that does or doesn't relate to the other table.
Then define a protected method on your DataContext-derived class that matches the signature in C# terms, and has a Function attribute mapping it to that SQL function. (Since this isn't something that you can give a sensible non-db-using version of in the C# version, you can implement the C# function by calling ExecuteMethodCall).
Then you can use that method instead of the join.
Still, this is likely less self-explanatory in the code and at risk of being less efficient than just keeping the join.

Categories

Resources