Entity Framework building Where clause on the fly using Expression - c#

Using Entity Framework C# and have this query, I need the part where it says:
where x.Login_Status == "Submitted"
to be dynamic. There are different cases it could be "Submitted" or null or something else and instead of writing multiple if statement with different queries in it, want to have a Predicate in a where clause.
status = (from x in ctx.table
where x.Login_Status == "Submitted"
orderby x.SUB_DATE descending
select new Model_Table()
{
Id = x.ID,
Name = x.NAME,
Code = x.Code,
DateSubmitted = x.SUB_DATE
}).ToList<Model_Table>();
Is that possible?
Solution:
Inside the if statement when more parameters encountered use this
where_expression = x => x.Login_Status == "Submitted" || x.Login_Status == null;
Here is a complete code that worked for me, anything between square brackets replace to suit your code:
Expression<Func<[Replace with your Entity], bool>> where_submitted = x => x.Login_Status == "Submitted";
// Check if all selected
if (CheckBox_Show_All_Submitted.Checked)
{
where_submitted = x => x.Login_Status == "Submitted" || x.Login_Status == null;
}
status =
ctx.[Replace with your Entity Table]
.Where(where_submitted)
.OrderByDescending(x => x.SUB_DATE)
.Select(x => new Model_Table
{
Id = x.ID,
Name = x.NAME,
Code = x.Code,
DateSubmitted = x.SUB_DATE
}).ToList<Model_Table>();

You need an Expression<Func<Entity,bool>>, not a Predicate<Entity>. The difference is that a predicate is a compiled delegate, and an expression is code as data and thus can be translated to SQL.
Here is an example:
//You can have this expression have different values based on your logic
Expression<Func<Entity,bool>> where_expression = x => x.Login_Status == "Submitted";
var query =
ctx.Table
.Where(where_expression)
.OrderByDescending(x => x.SUB_DATE)
.Select(x => new Model_Table())
{
Id = x.ID,
Name = x.NAME,
Code = x.Code,
DateSubmitted = x.SUB_DATE
}).ToList();
Please note that you need to replace Entity with the name of the real class.

Create an extension method for IQueryable like this:
public static class MethodExtensions{
public static IEnumerable<Model_Table> Query(this IQueryable<TEntity> source, string data){
return (from x in source
where x.Login_Status == data
orderby x.SUB_DATE descending
select new Model_Table()
{
Id = x.ID,
Name = x.NAME,
Code = x.Code,
DateSubmitted = x.SUB_DATE
}).ToList<Model_Table>();
}
}
Now you can use it like this:
var result = ctx.table.Query("somethingelse");

Related

System.InvalidOperationException issue in LINQ expression

When I run the following Linq:
var selectedProduct = db.Products.FirstOrDefault(a => a.ProductNr == productNr)?.Id;
model.PackTypes = db.Zones
.Where(az => az.ProductId == selectedProduct && az.StoragePrio > 0)
.ToList()
.DistinctBy(p => p.PackType)
.OrderBy(x => x.PackType)
.Select(x => new DropdownItemViewModel<int>
{
Id = (int)x.PackType,
Name = x.PackType.Translate()
});
return true;
I get this error:
System.InvalidOperationException: 'Nullable object must have a value.' on this code Id = (int)x.PackType,
Now I know I must do a nullcheck so I have tried this:
if (x.PackType != null)
return new DropdownItemViewModel<int>
{
Id = (int)x.PackType,
Name = x.PackType.Translate()
};
return null;
Still doesn't work, by that I mean I still have problem with NullCheck.
This query more effective and should not have all mentioned errors:
var query =
from p in db.Products
where p.ProductNr == productNr
join az in db.Zones on p.Id equals az.ProductId
where az.StoragePrio > 0 && az.PackType != null
select new { az.PackType };
model.PackTypes = query
.Distinct()
.OrderBy(x => x.PackType)
.Select(x => new DropdownItemViewModel<int>
{
Id = (int)x.PackType,
Name = x.PackType.Translate()
})
.ToList();
Instead of two database requests this query sends only one. Also all operations are done on the server side.

Why can't I remove columns/props from select in linq statement

