Generated SQL with PredicateBuilder, LINQPad and operator ANY - dump

I previously asked a question about chaining conditions in Linq To Entities.
Now I use LinqKit and everything works fine.
I want to see the generated SQL and after reading this answer, I use LinqPad.
This is my statement:
var predProduct = PredicateBuilder.True<Product>();
var predColorLanguage = PredicateBuilder.True<ColorLanguage>();
predProduct = predProduct.And(p => p.IsComplete);
predColorLanguage = predColorLanguage.And(c => c.IdColorEntity.Products.AsQueryable().Any(expr));
ColorLanguages.Where(predColorLanguage).Dump();
The code works in VS2008, compile and produce the correct result set, but in LinqPad, I've the following error:
NotSupportedException: The overload query operator 'Any' used is not Supported.
How can I see the generated SQL if LINQPad fails?
EDIT
If I write
var predColorLanguage = PredicateBuilder.True<ColorLanguage>();
predColorLanguage = predColorLanguage.And(c => c.IdColorEntity.Products.Any((p => p.IsComplete));
ColorLanguages.Where(predColorLanguage).Dump();
works... WTF?

As you're using LINQKit, you can make this work by calling Compile() on the expression that feeds the EntitySet, and then calling AsExpandable() on the main query:
var predProduct = PredicateBuilder.True<Product>();
var predColorLanguage = PredicateBuilder.True<ColorLanguage>();
predProduct = predProduct.And(p => p.IsComplete);
predColorLanguage = predColorLanguage.And (
c => c.IdColorEntity.Products.Any(predProduct.Compile()));
ColorLanguages.AsExpandable().Where(predColorLanguage).Dump();
As explained in the LINQKit article, the Compile method never actually runs: AsExpandable strips it out and modifies the expression tree so that it works with LINQ to SQL.

Related

EF Core 3 GroupBy multiple columns Count Throws with extensions but linq works

Here is the one that throws full exception:
var duplicateCountOriginal = _db.TableName
.GroupBy(g => new {g.ColumnA, g.ColumnB, g.ColumnC})
.Count(g => g.Count() > 1);
Exception:
System.ArgumentException: Expression of type 'System.Func2[System.Linq.IGrouping2[Microsoft.EntityFrameworkCore.Storage.ValueBuffer,Microsoft.EntityFrameworkCore.Storage.ValueBuffer],Microsoft.EntityFrameworkCore.Storage.ValueBuffer]' cannot be used for parameter of type 'System.Func2[Microsoft.EntityFrameworkCore.Storage.ValueBuffer,Microsoft.EntityFrameworkCore.Storage.ValueBuffer]' of method 'System.Collections.Generic.IEnumerable1[Microsoft.EntityFrameworkCore.Storage.ValueBuffer] Select[ValueBuffer,ValueBuffer](System.Collections.Generic.IEnumerable1[Microsoft.EntityFrameworkCore.Storage.ValueBuffer], System.Func2[Microsoft.EntityFrameworkCore.Storage.ValueBuffer,Microsoft.EntityFrameworkCore.Storage.ValueBuffer])' (Parameter 'arg1')
But the same thing works when it is written as linq (I prefer extensions)
var duplicateCount =
from a in _db.TableName
group a by new {a.ColumnA, a.ColumnB, a.ColumnC}
into g
where g.Count() > 1
select g.Key;
duplicateCount.Count()
I am unable to understand why one works or the other doesn't.
Also if I change the first one a little bit based on EF Core 3 changes like the following
var duplicateCountOriginal = _db.TableName
.GroupBy(g => new {g.ColumnA, g.ColumnB, g.ColumnC})
.AsEnumerable()
.Count(g => g.Count() > 1);
I get the following exception:
System.InvalidOperationException: Client projection contains reference to constant expression of 'Microsoft.EntityFrameworkCore.Metadata.IPropertyBase' which is being passed as argument to method 'TryReadValue'. This could potentially cause memory leak. Consider assigning this constant to local variable and using the variable in the query instead. See https://go.microsoft.com/fwlink/?linkid=2103067 for more information.
According to me, the link given by ms has no meaning to the whatever problem here is.
Please LMK if there is any logical explanation.
There is no logical explanation. Just EF Core query translation is still far from perfect and have many defects/bugs/unhandled cases.
In this particular the problem is not the query syntax or method syntax (what you call extensions), but the lack of Select after GroupBy. If you rewrite the method syntax query similar to the one using query syntax, i.e. add .Where, .Select and then Count:
var duplicateCount = _db.TableName
.GroupBy(g => new {g.ColumnA, g.ColumnB, g.ColumnC})
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.Count();
then it will be translated and executed successfully.

LINQ to Entities does not recognize the method - Include + Where

I have this working Code
var h = db.MyTable.Include("Children").Include("Parent").ToList();
but when I add where condition
var h = db.MyTable.Include("Children").Include("Parent").Where(x => x.Country==Session["country"].ToString()).ToList();
it throws me an error
LINQ to Entities does not recognize the method 'System.Object get_item (System.String)' method , and this method cannot be translated into a store expression.
How can i rewrite it ? Im beginner :-)
Thank you
This is caused by your Where-Expression. You should only use variables in there or call methods that can be translated to SQL.
First, save your Session value in a variable:
var country = Session["country"].ToString();
Then use country in your Where-Expression.
MSDN provides a list of methods you can use inside LINQ to SQL expressions and how they are mapped to SQL functions.
try this:
var t = Session["country"].ToString();
var h = db.MyTable.Include("Children").Include("Parent").Where(x => x.Country==t).ToList();
only activities can be parsed into expression-tree that is supported by your provider

