EF6 LINQ cast column - linq

My entity has a field called Value. This field is a string, but it hold int and DateTime values also.
My regular query is something like
SELECT * FROM tableA
WHERE entityType=#eID and CAST(value as int/datetime/varchar)==#value
How can I make EF generate a query with a cast?
Edit: create suggestion on uservoice

Here is example:
int eId = 1;
int valueInt = 1;
DateTime valueDateTime = DateTime.Now;
string valueString = "Test";
TableA result;
switch (eId)
{
case 1: result = context.tableA.Where(x => x.entityType == eId && Convert.ToInt32(x.value) == valueInt); break;
case 2: result = context.tableA.Where(x => x.entityType == eId && Convert.ToDateTime(x.value) == valueDateTime); break;
case 3: result = context.tableA.Where(x => x.entityType == eId && x.value == valueString); break;
}

It was implemented and completed by the EF team. Check the user invoice link listed on the question

Related

EF Core performance issue when using dynamic query using Expressions in .Net

Requirement : Select list of records from table for members whose ID and First name matches with records.
The number of members in the request may vary as per the request:
I have written following code to generate dynamic query using expressions.
private Expression<Func<TmpOncMemberAuth, bool>> CreateQueryExpression(List<MemberRequest> memberRequests)
{
var type = typeof(TmpOncMemberAuth);
// member =>
var memberParam = Expression.Parameter(type, "member");
var requests = memberRequests;
Expression selectLeft = null;
Expression filterExpression = null;
foreach (var member in requests)
{
var comparison = BuildSubQuery(member, memberParam, type);
if (selectLeft == null)
{
selectLeft = comparison;
filterExpression = selectLeft;
continue;
}
filterExpression =
Expression.Or(filterExpression, comparison);
}
return Expression.Lambda<Func<TmpOncMemberAuth, bool>>
(filterExpression, memberParam);
}
private Expression BuildSubQuery(MemberRequest member, ParameterExpression memberParam, Type type)
{
// LHS
// member.MemberID
Expression leftMemberID = Expression.Property(memberParam, type.GetProperty("MemberId"));
Expression leftMemberFirstName = Expression.Property(memberParam, type.GetProperty("MemberFirstName"));
// RHS
Expression requestMemberID = Expression.Constant(member.MemberID);
Expression requestFirstName = Expression.Constant(member.FirstName);
// member.MemberID == requestMemberID
Expression compareID =
Expression.Equal(leftMemberID, requestMemberID);
Expression compareFName =
Expression.Equal(leftMemberFirstName, requestFirstName);
// condition for a member
Expression comparison = Expression.AndAlso(compareID, compareFName);
return comparison;
}
// query call in the main
var whereQuery = CreateQueryExpression(memberData);
var memberAuthData = await FindAllAsyncFromTemp(whereQuery);
This generate a SQL query in the below format, which take a lot time
SELECT [#].[CaseID] AS [CaseId], [#].[MemberID] AS [MemberId], [#].[MemberFirstName], [#].[MemberLastName], [#].[MemberDOB]
FROM [#ONCMemberAuth] AS [#]
WHERE ((CASE
WHEN (([#].[MemberID] = '12345') AND ([#].[MemberFirstName] = 'James') ) THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END | CASE
WHEN (([#].[MemberID] = '6789') AND ([#].[MemberFirstName] = 'WILLERS') ) THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END)
) = CAST(1 AS bit)
I need to create SQL query like below which executes faster than the above one
SELECT [#].[CaseID] AS [CaseId], [#].[MemberID] AS [MemberId], [#].[MemberFirstName], [#].[MemberLastName], [#].[MemberDOB]
FROM [#ONCMemberAuth] AS [#]
WHERE ((([#].[MemberID] = '1234') AND ([#].[MemberFirstName] = 'James')) OR (([#].[MemberID] = '56789') AND ([#].[MemberFirstName] = 'WILLERS') ) )

Having trouble trying to order using linq

I am trying to order by start date(s.StartDate). Below is my code so far, my understanding is that I should be adding .orderby(s.StartDate) somewhere but I don't think I'm even taking the correct route now as I have tried many ways.
var query = from s in context.SessionSearch
where s.Children == 0 && s.IsPublic == isPublic
select s;
var query = from s in context.SessionSearch
where s.Children == 0 && s.IsPublic == isPublic
if (startDate != null)
{
query = query.Where(s => s.StartDate >= startDate && s.StartDate <= endDate);
}
You should be able to start with the "without startdate" option - you have a couple of options here - either declare the type of the query specifically:
IQueryable<SessionSearch> query = from s in context.SessionSearch
where s.Children == 0 && s.IsPublic == isPublic
order by s.StartDate
select s;
And then as you've tried, add the additional where clause to the query if there's a start date passed in:
query = query.Where(s => s.StartDate >= startDate && s.StartDate <= endDate);
This means that you would not be able to add further ordering via the ThenBy methods.
Alternatively, you can add the order by clause after you've finished adding the where clauses:
var query = from s in context.SessionSearch
where s.Children == 0 && s.IsPublic == isPublic
select s;
if (startDate != null) {
query = query.Where(s => s.StartDate >= startDate && s.StartDate <= endDate);
}
query = query.OrderBy(s => s.StartDate);
Props to JonSkeet for pointing these out.

Where clause using Expression tree builder

I have to fetch data from database and filter data using the linq where clause.
My filter is an integer column and it contains value more than 1000.
What i am doing in the code is, breaking this huge array into chunk of 1000's of each and putting it in the where clause of a base query
int j = 0;
int batchsize = 1000;
while ((j * batchsize) < items.Count())
{
List<long> batch = items.Skip(j * batchsize)
.Take(batchsize).ToList();
prequery = prequery.Where(x => batch.Contains(x.Id));
j++;
}
the query which is getting generated in sql is below,
SELECT
x.name,
x.email
FROM
table x
WHERE
x.Id IN (1,2,3,...,1000) AND
x.Id IN (1001,1002,1003....,2000)
i want the query to be generated as below,
SELECT
x.name,
x.email
FROM
table x
WHERE
x.Id IN (1,2,3,...,1000) OR
x.Id IN (1001,1002,1003....,2000)
can i achieve this using expression tree builder and generate the query dynamically, if so please help in doing
you could use the "Concat" API:
int j = 0;
int batchsize = 1000;
IQueryable<YourType> finalQuery = null;
while ((j * batchsize) < items.Count())
{
List<long> batch = items.Skip(j * batchsize)
.Take(batchsize).ToList();
if (finalQuery == null) {
finalQuery = prequery.Where(x => batch.Contains(x.Id));
}
else
finalQuery = finalQuery.Concat (prequery.Where(x => batch.Contains(x.Id)));
j++;
}
This will logically get you what you want: basically you want an "OR" operation between batches. Concat is translated into an "UNION ALL" database call.
BUT ... I don't understand why are you doing this, after all you are grabbing all your data, chunks are not helping you because in the end there will be only one statement executed.

Combining Sub Queries Into 1 Query Linq

Is there a way I can rewrite the following query to make it just one query?
try
{
var fileIds = (from f in context.SignalTo
where f.SignalFileID == 2
select new { f.GFileID }).ToList();
foreach (var id in fileIds)
{
var pp = (from p in context.ProjectFiles
where p.FileID == id.GFileID && p.ProjectID == ProjectID
select p);
if (pp != null)
{
ProjectFiles projectFile =(ProjectFiles) pp;
projectFile.MStatus = Status;
projectFile.DateLastUpdated = DateTime.Now;
context.SaveChanges();
}
}
}
You can combine the two query parts of your code into one.
You would then need to loop over the result set, making your updates. You would then call context.SaveChanges to submit all changes in one batch.
I can't tell if your existing code actually runs or compiles, but you need something like this:
Get the list of file ids you're interested in:
var fileIds = from f in context.SignalTo
where f.SignalFileID == 2
select f.GFileID;
fileIds is at this point an IQueryable where I assume T is an Int. No query has been excuted yet.
Now you can do
var pps = from p in context.ProjectFiles
where fileIds.Contains(p.FileID) && p.ProjectID == ProjectID
select p;
Still no query executed.
Then iterate over the result set
foreach( var pp in pps ) // query executed now
{
pp.MStatus = Status;
pp.DateLastUpdated = DateTime.Now;
}
context.SaveChanges(); // batch of updates executed now

using if else with LINQ Where

I want to generate dynamic query to check manage the where clause with number of parameters available...if some parameter is null i don't want to include it in the where clause
var test = from p in _db.test
where if(str1 != null){p.test == str} else i dnt wanna check p.test
I have around 14 parameters for the where clause
need help,
thanks
You can do it in steps:
// set up the "main query"
var test = from p in _db.test select _db.test;
// if str1 is not null, add a where-condition
if(str1 != null)
{
test = test.Where(p => p.test == str);
}
In addition to #Fredrik's answer, you can also use the short-circuit rules when evaluating boolean expressions like so:
var test = from p in _db.test
where str1 == null || p.test == str1;
Edit If you have lots of strings to test, (str1, str2, etc...) then you can use the following, which will be translated to an SQL IN clause:
var strings = new List<string>();
if (str1 != null) strings.Add(str1);
if (str2 != null) strings.Add(str2);
if (str3 != null) strings.Add(str3);
...
var test = from p in _db.test
where strings.Contains(p.test);
It's even easier if your strings are already in a collection (which, if you've got 14 of them, I assume they would be...)
Consider param1 and param2 are the parameters. Your query should be as under:
string param1 = "Value1";
string param2 = "Value2";
var q = from bal in context.FxBalanceDetails
where (string.IsNullOrEmpty(param1) || bal.Column1 == param1)
&& (string.IsNullOrEmpty(param2) || bal.Column2 == param2)
select bal;
This will ensure that the where clause gets applied for the particular parameter only when it is not null.
var test =
from p in _db.test
where p.str1 != null ? p.str1 : ""
select p;
Do you check the strings against the same Field of the entity?
If so you can write something like:
var strings = new[] { "foo", "bar", "ok", "", null };
var query = dataContext.YourTable.AsQueryable();
query = strings.Where(s => !string.IsNullOrEmpty(s))
.ToList()
.Aggregate(query, (q, s) => q.Where(e => e.YourField == s));
EDIT:
The previous solution is overcomplicated:
var strings = new[] { "foo", "bar", "ok", "", null }.Where(s => !string.IsNullOrEmpty(s))
.ToList();
var query = dataContext.YourTable.Where(e => strings.Contains(e.YourField));

Resources