EF Core: The LINQ expression could not be translated - Net Core 3.1 - entity-framework

I am trying to implement a query to find all Conditions in each Site that match the assigning user's conditions and delete them - excluding the ones that don't match.
var savedPartnerConditions = eipDbContext.EIP_User_Partner_Condition.Where(savedCondition => savedCondition.EIP_User_Partner_Id == savedPartner.EIP_User_Partner_Id && existingUser.CurrentUserConditions.Any(condition => condition.Code == savedCondition.Code));
eipDbContext.EIP_User_Partner_Condition.RemoveRange(savedPartnerConditions);
But the query cannot be translated resulting in the following error:
.Any(condition => condition.Code == e.Code))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync()
How can I contruct the query to fix the error?

I was able to fix this with the following code that seems to work:
var selectionResultSet = eipDbContext2.EIP_User_Partner_Condition.Where(savedCondition => savedCondition.EIP_User_Partner_Id == savedPartner.EIP_User_Partner_Id).ToList();
var savedPartnerConditions = selectionResultSet
.AsEnumerable()
.Where (savedCondition => (existingUser.CurrentUserConditions.Any(condition => condition.Code == savedCondition.Code)));
eipDbContext3.EIP_User_Partner_Condition.RemoveRange(savedPartnerConditions);

Related

use Intersect into IQueryable and EfCore

I'm trying to use LinQ Intersect (or equivalent) into an IQueryable method but it seems like I'm doing it wrong.
I have some PRODUCTS that match some SPECIFITY (like colors, materials, height...), those specifications have different values, for example:
color : blue, red, yellow
height : 128cm, 152cm...
I need to get the products that match ALL the list of couple specifityId / specifityValue I provide.
Here what I'm trying to do:
// The list of couple SpecifityID (color, material..) / SpecifityValue (red, yellow, wood...)
List<string> SpecId_SpecValue = new List<string>();
SpecId_SpecValue.Add("3535a444-1139-4a1e-989f-795eb9be43be_BEA");
SpecId_SpecValue.Add("35ad6162-a885-4a6a-8044-78b68f6b2c4b_Purple");
int filterCOunt = SpecId_SpecValue.Count;
var query =
Products
.Include(pd => pd.ProductsSpecifity)
.Where(z => SpecId_SpecValue
.Intersect(z.ProductsSpecifity.Select(x => (x.SpecifityID.ToString() + "_" + x.SpecifityValue)).ToList()).Count() == filterCOunt);
I got the error : InvalidOperationException: The LINQ expression 'DbSet() could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information. which mean it can't be translated to SQL and I need to ToList before my filter.
The problem is, I don't want to call ToList() because I got huge number of products in my Database and I don't want to load them in memory before filtering them.
Is there an other way to achieve what I need to do?
I ended up using a solution found in the link #Gert Arnold provide here.
I used BlazarTech.QueryableValues.SqlServer #yv989c's answers
Here's what is now working like a charm :
// The list of couple SpecifityID (color, material..) / SpecifityValue (red, yellow, wood...)
Dictionary<Guid, string> SpecId_SpecValue = new Dictionary<Guid, string>();
SpecId_SpecValue.Add(new Guid("3535a444-1139-4a1e-989f-795eb9be43be"), "BEA");
SpecId_SpecValue.Add(new Guid("35ad6162-a885-4a6a-8044-78b68f6b2c4b"), "Purple");
// BlazarTech.QueryableValues.SqlServer
var queryableValues = DbContext.AsQueryableValues(SpecId_SpecValue);
var query = Products.Include(pd => pd.ProductsSpecifity)
.Where(x => x.ProductsSpecifity
.Where(e => queryableValues
.Where(v =>
v.Key == e.SpecifityID &&
v.Value == e.SpecifityValue
)
.Any()
).Count() == dynamicFilter.Count);
The query expresses "products of which all x.SpecifityID.ToString() + "_" + x.SpecifityValue combinations exactly match some given combinations".
Set combination operators like Except often don't play nice with EF for various reasons I'm not going into here. Fortunately, in many of these cases a work-around can be found by using Contains, which EF does support well. In your case:
var query = Products.Include(pd => pd.ProductsSpecifity)
.Where(z => z.ProductsSpecifity
.Select(x => x.SpecifityID.ToString() + "_" + x.SpecifityValue)
.Count(s => SpecId_SpecValue.Contains(s)) == filterCount);
Please note that the comparison is not efficient. Transforming database values before comparison disables any use of indexes (is not sargable). But doing this more efficiently isn't trivial in EF, see this.