LINQ to Entities Contains Query

I'm trying to use Contains() in a simple query but it is failing, with the error :
Unable to create a constant value of type 'NewsletterApp.Models.NewsletterUser'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
Edit The intellisense actually directs me to use the NewsletterUser in Contains() -'(NewsletterUser item)'
I read that there were issues searching for an object using Contains() using EF with .NET 3.5, but I am using EF4.2 (also tried 4.1) with .NET 4.0 .
Code is below:
var db = new MyContext();
var newsletterUser = db.NewsletterUsers.Find(UserID);
var subscriberList = db.Subscribers
.Where(x => x.NewsletterList.ListOwner.NewsletterUsers.Contains(newsletterUser))
.ToList();
I suspect you want this
var db = new MyContext();
var newsletterUser = db.NewsletterUsers.Find(UserID);
var subscriberList = db.Subscribers
.Where(x => x.NewsletterList.ListOwner.NewsletterUsers
.Any(y => y.UserId == newsletterUser.UserId))
.ToList();
Any() checks for the existence of a item that fulfils the criteria specified in the lambda: "y => y.UserId == newsletterUser.UserId".
The exception you were getting: "Only primitive types ('such as Int32, String, and Guid') are supported in this context" is due to limitations set by LINQ to Entities. LINQ to Entities needs to resolve your query in a way that it can express to the database, and it can't do that with the Contains() method with anything other than a primitive type.
The thing is, the code you posted does run fine if you run it against an in memory collection (LINQ to Objects) - that's why it isn't flagged by the compiler.
Your query is wrong. Instead of comparing a property or field, you are comparing the whole object or entity, which can't be done in this manner.
Try the following code and it will work
var db = new MyContext();
var newsletterUser = db.NewsletterUsers.Find(UserID);
var subscriberList = db.Subscribers
.Where(x => x.NewsletterList.ListOwner.NewsletterUsers.UserId.Contains(newsletterUser.UserID))
.ToList();

EF4.1 Code First: Stored Procedure with output parameter

I use Entity Framework 4.1 Code First. I want to call a stored procedure that has an output parameter and retrieve the value of that output parameter in addition to the strongly typed result set. Its a search function with a signature like this
public IEnumerable<MyType> Search(int maxRows, out int totalRows, string searchTerm) { ... }
I found lots of hints to "Function Imports" but that is not compatible with Code First.
I can call stored procedures using Database.SqlQuery(...) but that does not work with output parameters.
Can I solve that problem using EF4.1 Code First at all?
SqlQuery works with output parameters but you must correctly define SQL query and setup SqlParameters. Try something like:
var outParam = new SqlParameter();
outParam.ParameterName = "TotalRows";
outParam.SqlDbType = SqlDbType.Int;
outParam.ParameterDirection = ParameterDirection.Output;
var data = dbContext.Database.SqlQuery<MyType>("sp_search #SearchTerm, #MaxRows, #TotalRows OUT",
new SqlParameter("SearchTerm", searchTerm),
new SqlParameter("MaxRows", maxRows),
outParam);
var result = data.ToList();
totalRows = (int)outParam.Value;

How call a custom method in lambda expression . I Use Entity Framework 4 . Stored expression error

Is it possible to call a custom method in a lambda expression.?
//Address a : It's an Entity
public string AddressConstructor(Address a)
{
return a.City + "," + a.Province;
}
var Test = _db.MyTableTest.Select( t => new ViewModel
{
MyID = t.ID,
StringAddress = AddressConstructor(t.Addr)
};
You should be able to accomplish this using LINQKit to inline your expression into the expression tree.
http://www.albahari.com/nutshell/linqkit.aspx
This will cause the concatanation you're attempting to be run on the SQL Server, not in memory (as described in the other answer). SQL Server of course knows how to concatanate strings, but if your AddressConstructor did something else that SQL Server doesn't understand, then this approach would not work and you would indeed need to perform your custom method in memory using the approach described in the other answer.
Essentially LINQKit will flatten the tree so it actually gets executed as:
var Test = _db.MyTableTest.Select( t => new ViewModel
{
MyID = t.ID,
StringAddress = t.Addr.City + "," + t.Addr.Province
};
which EF should have no problem executing (and the concatenation should happen on the SQL Server).
You need to call AsEnumerable so that the projection is executed locally:
var Test = _db.MyTableTest.AsEnumerable()
.Select( t => new ViewModel
{
MyID = t.ID,
StringAddress = AddressConstructor(t.Addr)
};
Otherwise, the Queryable.Select method is used instead of Enumerable.Select, which causes Entity Framework to try to translate the lambda expression to a SQL query (which of course is not possible in that case)