I have a simple query that is generating some odd SQL translations, which is blowing up my code when the object is saturated.
from x in DataContext.MyEntities
select new
{
IsTypeCDA = x.EntityType == "CDA"
}
I would expect this query should translate to:
SELECT (CASE WHEN [t0].[EntityType] = #p1 THEN 1 ELSE 0 END) as [IsTypeCDA]
...
Instead I get this :
SELECT
(CASE
WHEN #p1 = [t0].[EntityType] THEN 1
WHEN NOT (#p1 = [t0].[EntityType]) THEN 0
ELSE NULL
END) AS [IsTypeCDA]
...
Since I'm saturating a POCO where IsTypeCDA is a bool, it blows up stating I can't assign null to bool.
Any thoughts?
Edit: fixed the property names so they make sense...
from x in DataContext.MyEntities
select new
{
IsTypeCDA = x.EntityType == null
}
c# interpretation (false) or sql interpretation (null)?
This runs in sql so sql interpretation. That's why the funky translation - the operation does return a nullable bool.
Use this query to punt that nullable bool into a plain old bool.
from x in DataContext.MyEntities
select new
{
IsTypeCDA = ((bool?)(x.EntityType == "CDA")) ?? false
}
Linq to SQL does the "strange" comparison because database values can be NULL. Unfortunately, it doesn't seem to do well translating the tri-valued comparison result. At first blush, I'd wonder why any of your entities have NULL for their EntityType. Modifying your schema to disallow NULL values would be the most straightforward solution.
However, assuming the table is defined this way for legitimate business reasons you can work around it in a couple ways.
First off, you could coalesce the NULLs into a placeholder value.
from x in DataContext.MyEntities
select new
{
IsTypeCDA = (x.EntityType ?? "") == "CDA"
}
Alternately, using String.Equals generates a more thorough comparison that handles NULLs.
from x in DataContext.MyEntities
select new
{
IsTypeCDA = string.Equals(x.EntityType, "CDA")
}
Neither of these will generate the simple SQL you were expecting, but they'll both get the job done.
I would have IsTypeCDA as a get only property and select into that class:
public class SomeName
{
public string EntityType { get; set; }
public bool IsTypeCDA { get { return EntityType == EntityType.CDA; } }
}
...
from x in DataContext.MyEntities
select new SomeName
{
EntityType = x.EntityType
}
Related
I have a piece of code that's performing badly, and need to rewite it to introduce a proper where clause before starting the .ToList however, that's where I'm getting stuck.
Currently the code looks lke this (roughly, I've taken some of the search criteria out to make it easier to display)
var Widgets = from b in _caspEntities.Widgets.Include("WidgetRegionLogs")
.Include("WidgetStatusLogs").Include("WidgetVoltageTests")
select b;
IEnumerable<Widget> results = Widgets.ToList();
if (comboBoxRegion.SelectedValue.ToString() != "0")
{
results = from b in results
where b.CurrentRegionLog != null && b.CurrentRegionLog.RegionId == int.Parse(comboBoxRegion.SelectedValue.ToString())
select b;
}
if (comboBoxStatus.SelectedValue != null)
{
results = from b in results
where b.CurrentStatusLog != null && b.CurrentStatusLog.StatusId == comboBoxStatus.SelectedValue.ToString()
select b;
}
if (txtCode.Text.Trim().Length > 0)
{
results = from b in results
where b.CodeNumber == txtCode.Text.Trim()
select b;
}
dataGridViewWidget.DataSource = results.ToList();
I can write the SQL easily enough, essentially the model is simple, I have a Widget it has a RegionLog and a StatusLog, both of which store a history. The current region and status are retrieved from this by grouping by WidgetID and selecting the most recent Date Updated (and then going off to Region and Status tables to get the actual value).
So, I need to translate this into LINQ, but to be honest I don't have a clue but am ken and willing to learn. In my head, I think I need to add some better where clauses, and then do the Widget.toList after I have applied the where clauses. I'm struggling with the CurrentRegionLog and CurrentStatusLog concepts as they are not populated until I run the IEnumerable.
If anyone can give some pointers, I'd be grateful,
Thanks
Edit - Added
public BatteryRegionLog CurrentRegionLog
{
get { return _currentRegionLog; }
}
private BatteryRegionLog _currentRegionLog
{
get
{
if (this.BatteryRegionLogs.Count > 0)
{
BatteryRegionLog log = this.BatteryRegionLogs.OrderByDescending(item => item.LastModifiedDate).First();
return log;
}
else
{
return null;
}
}
}
You can compose the query like this:
if (comboBoxRegion.SelectedValue.ToString() != "0")
{
var id = int.Parse(comboBoxRegion.SelectedValue.ToString()
Widgets = from b in Widgets
let currentRegionLog =
b.BatteryRegionLogs
.OrderByDescending(item => item.LastModifiedDate)
.FirstOrDefault()
where currentRegionLog.RegionId == id)
select b;
}
... // Same for the other criteria.
dataGridViewWidget.DataSource = Widgets.ToList();
The whole query is not executed before you do ToList(). As everything is translated to SQL you don't need the null check b.CurrentRegionLog != null. SQL will evaluate b.CurrentRegionLog.RegionId == id just fine when there is no CurrentRegionLog.
Edit
Since CurrentRegionLog is a calculated property of your Widget class it cannot be translated into SQL. I made an effort to incorporate the code of calculated property into the query in a way that only the basic navigation property is used, so EF can translate it to SQL again.
try remove this line:
IEnumerable<Widget> results = Widgets.ToList();
and just use the Widgets variable you get in at the top
The .ToList() goes to the database and materialiaze all the data into entities.
if you don't call the .ToList() the query is still "open" for a where clause
I am querying a tinyint column and entity-framework generates a SELECT query that introduces a CAST to INT for this column even when the value that I am using in the WHERE clause is of the type byte.
Looking the Model, the generated Type for my tinyint column is byte.
Looking the code:
byte byteValue = 6;
var entityList = from r in rep.DataContext.FooTable
where r.TinyintColumn == byteValue
select r;
Looking the generated query:
SELECT [Extent1].[TinyintColumn] AS [TinyintColumn] WHERE #p__linq__0 = CAST( [Extent1].[TinyintColumn] AS int)
I have strict restrictions in the performance so I don't want those CAST in any select.
So my question whould be, is there any way to avoid this CAST over the column tinyint? or am I doing something wrong?
Thanks in advance.
If you use IList<T>.Contains with a List<byte> the Entity Framework won't cast.
List<byte> byteValue = new List<byte> { 6 };
var entityList = from r in rep.DataContext.FooTable
where byteValue.Contains(r.TinyintColumn)
select r;
I ran into the same problem and blogged about it.
My colleague found very nice trick to overcome this issue on Entity Framework 4.0.
Works for smallint, I didn't try on tinyint.
Insteal of equals (==) - use Contains() operator which was implemented with EF 4.0.
For example:
say you have the column SmallIntColumn.
instead of:
short shortValue = 6;
var entityList = from r in rep.DataContext.FooTable
where r.SmallIntColumn == shortValue
select r;
use
short[] shortValue = new short[] { 6 };
var entityList = from r in rep.DataContext.FooTable
where shortValue.Contains(r.SmallIntColumn)
select r;
Check the SQL generated - it is now without the CAST!
And from my tests - the execution plan used my (filtered) index on the column just perfectly.
Hope it helped.
Shlomi
The contains solution may not be optimized by the DB if the smallint comparison is one segment of the filtering on multiple columns and there is an index matching the those columns. I verified that using the Equals method fixed this problem with the SmallInt type, at least on EF6.
Instead of
short shortValue = 6;
var entityList = from r in rep.DataContext.FooTable
where r.SmallIntColumn == shortValue
select r;
use
short shortValue = 6;
var entityList = from r in rep.DataContext.FooTable
where r.SmallIntColumn.Equals(shortValue)
select r;
The CAST will affect performance because indexes won't be used on TinyintColumn
This is combination of points 2 and 4 in "Ten Common SQL Programming Mistakes". CAST is a function on a column, and without it you'll have a datatype mismatch anyway
#p__linq__0 should be tinyint or explicitly CAST.
However, it could be LINQ doesn't like tinyint primary keys according to MS Connect and (SO) asp.net mvc linq sql problem
You could "byte" the bullet (sorry) and use smallint...
I'm posting the solution I've taken for this problem.
It seems that EntityFramework 4.0 always generates queries with CAST in tinyint or smallint fields. So for performance optimization, I have decided to change to INT those fields to avoid the CAST and I have changed the size of other nvarchar fields that I still could decrease from nvarchar(50) to nvarchar(30). So at the end I have changed the size of the row from 143 Bytes to 135 Bytes.
If you have an Sql table column data type of tinyint , the corresponding POCO objects should have a property of type byte. This will work for you. Else you when you are iterating through the LINQ object , it will throw an error stating that unable to convert byte type to say int or whatever as you may have defined for the property.
I just verified with EF 4.3 Code First Approach , Everything went well.
I ran into the exact same problem while using EF with a lambda expression. Beefing up the datatype to an int is not a solution and even bad practice. What i found and as other reported here is that you do get correct code when you take a more clumsy aproach, like:
SomeEntity.FindBy( i => new List { 1 }.Contains( i.TinyintColumn ) )
But when then you run into other issues with more then one value to match against. The following will not use parameterised query values, and but just inline them into the query body!
SomeEntity.FindBy( i => new List { 1, 2 }.Contains( i.TinyintColumn ) )
That is not as bad at the original problem, but still not good as it means the database has to complile a plan for every combination of values that you throw at it and makes performance analysis next to impossible as there is no proper aggregation of execution times. It also has some performance effects you rather not see in high load enviroments!
Do not get me started on what these kind of behaviors/anti-patterns would do to char/nchar datatypes and their effect on indexes. As i see it, centralizing everything around the datatype system C# implements is both limited and causes major issues.
My view on EF is that very basic queries on well modelled tables are transformed to bad SQL code and EF follows anti-patterns. It is not something I find impressive in the light of the hype and the added complexity in development EF brings! I wont go into that here right now, as that would be a whole different discussion!
Pick any of the above solutions, but know the drawbacks before using them. Maybe version 10 of EF will solve the problem to a degree, i don't hold my breath however.
If you like to preserve the logic you can use expression rewrite method.
The code will be like
db.MyEntities.Where(e => e.Id == i).FixIntCast()
and you keep the application logic as is.
Try more complex version of IntCastFixExtension :
namespace System.Linq {
/// <summary>
/// author: Filip Sielimowicz inspired by
/// http://www.entityframework.info/Home/SmallIntProblem
/// </summary>
public static class IntCastFixExtension {
public static IQueryable<T> FixIntCast<T>(this IQueryable<T> q, bool narrowMemberExpr = true, bool narrowConstantExpr = true) {
var visitor = new FixIntCastVisitor() {
narrowConstExpr = narrowConstantExpr,
narrowMembExpr = narrowMemberExpr
};
Expression original = q.Expression;
var expr = visitor.Visit(original);
return q.Provider.CreateQuery<T>(expr);
}
private class FixIntCastVisitor : ExpressionVisitor {
public bool narrowConstExpr;
public bool narrowMembExpr;
protected override Expression VisitBinary(BinaryExpression node) {
bool eq = node.NodeType == ExpressionType.Equal;
bool neq = node.NodeType == ExpressionType.NotEqual;
if (eq || neq) {
var leftUncasted = ReducePossiblyNotNecessaryIntCastExpr(node.Left);
var rightUncasted = ReducePossiblyNotNecessaryIntCastExpr(node.Right);
var rightConst = node.Right as ConstantExpression;
if (leftUncasted == null) {
return base.VisitBinary(node);
}
if (rightUncasted != null) {
if (NarrowTypesAreCompatible(leftUncasted.Type, rightUncasted.Type)) {
// Usuwamy niepotrzebne casty do intów występujące po obu stronach equalsa
return eq ? Expression.Equal(leftUncasted, rightUncasted) : Expression.NotEqual(leftUncasted, rightUncasted);
}
} else if (rightConst != null) {
// Zamiast casta argumentu z lewej w górę do inta (tak zrobił linq2entity)
// zawężamy występującą po prawej stałą typu 'int' do typu argumentu z lewej
if (narrowConstExpr && (rightConst.Type == typeof(int) || rightConst.Type == typeof(int?))) {
var value = rightConst.Value;
var narrowedValue = value == null ? null : Convert.ChangeType(rightConst.Value, leftUncasted.Type);
Expression narrowedConstExpr = Expression.Constant(narrowedValue, leftUncasted.Type);
return eq ? Expression.Equal(leftUncasted, narrowedConstExpr) : Expression.NotEqual(leftUncasted, narrowedConstExpr);
}
} else if (node.Right.NodeType == ExpressionType.MemberAccess) {
// Jak po prawej mamy wyrażenie odwołujące się do zmiennej typu int to robimy podobnie jak przy stałej
// - zawężamy to, zamiast upcasta do inta z lewej.
if (narrowMembExpr) {
var rightMember = node.Right;
var narrowedMemberExpr = Expression.Convert(rightMember, leftUncasted.Type);
return eq ? Expression.Equal(leftUncasted, narrowedMemberExpr) : Expression.NotEqual(leftUncasted, narrowedMemberExpr);
}
}
}
return base.VisitBinary(node);
}
private bool NarrowTypesAreCompatible(Type t1, Type t2) {
if (t1 == typeof(short?)) t1 = typeof(short);
if (t2 == typeof(short?)) t2 = typeof(short);
if (t1 == typeof(byte?)) t1 = typeof(byte);
if (t2 == typeof(byte?)) t2 = typeof(byte);
return t1 == t2;
}
private bool IsNullable(Type t) {
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}
private Expression CorrectNullabilityToNewExpression(Expression originalExpr, Expression newExpr) {
if (IsNullable(originalExpr.Type) == IsNullable(newExpr.Type)) {
return newExpr;
} else {
if (IsNullable(originalExpr.Type)) {
Type nullableUncastedType = typeof(Nullable<>).MakeGenericType(newExpr.Type);
return Expression.Convert(newExpr, nullableUncastedType);
} else {
Type notNullableUncastedType = Nullable.GetUnderlyingType(newExpr.Type);
return Expression.Convert(newExpr, notNullableUncastedType);
}
}
}
private Expression ReducePossiblyNotNecessaryIntCastExpr(Expression expr) {
var unnecessaryCast = expr as UnaryExpression;
if (unnecessaryCast == null ||
unnecessaryCast.NodeType != ExpressionType.Convert ||
!(unnecessaryCast.Type == typeof(int) || unnecessaryCast.Type == typeof(int?))
) {
// To nie jest cast na inta, do widzenia
return null;
}
if (
(unnecessaryCast.Operand.Type == typeof(short) || unnecessaryCast.Operand.Type == typeof(byte)
|| unnecessaryCast.Operand.Type == typeof(short?) || unnecessaryCast.Operand.Type == typeof(byte?))
) {
// Jest cast z shorta na inta
return CorrectNullabilityToNewExpression(unnecessaryCast, unnecessaryCast.Operand);
} else {
var innerUnnecessaryCast = unnecessaryCast.Operand as UnaryExpression;
if (innerUnnecessaryCast == null ||
innerUnnecessaryCast.NodeType != ExpressionType.Convert ||
!(innerUnnecessaryCast.Type == typeof(int) || innerUnnecessaryCast.Type == typeof(int?))
) {
// To nie jest podwójny cast między intami (np. int na int?), do widzenia
return null;
}
if (
(innerUnnecessaryCast.Operand.Type == typeof(short) || innerUnnecessaryCast.Operand.Type == typeof(byte)
|| innerUnnecessaryCast.Operand.Type == typeof(short?) || innerUnnecessaryCast.Operand.Type == typeof(byte?))
) {
// Mamy podwójny cast, gdzie w samym środku siedzi short
// Robimy skrócenie, żeby intów nie produkował zamiast short -> int -> int?
// powinno ostatecznie wychodzić short -> short czyli brak castowania w ogóle.
return CorrectNullabilityToNewExpression(unnecessaryCast, innerUnnecessaryCast.Operand);
}
}
return null;
}
}
}
}
The db column is probably nullable.
Try this: r.TinyintColumn.Value == byteValue
I have the below LINQ Method I am trying to create. The issue seems to be the Second WHERE clause. I am getting this error -->
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<MatrixReloaded.Data.CMO.tblWorkerHistory>' to 'bool'
I also had && there vs WHERE but I was getting a similar error. I don't NEED anything from tblWorkerHistories except the EndDate stuff.
There is a Many To Many relationship between the 2 tables with EnrollmentID being a FK on both.
public static DataTable GetCurrentWorkersByEnrollmentID(int enrollmentID)
{
using (var context = CmoDataContext.Create())
{
context.Log = Console.Out;
var currentWorkers = from enrollment in context.tblCMOEnrollments
where enrollment.EnrollmentID == enrollmentID
where enrollment.tblWorkerHistories.Where(a => a.EndDate == null || a.EndDate > DateTime.Now)
select
new
{
enrollment.CMONurseID,
enrollment.CMOSocialWorkerID,
SupportWorkerName = enrollment.tblSupportWorker.FirstName + " " + enrollment.tblSupportWorker.LastName,
SupportWorkerPhone = enrollment.tblSupportWorker.Phone
};
return currentWorkers.CopyLinqToDataTable();
}
}
This is the problem:
where enrollment.tblWorkerHistories.Where(/* stuff */)
Where returns a sequence... whereas you need something that will return a Boolean value. What are you trying to do with that embedded Where clause?
As Marc says, it could be that you just need an Any call instead of Where... but if you could explain what you're trying to do, that would make it a lot easier to help you. Note that Any does return a Boolean value, instead of a sequence.
EDIT: Okay, so in SQL you'd use a join, but you don't need an explicit join here because LINQ is implicitly doing that for you, right? If you're trying to find enrollments where any of the histories match the date, and you don't care about the histories themselves, then Any is indeed what you want:
var currentWorkers = from enrollment in context.tblCMOEnrollments
where enrollment.EnrollmentID == enrollmentID
where enrollment.tblWorkerHistories.Any
(a => a.EndDate == null || a.EndDate > DateTime.Now)
select ...
I suspect you mean .Any instead of .Where in the sub-query; the outermost .Where (i.e. the second where) expects a predicate expression, but yours is currently a selector - try:
where enrollment.tblWorkerHistories.Any(
a => a.EndDate == null || a.EndDate > DateTime.Now)
The LINQ-to-SQL query in Visual Studio generates an SQL query with errors. In LINQPad, the same LINQ query using the same database (or DataContext) runs just fine.
LINQ Query
var accesDomaines = from t in db.Access
where t.IdUser == access.IdUtilisateur
where t.IdDomain != null
where t.IdRole == access.IdRole
where t.IdPlace == access.IdPlace
select t;
Here's a small part of generated SQL where the error occurs:
WHERE (...) AND ([t3].[IdRole] = ) AND (...)
After the equals in where clause, there's literally nothing ! In the SQL query of LINQPad we see the good where clause:
WHERE (...) AND ([t3].[IdRole] IS NULL) AND (...)
When I compare the two generated SQL queries from VS and LINQPad, line by line, this is the same thing. Except LINQPad is using params and also the missing right part of equal in where clause of Visual Studio, as shown before.
Note 1
In the LINQ query, I tried with this syntax in where clauses:
where t.IdRole.Equals(acces.IdRole.Value)
But also generates a bad result. I even tried something like this before the LINQ query:
if (!acces.IdRole.HasValue) { acces.IdRole = null; }
Note 2
Properties are nullable integers. I do want null in query if property is null. Obviously, I want the value of property if there's a value.
Note 3
I have tried the proposition made in this question: Linq where column == (null reference) not the same as column == null
...with no success.
Any explanation of two similar LINQ queries, but generating a good and a bad SQL query? Any suggestion to solve this problem?
Thank you!
try this:
where object.Equals(t.IdRole, access.IdRole)
Use
object.Equals()
.Net would take care of generating correct sql for null condition.
Example:
Say you have a street table with columns like Suffix and Prefix. Then following linq query doesn't work:
string suffix = "ST";
string prefix = null;
var map = from s in Streets
where s.Suffix==suffix || s.Prefix==prefix
select s;
It would generate following sql:
SELECT [t0].[StreetId], [t0].[Prefix], [t0].[Suffix]
FROM [Street] AS [t0]
WHERE ([t0].[Suffix] = #p0) AND ([t0].[Prefix] = #p1)
we can clearly see it will not return any result.
Using object.Equals():
string suffix = "ST";
string prefix = null;
var map = from s in Streets
where object.Equals(s.Suffix, suffix) && object.Equals(s.Prefix,prefix)
select s;
would generate sql:
SELECT [t0].[StreetId], [t0].[Prefix], [t0].[Suffix]
FROM [Street] AS [t0]
WHERE ([t0].[Suffix] IS NOT NULL) AND ([t0].[Suffix] = #p0)
AND ([t0].[Prefix] IS NULL)
Which is correct one.
(Little late, but wanted to expand the answer for the benefit of others)
Have you tried verifying whether your properties had values with the HasValues property provided by the Nullables?
where t.IdRole == access.IdRole.HasValues ? access.IdRole.Value : null
Perhaps this could work. I haven't used LINQ-to-SQL really.
This example worked for me:
var aff3 = from a in context.Affiliates
where ((name == null && a.CompanyName == null) || (a.CompanyName == name))
select a.ID;
What is the best way to assemble a dynamic WHERE clause to a LINQ statement?
I have several dozen checkboxes on a form and am passing them back as: Dictionary<string, List<string>> (Dictionary<fieldName,List<values>>) to my LINQ query.
public IOrderedQueryable<ProductDetail> GetProductList(string productGroupName, string productTypeName, Dictionary<string,List<string>> filterDictionary)
{
var q = from c in db.ProductDetail
where c.ProductGroupName == productGroupName && c.ProductTypeName == productTypeName
// insert dynamic filter here
orderby c.ProductTypeName
select c;
return q;
}
(source: scottgu.com)
You need something like this? Use the Linq Dynamic Query Library (download includes examples).
Check out ScottGu's blog for more examples.
I have similar scenario where I need to add filters based on the user input and I chain the where clause.
Here is the sample code.
var votes = db.Votes.Where(r => r.SurveyID == surveyId);
if (fromDate != null)
{
votes = votes.Where(r => r.VoteDate.Value >= fromDate);
}
if (toDate != null)
{
votes = votes.Where(r => r.VoteDate.Value <= toDate);
}
votes = votes.Take(LimitRows).OrderByDescending(r => r.VoteDate);
You can also use the PredicateBuilder from LinqKit to chain multiple typesafe lambda expressions using Or or And.
http://www.albahari.com/nutshell/predicatebuilder.aspx
A simple Approach can be if your Columns are of Simple Type like String
public static IEnumerable<MyObject> WhereQuery(IEnumerable<MyObject> source, string columnName, string propertyValue)
{
return source.Where(m => { return m.GetType().GetProperty(columnName).GetValue(m, null).ToString().StartsWith(propertyValue); });
}
It seems much simpler and simpler to use the ternary operator to decide dynamically if a condition is included
List productList = new List();
productList =
db.ProductDetail.Where(p => p.ProductDetailID > 0 //Example prop
&& (String.IsNullOrEmpty(iproductGroupName) ? (true):(p.iproductGroupName.Equals(iproductGroupName)) ) //use ternary operator to make the condition dynamic
&& (ID == 0 ? (true) : (p.ID == IDParam))
).ToList();
I came up with a solution that even I can understand... by using the 'Contains' method you can chain as many WHERE's as you like. If the WHERE is an empty string, it's ignored (or evaluated as a select all). Here is my example of joining 2 tables in LINQ, applying multiple where clauses and populating a model class to be returned to the view. (this is a select all).
public ActionResult Index()
{
string AssetGroupCode = "";
string StatusCode = "";
string SearchString = "";
var mdl = from a in _db.Assets
join t in _db.Tags on a.ASSETID equals t.ASSETID
where a.ASSETGROUPCODE.Contains(AssetGroupCode)
&& a.STATUSCODE.Contains(StatusCode)
&& (
a.PO.Contains(SearchString)
|| a.MODEL.Contains(SearchString)
|| a.USERNAME.Contains(SearchString)
|| a.LOCATION.Contains(SearchString)
|| t.TAGNUMBER.Contains(SearchString)
|| t.SERIALNUMBER.Contains(SearchString)
)
select new AssetListView
{
AssetId = a.ASSETID,
TagId = t.TAGID,
PO = a.PO,
Model = a.MODEL,
UserName = a.USERNAME,
Location = a.LOCATION,
Tag = t.TAGNUMBER,
SerialNum = t.SERIALNUMBER
};
return View(mdl);
}
Just to share my idea for this case.
Another approach by solution is:
public IOrderedQueryable GetProductList(string productGroupName, string productTypeName, Dictionary> filterDictionary)
{
return db.ProductDetail
.where
(
p =>
(
(String.IsNullOrEmpty(productGroupName) || c.ProductGroupName.Contains(productGroupName))
&& (String.IsNullOrEmpty(productTypeName) || c.ProductTypeName.Contains(productTypeName))
// Apply similar logic to filterDictionary parameter here !!!
)
);
}
This approach is very flexible and allow with any parameter to be nullable.
You could use the Any() extension method. The following seems to work for me.
XStreamingElement root = new XStreamingElement("Results",
from el in StreamProductItem(file)
where fieldsToSearch.Any(s => el.Element(s) != null && el.Element(s).Value.Contains(searchTerm))
select fieldsToReturn.Select(r => (r == "product") ? el : el.Element(r))
);
Console.WriteLine(root.ToString());
Where 'fieldsToSearch' and 'fieldsToReturn' are both List objects.
This is the solution I came up with if anyone is interested.
https://kellyschronicles.wordpress.com/2017/12/16/dynamic-predicate-for-a-linq-query/
First we identify the single element type we need to use ( Of TRow As DataRow) and then identify the “source” we are using and tie the identifier to that source ((source As TypedTableBase(Of TRow)). Then we must specify the predicate, or the WHERE clause that is going to be passed (predicate As Func(Of TRow, Boolean)) which will either be returned as true or false. Then we identify how we want the returned information ordered (OrderByField As String). Our function will then return a EnumerableRowCollection(Of TRow), our collection of datarows that have met the conditions of our predicate(EnumerableRowCollection(Of TRow)). This is a basic example. Of course you must make sure your order field doesn’t contain nulls, or have handled that situation properly and make sure your column names (if you are using a strongly typed datasource never mind this, it will rename the columns for you) are standard.
System.Linq.Dynamic might help you build LINQ expressions at runtime.
The dynamic query library relies on a simple expression language for formulating expressions and queries in strings.
It provides you with string-based extension methods that you can pass any string expression into instead of using language operators or type-safe lambda extension methods.
It is simple and easy to use and is particularly useful in scenarios where queries are entirely dynamic, and you want to provide an end-user UI to help build them.
Source: Overview in Dynamic LINQ
The library lets you create LINQ expressions from plain strings, therefore, giving you the possibility to dynamically build a LINQ expression concatenating strings as you require.
Here's an example of what can be achieved:
var resultDynamic = context.Customers
.Where("City == #0 and Age > #1", "Paris", 50)
.ToList();