Dynamic query in EF using Expression

I am trying to understand what is the best way to create a dynamic query.
I have a requirement where I will be writing an API to retrieve data from DB. the API has lot of filter paramters. eg. I need to retrieve movies that can be filtered on following properties.
MovieName, Genere, Rating, Language, Category
I can give these parameters in any combination.. so in my Data layer I started framing my dynamic query like this.
IQueryable<Movie> qryContext = null;
if(!string.isnullorEmpty(request.MovieName))
qryContext = context.Movies.Where(x => x.MovieName == request.MovieName)
if(!string.isnullorEmpty(request.Genere))
qryContext = context.Movies.Where(x => x.Genere == request.Genere)
if(!string.isnullorEmpty(request.Language))
qryContext = context.Movies.Where(x => x.Language == request.Language)
if(!string.isnullorEmpty(request.Category))
qryContext = context.Movies.Where(x => x.Category == request.Category)
if(qryContext!= null)
return qryContext.ToList();
else
return null;
Based on the given parameters, the sql query is framed..
But When I search in google reg dynamic queries in EF, most of the links refer to using Expression. Do I need to make use of Expression or can I proeeed with the above method.
Also let me know what advantage I get on using expressions.
Basically what you see inside the Where(...) is expression.
In your example this would be x => x.Language == request.Language
if(!string.isnullorEmpty(request.Language)) qryContext = context.Movies.Where(x => x.Language == request.Language)
Also, I would recommend you to take a look into the Dynamic expression library from EF Plus team. Here https://dynamic-linq.net/basic-simple-query
This allows you to pass expression as a string. And you can construct your filters in the Frontend, and pass them as a string, which helps you to write a much cleaner implementation of filters.

Verify if a user typed a word from a ReactiveList with Reactive Extension