I want to remove columns which I don't need to display!
Basically customer needs to see only FirstName and LastName, so I wanted to return only FirstName and LastName to end user.
But my app breaks If I remove other props from select:
var product = await _context.Products.OrderBy(p => p.CreatedDate)
.Select(x => new Product
{
ProductId = x.ProductId,
GroupId = x.GroupId,
ProductStatus = x.ProductStatus,
Title = x.Title,
Price = x.Price
}).FirstOrDefaultAsync(u => u.ProductId == dbObj.Id && u.GroupId == ProductGroup.Drinks && u.ProductStatus.Id == (int)ProductStatusEnum.Active);
I would like to have something like this:
var product = await _context.Products.OrderBy(p => p.CreatedDate)
.Select(x => new Product
{
Title = x.Title,
Price = x.Price
}).FirstOrDefaultAsync(u => u.ProductId == dbObj.Id && u.GroupId == ProductGroup.Drinks && u.ProductStatus.Id == (int)ProductStatusEnum.Active);
In classic (T-SQL) SQL statement I should be able to have columns in WHERE but NOT IN SELECT also, and here in LINQ looks like I must have columns in SELECT if I use them in WHERE.
Thanks
Cheers
Your query is not selecting columns, it's selecting new Product objects which you are setting the property values on. In the second case, you are only setting the Title and Price properties, and the others will be whatever the defaults are.
You may want to look at anonymous types, e.g.:
.Select(x => new { Title = x.Title, Price = x.Price });
Just change the order of the Linq statements. First filter by using Where before calling Select and use an anonymous object to project into:
var product = await _context.Products
.Where(u => u.ProductId == dbObj.Id
&& u.GroupId == ProductGroup.Drinks
&& u.ProductStatus.Id == (int) ProductStatusEnum.Active)
.OrderBy(p => p.CreatedDate)
.Select(x => new
{
Title = x.Title,
Price = x.Price
}).FirstOrDefaultAsync();
This creates an anonymous type (product) with 2 properties Title and Price.
If you don't want to use an anonymous type another option is to create a class which contains only the properties you want.
public class ProductSummary
{
public string Title { get; set; }
public decimal Price { get; set; }
}
Then you can select into the concrete class:
.Select(x => new ProductSummary { Title = x.Title, Price = x.Price })

Map Linq groupby count to C# entity

