Entity Framework 5 upgrade from 4 - c#

I'm having an issue with the Where clause in a search, in my original version EF4 I could add a Where clause with 2 parameters, the where clause (string predicate) and a ObjectParameter list such as
var query = context.entities.Where(WhereClause.ToString(), Params.ToArray());
since my upgrade to EF5 I don't seem to have that option am I missing something?
This was originally used to build dynamic where clause such as "it.entity_id = #entity_id" then holding the variable value in the ObjectParameter.
I'm hoping I don't have to rewrite all the searches that have been built out this way, so any assistance would be greatly appreciated.
Cheers

In order to use ESQL with DbContext, you will have to "drop down" to ObjectContext.
var objectContext = ((IObjectContextAdapter)context).ObjectContext;
var query = objectContext.CreateQuery<MyEntity>(
WhereClause.ToString(),
Params.ToArray());

Related

EF PostgreeSQL array_append

I want append a item to column with type int[].
i see PostgreSQL document with array_append can do this.
but how can we do this with EF Core? (i use Npgsql.EntityFrameworkCore.PostgreSQL)
This currently isn't supported (the list of supported array translations can be found here).
In the meantime, as a workaround you can use raw SQL queries to express your query directly in SQL, and possibly compose additional LINQ operators over that.
EDIT: opened https://github.com/npgsql/efcore.pg/issues/2026 to track, this should be doable for 6.0.
raw SQL Query:
var sql= $"UPDATE public.\"TableName\" SET \"ArrayColumnName\" = array_append(\"ArrayColumnName\", {value}) WHERE ... ;";//fill ... with your condition
var result =await _dbContext.Database.ExecuteSqlRawAsync(sql) ;//return int

Entity Framework Core LINQ Tree Expression Problem with Concat

So I have a IQueryable extension that does a little more than this block of code. Essentially I'm combining of bunch of strings and afterwards doing a Contains on them. The problem I'm running into is that Entity Framework Core doesn't support System.String.Concat, and executes that part of the query locally which is definitely not what I want.
Here's the little loop I was using to add these different strings together:
List<Expression> stringExpressionsToConcat = new List<Expression>();
foreach (var member in memberExpressions)
{
stringExpressionsToConcat.Add(member);
stringExpressionsToConcat.Add(spaceConstant);
}
//call the concat to create the final term to search on
NewArrayExpression arrayExpression = Expression.NewArrayInit(typeof(string), stringExpressionsToConcat);
searchStringExpression = Expression.Call(concatMethod, arrayExpression);
This is working client side, but will not get compiled for Entity to SQL. I had the same problem on an Order By clause where I was doing something like this:
.ThenByDescending(e => string.Concat(e.FirstName, " ", e.LastName))
This obviously also did not translate to Entity to SQL, since it's exactly what I'm building in my expression tree. However, changing it to this....
.ThenByDescending(e => e.FirstName + " " + e.LastName)
Does translate to Entity to SQL. So I'm wondering how I can create the same expression represented in the code above that correctly gets sent to SQL. I've tried using Expression.Add but add is not supported on string types in the expression builder. Is this possible or is there some extra code in Entity Framework that makes this possible? I've tried to explore the source code on GitHub but it's a little bit overwhelming to find exactly where it is happening.
Aha! I have figured it out!! This expression tree exhibits the same behavior and sends the data to SQL!
Modified code below:
//more than one member has the property, we need to combine them with spaces in between
List<Expression> stringExpressionsToConcat = new List<Expression>();
foreach (var member in memberExpressions)
{
stringExpressionsToConcat.Add(member);
stringExpressionsToConcat.Add(spaceConstant);
}
searchStringExpression = stringExpressionsToConcat[0];
for (int i = 1; i < stringExpressionsToConcat.Count; i++)
{
searchStringExpression = Expression.Add(searchStringExpression,
stringExpressionsToConcat[i], typeof(string).GetMethod("Concat", new[] {
typeof(string), typeof(string) }));
}
That change goes from Entity Framework throwing the warning in 2.2 (and the error in 3.1) to translating it into the below SQL code!
([e].[MemberExpression1] + N' ') + [e].[MemberExpression2]) + N' ') + [e].[MemberExpression3]) + N' ')
This is exactly how Entity Framework is generating the SQL clause I talked about in my answer with the Order By! I'm not sure what the reasoning is...yet, but if you're looking to create your own expression trees that add together strings that the Entity Framework Core tree visitor can translate into sql this is it!
Credit to this answer for pointing me in the correct direction:
https://stackoverflow.com/a/3858421/5245385
Basically: Wait for EfCore to support this at some point, or use Ef classic for now.
EfCore is very limited in what it supports from LINQ - feels a little like the people developing it did never use it and never read about all the stuff LINQ can. THere is no workaround.
In EfCore 2.2 it was doing client side evaluation - in 3.1 that is disabled and it tells you to go and program different.
If you feel like it, open an issue on the github page and then maybe someone picks it up for the November 5.0 release. Chance is no because this IS a little more arcane and they are overloaded with issues that are way more common.

How to specify index in Entity Framework 5.0

