c# linq returns duplicate data - c#

I have a this linq query:
var fling = (from b in flowering.FlowerViews
where ((!string.IsNullOrEmpty(flow_name)) && b.FLOWER_NAME == flow_name) || flow_name==""
where ((!string.IsNullOrEmpty(color_name)) && b.COLOR_NAME == color_name) || color_name == ""
where ((!string.IsNullOrEmpty(size)) && b.FLOWER_SIZE == size) || size==""
where ((low_price!=0) && low_price<= b.FLOWER_PRICE) || low_price==0
where ((high_price!=0) && high_price >= b.FLOWER_PRICE) || high_price==0
orderby b.COLOR_NAME
select new { b.FLOWER_NAME, b.COLOR_NAME, b.FLOWER_SIZE, b.FLOWER_PRICE, b.CHAR_DESC});
my where clauses work for me but when I run a for each loop over the returned values there is duplicate data because b.CHAR_DESC has 3 values to it where all the other return data only have one. I am wondering if there is a way to get the 3 values assigned to b.CHAR_DESC into a structure that does not cause duplicate b.Flower_name's to show up

Based on this post you should be able to call Distinct() for the anonymous type
var list = fling.Distinct().ToList();
And the compiler will take care of GetHashCode() and Equals() for the anonymous type based on attribute values.

Add .Distinct() at the end of your select clause, after the final parenthesis.

Related

Entity Framework Core : how to add conditional where clause from input filter parameter?