I have a ReactiveList with keywords. The user can add or remove keyword from that list. The app needs to verify if the user typed one of the keywords.
There was already a similar post but it doesn't take in account a flexible list:
Using Reactive Extension for certain KeyPress sequences?
var keyElements = new ReactiveList<KeyElement>();
IObservable<IObservable<int>> rangeToMax = Observable.Merge(keyElements.ItemsAdded, keyElements.ItemsRemoved).Select(obs => Observable.Range(2, keyElements.Select(ke => ke.KeyTrigger.Length).Max()));
IObservable<IObservable<string>> detectedKeyTrigger = rangeToMax
.Select(n => _keyPressed.Buffer(n, 1))
.Merge().Where(m => keyElements.Where(ke => ke.KeyTrigger == m).Any());
//Here I want to end up with IObservable<string> instead of IObservable<IObservable<string>>
I can get rid of the outer IObservable by reassigning the detectedKeyTrigger each time an element in the reactive list changes, but then I lose all my subscriptions.
So, how can I end up with just an Observable of strings?
First off, both Max and Any have overloads which takes a selector and a predicate respectively. This negates the need of the Select.
Next, I changed the Observable.Merge to use the Changed property of ReactiveList which is the Rx version of INotifyCollectionChanged. I also changed the Select to produce an IEnumerable of ints instead; it just felt more Rightâ„¢.
var keyElements = new ReactiveList<KeyElement>();
IObservable<IEnumerable<int>> rangeToMax = keyElements.Changed
.Select(_ => Enumerable.Range(2, keyElements.Max(keyElement => keyElement.KeyTrigger.Length));
IObservable<IObservable<string>> detectedKeyTrigger = rangeToMax.
.Select(range => range
.Select(length => _keyPressed.Buffer(length, 1).Select(chars => new string(chars.ToArray()))) // 1
.Merge() // 2
.Where(m => keyElements.Any(ke => ke.KeyTrigger == m)) // 3
.Switch(); // 4
Create an IObservable<string> which emits the last n characters typed by the user. Create such an observable for each of the possible lengths of an combo
Merge the observables in the IEnumerable<IObservable<string>> into one Observable<string>
Only let strings which mach one of the KeyTriggers through
As rangeToMax.Select produces an IObservable<IObservable<string>> we use Switch to only subscribe to the most recent IObservable<string> the IObservable<IObservable<string>> produces.

Entity Framework: combining exact and wildcard searching conditional on search term

I'm creating a query to search the db using EF. TdsDb being the EF context.
string searchValue = "Widget";
TdsDb tdsDb = new TdsDb();
IQueryable<Counterparty> counterparties;
I can do exact match:
counterparties = tdsDb.Counterparties.Where(x => x.CounterpartyName == searchValue);
or wildcard match:
counterparties = tdsDb.Counterparties.Where(x => x.CounterpartyName.Contains(searchValue));
But I want to be able to do both i.e. (psudo code)
counterparties = tdsDb.Counterparties.Where(x =>
if (searchValue.EndsWith("%"))
{
if (searchValue.StartsWith("%"))
{x.CounterpartyName.Contains(searchValue)}
else
{x.CounterpartyName.StartsWith(searchValue)}
}
else
{x => x.CounterpartyName == searchValue}
);
Now clearly I can't put an if statement in the where clause like that. But I also can't duplicate the queries: shown here they are hugely dumbed down. The production query is far longer, so having multiple versions of the same long query that vary on only one clause seems very unhealthy and unmaintainable.
Any ideas?
You should be able to use the ternary operator:
bool startsWithWildCard = searchValue.StartsWith("%");
bool endsWithWildCard = searchValue.EndsWith("%");
counterparties = tdsDb.Counterparties.Where(x =>
endsWithWildCard
? (startsWithWildCard
? x.CounterpartyName.Contains(searchValue)
: (x.CounterpartyName.StartsWith(searchValue)))
: (x.CounterpartyName == searchValue));
Did you test btw if querying by a searchValue that has an % at the beginning or end works as you expect? It might be possible that % will be escaped as a character to query for because StartsWith and Contains will prepend/append % wildcards to the generated SQL search term anyway. In that case you need to cut off the % from the searchValue before you pass it into StartsWith or Contains.

Command Timeout with EF 4.1 / SQL Server 2008

I see that the question's been asked several times of how to set the command timeout for long-running queries with Entity Framework. The problem I'm running into now is that the query that gets run against the server doesn't really take that long to execute and return.
Here's the code that runs the query:
var records = (from c in _context.Set<CompletedQuiz>()
where c.FarmId == _entityId && c.ToolboxId == _toolboxId
group c by new { c.UserId, c.LessonId } into g
select g).ToList()
.Select(c => new {
UserId = c.Key.UserId,
LessonId = c.Key.LessonId,
NumQuestions = c.Max(n => n.TotalNumQuestions),
NumLessons = c.Select(l => l.LessonId).Distinct().Count(),
Start = c.Min(s => s.LogonDateTime),
End = c.Max(e => e.LogoffDateTime),
MaxScore = c.Max(s => s.Score),
Passed = c.Any(p => p.Passed)
});
I'm selecting from a fairly simple view called CompletedQuizzes, and grouping on the record ids for users and lessons. I've run this with SQL Profiler running to capture the actual query that's executed; if I run that exact same query in SSMS, it runs almost instantly (<0 seconds). However, running from my application will often exceed the default command timeout of 30 seconds. I put a breakpoint on the line that's shown above, and I added the call to .ToList() to make sure that the query is executed immediately.
What else should I be checking as a possible culprit here?
EDIT:
I still don't understand why the code above takes so long to execute, but I reworked it using using Linq extension methods, and now it runs as fast as I would expect. Here's what it looks like now:
var records = _context.Set<CompletedQuiz>()
.Where(c => c.FarmId == _entityId && c.ToolboxId == _toolboxId)
.GroupBy(c => new { c.UserId, c.LessonId })
.Select(c => new {
UserId = c.Key.UserId,
LessonId = c.Key.LessonId,
NumQuestions = c.Max(n => n.TotalNumQuestions),
NumLessons = c.Select(l => l.LessonId).Distinct().Count(),
Start = c.Min(s => s.LogonDateTime),
End = c.Max(e => e.LogoffDateTime),
MaxScore = c.Max(s => s.Score),
Passed = c.Any(p => p.Passed)
});
I guess at this point I would adjust my question to why is the query generated by the second block of code executed so much more quickly from my application?
I think it is qutie obvious your first query is using .ToList() befor projection. It means that you load all CompleteQuiz instances satisfying your condition to your application and execute all aggregations in your application. It also means that there must be a lot of subsequent queries to lazy load your relations for aggregation computations.
In your second query there is no ToList and thus whole query including all aggregations is performed in the database.