I have a select statement where it returns thousands of records. i want to use Index hints with the query. How do i do this using Entity Framework
var actCosts = db.ActCosts.Where(t => t.ScenarioID == scenarioID).ToList();
like this
select * from ActCost WITH(INDEX(IX_ActCost_ScenarioID)) where ScenarioID = 797
There is no way of adding query hints to the current version of Entity Framework; however, they are considering implementing it in a future release.
See the proposal at https://entityframework.codeplex.com/workitem/261 as well as some possible work-arounds.

Getting a table from a string in EF

I'm trying to get a table (entity) from the entity framework context and all I have is a string parameter input to my service.
In ADO.NET I'd do something like:
var tableName = "tablePrefix" + inputString;
How do I solve this in EF?
Any help will be much appreciated :)
To get a Dbset dynamically without knowing the type upfront you can use the Set method on the DbContext. So you could do.
var type = Type.GetType("MyType");
var set = myContext.Set(type);
Once you have the set you can then query to find an entity by primary key etc.
As Kirill says though, why do you want to do this?
Cheers
Chris

Select clause containing non-EF method calls

I'm having trouble building an Entity Framework LINQ query whose select clause contains method calls to non-EF objects.
The code below is part of an app used to transform data from one DBMS into a different schema on another DBMS. In the code below, Role is my custom class unrelated to the DBMS, and the other classes are all generated by Entity Framework from my DB schema:
// set up ObjectContext's for Old and new DB schemas
var New = new NewModel.NewEntities();
var Old = new OldModel.OldEntities();
// cache all Role names and IDs in the new-schema roles table into a dictionary
var newRoles = New.roles.ToDictionary(row => row.rolename, row => row.roleid);
// create a list or Role objects where Name is name in the old DB, while
// ID is the ID corresponding to that name in the new DB
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] };
var list = roles.ToList();
But calling ToList gives me this NotSupportedException:
LINQ to Entities does not recognize
the method 'Int32
get_Item(System.String)' method, and
this method cannot be translated into
a store expression
Sounds like LINQ-to-Entities is barfing on my call to pull the value out of the dictionary given the name as a key. I admittedly don't understand enough about EF to know why this is a problem.
I'm using devart's dotConnect for PostgreSQL entity framework provider, although I assume at this point that this is not a DBMS-specific issue.
I know I can make it work by splitting up my query into two queries, like this:
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select r;
var roles2 = from r in roles.AsEnumerable()
select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] };
var list = roles2.ToList();
But I was wondering if there was a more elegant and/or more efficient way to solve this problem, ideally without splitting it in two queries.
Anyway, my question is two parts:
First, can I transform this LINQ query into something that Entity Framework will accept, ideally without splitting into two pieces?
Second, I'd also love to understand a little about EF so I can understand why EF can't layer my custom .NET code on top of the DB access. My DBMS has no idea how to call a method on a Dictionary class, but why can't EF simply make those Dictionary method calls after it's already pulled data from the DB? Sure, if I wanted to compose multiple EF queries together and put custom .NET code in the middle, I'd expect that to fail, but in this case the .NET code is only at the end, so why is this a problem for EF? I assume the answer is something like "that feature didn't make it into EF 1.0" but I am looking for a bit more explanation about why this is hard enough to justify leaving it out of EF 1.0.
The problem is that in using Linq's delayed execution, you really have to decide where you want the processing and what data you want to traverse the pipe to your client application. In the first instance, Linq resolves the expression and pulls all of the role data as a precursor to
New.roles.ToDictionary(row => row.rolename, row => row.roleid);
At that point, the data moves from the DB into the client and is transformed into your dictionary. So far, so good.
The problem is that your second Linq expression is asking Linq to do the transform on the second DB using the dictionary on the DB to do so. In other words, it is trying to figure out a way to pass the entire dictionary structure to the DB so that it can select the correct ID value as part of the delayed execution of the query. I suspect that it would resolve just fine if you altered the second half to
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select r.RoleName;
var list = roles.ToDictionary(roleName => roleName, newRoles[roleName]);
That way, it resolves your select on the DB (selecting just the rolename) as a precursor to processing the ToDictionary call (which it should do on the client as you'd expect). This is essentially exactly what you are doing in your second example because AsEnumerable is pulling the data to the client before using it in the ToList call. You could as easily change it to something like
var roles = from rl in Old.userrolelinks
join r in Old.roles on rl.RoleID equals r.RoleID
where rl.UserID == userId
select r;
var list = roles.AsEnumerable().Select(r => new Role { Name = r.RoleName, ID = newRoles[r.RoleName] });
and it'd work out the same. The call to AsEnumerable() resolves the query, pulling the data to the client for use in the Select that follows it.
Note that I haven't tested this, but as far as I understand Entity Framework, that's my best explanation for what's going on under the hood.
Jacob is totally right.
You can not transform the desired query without splitting it in two parts, because Entity Framework is unable to translate the get_Item call into the SQL query.
The only way is to write the LINQ to Entities query and then write a LINQ to Objects query to its result, just as Jacob advised.
The problem is Entity-Framework-specific one, it does not arise from our implementation of the Entity Framework support.

Categories