I am not sure if this is possible, but I have a linq to sql statement that takes in input filter parameter which could be null or missing a value like so
public List<MyViewModel> GetRecords(SearchDto? filter)
{
List<MyViewModel> results =
this.dbContext.MyTable
Where(s => s.IsActive == 'Y' &&
filter != null && !stringIsNullOrEmpty(s.deptId) && s.deptId == filter.deptId)
ToList();
}
When I add the conditional filter in the Where clause, my result is 0 count when the filter is null or empty.
I prefer not to do the old fashion way which is after the linq is call, add many lines of code like the following
if (!string.IsNullEmpty(filter.deptId)
{
results = result.Where(s => s.deptId == filter.deptId);
}
I would like to add the conditional Where filter in the linq statement instead of old fashion C# code.
Thanks for any advice
To correctly represent conditionally added filtering like in the "old fashion way" you should use something like this:
.Where(s.IsActive == 'Y'
&& (filter == null
|| (string.IsNullOrEmpty(s.deptId) || s.deptId == filter.deptId))`
So the "optional" condition evaluates to true for your AND clause when filter is null or empty.

comparing 2 lists of objects and return changes in the new list

I have a web app that gives users a feature to update (no delete or add) multiple records on the same page. As users submit changes, I pull the original list from the database and use linq to compare it to the updated list. Any changed records will be put on a new list and send to database for update.
Below is my code to compare. As I debug, I can see the 2 lists are different but the code returns the Differences with comparer = null, first = null, second = null. Can you guys spot the bug?
var Differences = OriginalList.Where(x => !NewList.Any(x1 => x1.ServiceName == x.ServiceName
&& x1.ServiceDescription == x.ServiceDescription
&& x1.ServiceURL == x.ServiceURL
&& x1.OrderIndex == x.OrderIndex
&& x1.GroupID == x.GroupID
&& x1.Active == x.Active))
.Union(NewList.Where(x => !OriginalList.Any(x1 => x1.ServiceName == x.ServiceName
&& x1.ServiceDescription == x.ServiceDescription
&& x1.ServiceURL == x.ServiceURL
&& x1.OrderIndex == x.OrderIndex
&& x1.GroupID == x.GroupID
&& x1.Active == x.Active)));
return Differences;
You are probably looking for Linq's Except method.
https://msdn.microsoft.com/library/bb300779(v=vs.100).aspx
You'll need to define how to compare for equality of your "x1" object. Probably the easiest way to do that is to override Equals():
https://msdn.microsoft.com/en-us/library/ms173147(v=vs.80).aspx
Then to get the difference, you simply do:
var Differences = OriginalList.Except(NewList);

C# LINQ DataTime List

Have a some problem with converting to List
public List<DateTime> findDateBetween(DateTime start,DateTime end)
{
var query = from entry in sd.gw_chemistry
where (entry.insert_datetime >=start & entry.insert_datetime<=end & a == entry.well_id & b == entry.indicator_id)
select entry.insert_datetime;
return (List<DateTime>)query;
}`
Error:
System.InvalidCastException: Unable to cast object of type "System.Data.Objects.ObjectQuery1[System.Nullable1[System.DateTime]]" to type "System.Collections.Generic.List`1[System.DateTime]".
There are a number of problems with your code.
Your query is selecting elements of type DateTime? (or Nullable<DateTime>). You will need to decide what you want to do if a date is null. Exclude it from the results? Return a default value? If you can be sure it will never be null, you can select entry.insert_datetime.Value.
Your query does not return a list, you will have to convert it to a list using ToList().
For the conditional AND operator (&&) it appears you are using &.
You are using variables a and b that do not seem to be defined anywhere (unless they are member variables).
So assuming that a and b are member variables, and an insert_datetime is never null you can do:
return sd.gw_chemistry
.Where(e =>
e.insert_datetime >= start && e.insert_datetime <= end &&
a == entry.well_id && b == entry.indicator_id)
.Select(e => e.insert_datetime.Value)
.ToList();
As the error is trying to tell you, that isn't a List<T>, and you can't cast it to a type that it isn't.
You can create a List<T> from your query by calling .ToList().
public List<DateTime> findDateBetween(DateTime start,DateTime end)
{var query = from entry in sd.gw_chemistry
where (entry.insert_datetime >=start & entry.insert_datetime<=end & a == entry.well_id & b == entry.indicator_id)
select entry.insert_datetime;
return query.ToList();}
Nothing serious here. You just need to call ToList() on your query. However I do see the usage of & instead of && as a serious problem.
Correct code should look like:
public List<DateTime> findDateBetween(DateTime start,DateTime end)
{
var query =
from entry in sd.gw_chemistry
where (entry.insert_datetime >=start &&
entry.insert_datetime<=end &&
a == entry.well_id &&
b == entry.indicator_id)
select entry.insert_datetime.Value;
return query.ToList();
}`

Only do Where condition if a value is passed in

I have the following LINQ statement that does on where on the date and a LabID.
I'm passing in a list of LABS and a date, however they are not required, and I could potentially only pass in a date, and no lab, in which case I'd like to get results for all labs for that particular lab.
here is what I have now:
List<dExp> lstDatExp = (from l in ctx.dExp.Include("datLab")
where values.Contains(l.datL.Lab_ID)
&& l.reportingPeriod == reportingPeriod
select l).ToList<dExp>();
But this breaks if the value getting passed in is not there. How do I change this to make sure both of my where statements are optional?
With IQueryable you can simply add conditions in steps:
int? reportingPeriod = ...;
IQueryable<dExp> resultsQuery = // don't use `var` here.
ctx.dExp.Include("datLab");
if (values != null)
resultsQuery = resultsQuery.Where(exp => values.Contains(exp.datL.Lab_ID));
if (reportingPeriod.Hasvalue)
resultsQuery = resultsQuery.Where(exp => exp.reportingPeriod == reportingPeriod.Value);
// additional .Where(), .OrderBy(), .Take(), .Skip() and .Select()
// The SQL query is made and executed on the line below
// inspect the string value in the debugger
List<dExp> results = resultsQuery.ToList();
Here are two ways to do that.
But first, please don't use a single lowercase l as an identifier. It is way too easy to confuse it with the number 1. More generally, stp using abbrevs in yr cde, it mks it hrdr to rd.
First technique:
var query = from lab in ctx.dExp.Include("datLab")
where values == null || values.Contains(lab.datL.Lab_ID)
where reportingPeriod == null || lab.reportingPeriod == reportingPeriod
select lab;
var list = query.ToList<dExp>();
Second technique:
IEnumerable<dExp> query = ctx.dExp.Include("datLab");
if (values != null)
query = query.Where(lab=>values.Contains(lab.datL.Lab_ID));
if (reportingPeriod != null)
query = query.Where(lab=>lab.reportingPeriod == reportingPeriod);
var list = query.ToList<dExp>();
What we do is something like (l.reportingPeriod == reportingPeriod || reportingPeriod == null) So you check to see if the parameter is its default meaning it hasnt been used or if there is something there check it against the database.
You need to check if your values are null before doing the query, and if they are, don't do the extra condition.
List<dExp> lstDatExp =
(from l in ctx.dExp.Include("datLab")
where
(values == null || values.Contains(l.datL.Lab_ID)) &&
(reportingPeriod == null || l.reportingPeriod == reportingPeriod)
select l).ToList<dExp>();
This way if values or reportingPeriod are null they are essentially optional.

Problem creating empty IQueryable<T> object

Basically i want to merge two Iqueryable to one Iqueryable and then return the complete record set after my loop ends. It runs perfectly but in the end my objret have nothing but when i debug the loop obj have some records. wht im doing wrong
IQueryable<MediaType> objret = Enumerable.Empty<MediaType>().AsQueryable();
var typ = _db.MediaTypes.Where(e => e.int_MediaTypeId != 1 && e.int_MediaTypeId_FK == null).ToList();
for (int i = 0; i < typ.Count; i++)
{
IQueryable<MediaType> obj = _db.MediaTypes.Where(e => e.bit_IsActive == true && e.int_MediaTypeId_FK == typ[i].int_MediaTypeId);
IQueryable<MediaType> obj1 = _db.MediaTypes.Where(e => e.int_OrganizationId == Authorization.OrganizationID && e.bit_IsActive == true && e.int_MediaTypeId_FK == typ[i].int_MediaTypeId);
if (obj1.Count() > 0)
obj.Concat(obj1);
if(obj.Count() > 0)
objret.Concat(obj);
}
return objret;
Just like the other query operators, Concat doesn't change the existing sequence - it returns a new sequence.
So these lines:
if (obj1.Count() > 0)
obj.Concat(obj1);
if(obj.Count() > 0)
objret.Concat(obj);
should be
if (obj1.Count() > 0)
objret = objret.Concat(obj1);
if(obj.Count() > 0)
objret = objret.Concat(obj);
I'm not sure how well IQueryable is going to handle this, given that you're mixing LINQ to SQL (? maybe Entities) with Enumerable.AsQueryable, mind you. Given that you're already executing the queries to some extent due to the Count() calls, have you considered building up a List<T> instead?
(You don't need to execute the Count() at all - just call List<T>.AddRange(obj1) and ditto for obj.)
As jeroenh mentioned, ideally it would be nice to use a solution which could do it all that the database without looping at all in your C# code.
I think you should not do this with a for loop. The code you posted will go to the db for every single active mediatype twice to get Count() and additionally, twice to get actual results.
Checking the Count() property is not necessary: concatenating empty result sets has no additional effect.
Furthermore, I think what you're trying to achieve can be done with a single query, something along the lines of (not tested):
// build up the query
var rootTypes = _db.MediaTypes.Where(e => e.int_MediaTypeId != 1 && e.int_MediaTypeId_FK == null);
var activeChildren = _db.MediaTypes
.Where(e => e.bit_IsActive);
var activeChildrenForOrganization = _db.MediaTypes
.Where(e => e.int_OrganizationId == Authorization.OrganizationID && e.bit_IsActive);
var q = from types in rootTypes
join e in activeChildren
on types.int_MediaTypeId equals e.int_MediaTypeId_FK into joined1
join e in activeChildrenForOrganization
on types.int_MediaTypeId equals e.int_MediaTypeId_FK into joined2
select new {types, joined1, joined2};
// evaluate the query and concatenate the results.
// This will only go to the db once
return q.ToList().SelectMany(x => x.joined1.Concat(x.joined2));

Categories

Resources