I have this sql query that performs a groupby on a single field. It then counts the groupby's. So far so good.
select type, count(*)
from myTable
group by type
//Result
//TypeA = 5
//TypeB = 3
However, I am having trouble performing this query with Linq as I need to map the outcome of Count() to a specific entity.
The entity I want to map the count to:
public class MyEtity(){
public int TypeACount {get; set;}
public int TypeBCount {get; set;}
}
The linq query I currently use which
MyEntity test = data
.GroupBy(c => c.type)
.Select(g => new MyEntity (){
TypeACount = g.Where(d => d.type == "A").Count(),
TypeBCount = g.Where(d => d.type == "B").Count()
});
Extra info
Based on some answers, a little extra info. My original plan was to use following.
var firstResults = session.Query<MyEntity>()
.Where(//several date filter conditions)
.ToList();
return new MyEntity() {
TypeACount = firstResults.Where(s => s.type == "A").Count(),
TypeBCount = firstResults.Where(s => s.type == "B").Count()
};
This works, but table queried is rather large and the query took quite some time. Based on a colleagues feedback I was asked if the query couldn't be made in to 1 part instead of separating it. The idea being that the query counting logic would remain in SQL rather than in C#. I don't know if that would actually be faster, but that is what I am trying to figure out.
You should map after you get the information
var results = data
.Where(c => c.TypeOfUsage == "A" || c.TypeOfUsage == "B")
.GroupBy(c => c.TypeOfUsage)
.Select(g => new
{
Type = g.Key,
Count = g.Count()
}).ToList();
MyEntity test = new MyEntity
{
TypeACount = results.FirstOrDefault(d => d.Type == "A")?.Count ?? 0,
TypeBCount = results.FirstOrDefault(d => d.Type == "B")?.Count ?? 0
}
Or if you don't have C# 6
var a = results.FirstOrDefault(d => d.Type == "A");
var b = results.FirstOrDefault(d => d.Type == "B");
MyEntity test = new MyEntity
{
TypeACount = a == null ? 0 : a.Count,
TypeBCount = b == null ? 0 : b.Count
}
Another option would be to use a constant group by.
MyEntity test= data
.Where(c => c.TypeOfUsage == "A" || c.TypeOfUsage == "B")
.GroupBy(c => 1)
.Select(g => new MyEntity
{
TypeACount = g.Where(d => d.TypeOfUsage == "A").Count(),
TypeBCount = g.Where(d => d.TypeOfUsage == "B").Count()
}).Single();
This would be more like the following SQL
select
sum(case when typeOfUseage = 'A' then 1 else 0 end) AS TypeACount
, sum(case when typeOfUseage = 'B' then 1 else 0 end) AS TypeBCount
from myTable
why not the clasic way?, I do not see in your query the reason for group by or Select;
var entity=new MyEntity()
entity.TypeACount = data.Count(a => a.TypeOfUsage == "A"),
entity.TypeBCount =data.Count(b => b.TypeOfUsage == "B")

Lambda not equal on join

Table 1 called Category contains 70 records
Table 2 called FilterCategorys contains 0 records (currently).
my lambda join, I want to pull only records that don't match, so in this case I expect to get 70 records back. Here's my incorrect Lambda:
var filteredList = categorys
.Join(filterCategorys,
x => x.Id,
y => y.CategoryId,
(x, y) => new { catgeory = x, filter = y })
.Where(xy => xy.catgeory.Id != xy.filter.CategoryId)
.Select(xy => new Category()
{
Name = xy.catgeory.Name,
Id = xy.catgeory.Id,
ParentCategoryId = xy.catgeory.ParentCategoryId
})
.ToList();
Whats the correct syntax I need here?
Not sure if you have a requirement of using lambdas (rather than query syntax), but I prefer query syntax for statements that have outer joins.
This should be equivalent:
var filteredList = (
from c in Categorys
join fc in FilterCategorys on c.Id equals fc.CategoryId into outer
from o in outer.DefaultIfEmpty()
select new
{
Category = new Category
{
Name = c.Name,
Id = c.Id,
ParentCategoryId = c.ParentCategoryId
},
Exists = (o != null)
})
.Where(c => !c.Exists)
.Select(c => c.Category);
If you want to do it in purely lambda:
var match = categorys.Join(filterCategorys, x => x.Id, y => y.CategoryId, (x, y) => new { Id = x.Id });
var filteredList = categorys.Where(x => !match.Contains(new {Id = x.Id}));
I haven't measured the performance of this, but for 70 records, optimization is not an issue.
Well I came up with a solution that takes away the need for the join.
var currentIds = filterCategorys.Select(x => x.Id).ToList();
var filteredList = categorys.Where(x => !currentIds.Contains(x.Id));
very similar to #Zoff Dino answer, not sure about performance, maybe someone would like to check.
Try this:
var categories= ...
var filteredCategories=...
var allExceptFiltered = categories.Except(filteredCategories, new CategoryComparer()).ToList();
If you don't provide a custom Comparer that framework has no way of knowing that 2 Category objects are the same(even if they have the same ID),it just thinks that they are different objects (it checks for reference equality )
so you must add this class to your project:
public class CategoryComparer: IEqualityComparer<Category>
{
public bool Equals(Category x, Category y)
{
if (x == null && y == null)
return true;
if (x == null)
return false;
if (y == null)
return false;
return x.CategoryId.GetHashCode() == y.CategoryId.GetHashCode();
}
public int GetHashCode(Category obj)
{
return obj.CategoryId.GetHashCode();
}
}
update
Also check out Wyatt Earp's answer,it is very useful to know how to do an outer join
update 2
Your problem is the Join method.
The Where clause is "called" after the join.so after you have joined the listed based on the ID you select those which have different IDs,that's why you get no resuts
Could you draw bracket and it should work.
....Where(xy => (xy.catgeory.Id != xy.filter.CategoryId))

How to write Linq depending if a value is provided or not

I am trying to write a LINQ statement with some optional where clauses. This is for a search. The user can select a specific site to search or search against all sites:
var query =
_db.STEWARDSHIP
.OrderBy(r => r.SITE.SITE_NAME)
.Where(r => r.SITE_ID == SiteId)
.Where(r => r.VISIT_TYPE_VAL.VISIT_TYPE_ID == VisitTypeId)
.Select(r => new
{
id = r.STEWARDSHIP_ID,
name = r.SITE.SITE_NAME,
visit_type = r.VISIT_TYPE_VAL.VISIT_TYPE_DESC,
visit_date = r.VISIT_DATE
});
return query;
So when the method gets SiteId = 14, for instance, no problem. However, when it gets SiteId = null, then that where clause should not be considered.
Thanks
Eric
That's easy:
var query = _db.STEWARDSHIP.OrderBy(r => r.SITE.SITE_NAME);
if (SiteId != null)
{
query = query.Where(r => r.SITE_ID == SiteId);
}
query = query.Where(r => r.SITE.SITE_TYPE_VAL.SITE_TYPE_ID == SiteTypeId)
.Select(r => new
{
id = r.STEWARDSHIP_ID,
name = r.SITE.SITE_NAME,
visit_type = r.VISIT_TYPE_VAL.VISIT_TYPE_DESC,
visit_date = r.VISIT_DATE
});
return query;
This works because queries compose nicely - and they really only represent queries; it's only when you try to fetch data from them that the query is actually executed.
Can't you just edit the where clause to something like
.Where(r=>SiteId == null || r.SiteId == SiteId)
you can use where clause in one statement ..like this ..
.Where(r => SiteID == null || r.SITE_ID == SiteID)
I'm stealing a trick from TSQL. Just check for the null value as well.
...
.Where(r => SiteID == null || r.SITE_ID == SiteID)
...
The SQL example is this:
WHERE (SITE_ID = #given OR #given IS NULL) --return matches or all
Though if that value is mutable and you want the value at the time the query was built, try this instead:
var localSiteID = SiteID;
...
.Where(r => localSiteID == null || r.SITE_ID == SiteID)
...

Categories