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));
Related
Of course I made some research and tried something but can't make it.
I have this code so far:
var workLogs = (from wl in _db.WorkLogs
where (wl.Users_UserId == userId && wl.Works_WorkId == workId && wl.Date > beginDate && wl.Date < endDate)
select new
{
wl.Users_UserId,
wl.Works_WorkId,
wl.Time,
wl.Date
})
.AsEnumerable()
.Select(wl => new
{
userName = GetUserNameFromId(wl.Users_UserId),
workName = GetWorkNameFromId(wl.Works_WorkId),
wl.Time,
wl.Date
});
As you can see I tried enumeration but when I run the application, workLogs comes empty. And when I inspect it with a breakpoint, I see "Enumeration yielded no results."
What I trying to make is return work and user name in workLogs. And I wrote two method to do this. I just cant call them in my Linq query.
Without my methods, it works good as you can see it: http://i.imgur.com/ALb5f0K.png
And my methods also works good outside of query.
I hope I made myself clear.
If there are no results then it means your underlying source doesn't have data, or your where clause is filtering out more than you think it should be. There is no problem that you could have with your select statement(s) that would cause a sequence that would normally return results to instead return no results. At most you could cause it to throw an exception.
There is no reason why the following code should not work. You do not need other code to make this work.
If this return no results, it just means that the where clause is filtering everything.
Make a manual foreach with a if statement to debug this and you will see that there is no problem with your LINQ request.
var workLogs = from wl in _db.WorkLogs
where (wl.Users_UserId == userId && wl.Works_WorkId == workId && wl.Date > beginDate && wl.Date < endDate)
select new
{
GetUserNameFromId(wl.Users_UserId),
GetWorkNameFromId(wl.Works_WorkId),
wl.Time,
wl.Date
}
Try replacing .AsEnumerable() with .ToArray().
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.
I have a LINQ query that run on datatable that has 5,00,000 plus records. This query returns me only one row but takes almost 30 seconds to run. This is my query
var callDetailsForNodes = from records in dtRowForNode.Select().Select(dr =>
new
{
caller1 = StringComparer.CurrentCultureIgnoreCase.Compare(dr["F1"], dr["F2"]) < 0 ? dr["F1"] : dr["F2"],
caller2 = StringComparer.CurrentCultureIgnoreCase.Compare(dr["F1"], dr["F2"]) < 0 ? dr["F2"] : dr["F1"],
time = dr["F3"],
filters = dr.Field<string>("F9")
}).Where(dr => (dtMin <= Convert.ToDateTime(dr.time)) && (dtMax >= Convert.ToDateTime(dr.time)) && (lstCallType.Contains(dr.filters))
&& (dtMinTime <= Convert.ToDateTime(dr.time).TimeOfDay) && (dtMaxTime >= Convert.ToDateTime(dr.time).TimeOfDay))
.GroupBy(drg => new { drg.caller1, drg.caller2 })
.Select(drg => new { drg.Key.caller1, drg.Key.caller2, count = drg.Count() }).AsEnumerable()
where (records.caller1.ToString() == VerSelected || records.caller2.ToString() == VerSelected)
select records;
Again i run a query to rearrange the data get it from above query as
var callDetailsForNodes_ReArrange = from records in callDetailsForNodes.Select(r => new
{
caller1 = r.caller1.ToString() == VerSelected ? r.caller1 : r.caller2,
caller2 = r.caller1.ToString() != VerSelected ? r.caller1 : r.caller2,
count = r.count
})
select records;
Then i am just binding this collection to gridview.
Is there any efficient way to query on such a large dataset
Edit
I have try to debug the programm step by step and find that this 2 queries actually runs fast and time is taken at the step when i add the result set of this query to ObservableCollection to bind it to gridview. Here is the code
foreach (var callDetailsForNode_ReArrange in callDetailsForNodes_ReArrange)
{
_CallForNodes.Add(new CallForNodeData
{
Caller1 = callDetailsForNode_ReArrange.caller1.ToString(),
Caller2 = callDetailsForNode_ReArrange.caller2.ToString(),
Count = callDetailsForNode_ReArrange.count
});
}
Here callDetailsForNodes_ReArrange has resultset count = 1
One thing that would help would be to convert dtMin, dtMax and dtMinTime before the call into the units of the data (dr.time). Then you can get rid of the Convert.ToDateTime that is happening multiple times on each record.
I have tidied up your query a little (although this won't make a massive amount of difference to performance, and there may be typos as I don't have VS to hand). From your edit it seems you are a little confused by deferred execution in LINQ. callDetailsForNodes does not represent your results - it is a query that will provide your results once it is executed.
If you have to do all this querying in process I suggest you add a ToList after the first select and run that in isolation. Then add ToList to the Where clause. Calling ToList will force your query to execute and you will see where the delays are.
One final note - you should pass your records directly to ObservableCollection constructor rather than calling Add for each item. Calling Add will (I think) cause the collection to raise a changed notification which is not a big deal for small lists but will slow things down for larger lists.
var callDetailsForNodes = dtRowForNode.AsEnumerable()
.Select(dr => new {
caller1 = StringComparer.CurrentCultureIgnoreCase.Compare(dr["F1"], dr["F2"]) < 0 ? dr["F1"] : dr["F2"],
caller2 = StringComparer.CurrentCultureIgnoreCase.Compare(dr["F1"], dr["F2"]) < 0 ? dr["F2"] : dr["F1"],
time = Convert.ToDateTime(dr["F3"]),
filters = dr.Field<string>("F9")})
.Where(dr => (dtMin <= dr.time)
&& (dtMax >= dr.time)
&& (lstCallType.Contains(dr.filters))
&& (dtMinTime <= dr.time.TimeOfDay)
&& (dtMaxTime >= dr.time.TimeOfDay)
&& caller1 == VerSelected || caller2 == VerSelected))
.GroupBy(drg => new { drg.caller1, drg.caller2 })
.Select(drg => new { drg.Key.caller1, drg.Key.caller2, count = drg.Count());
I am trying to use AddMonths in a query
List<Entities.Subscriber> items = (from s in context.Subscribers
where s.Validated == false && s.ValidationEmailSent == true && s.SubscriptionDateTime < DateTime.Now.AddMonths(-1)
select s).ToList();
But I recieve an error :
LINQ to Entities does not recognize the method 'System.DateTime
AddMonths(Int32)' method, and this method cannot be translated into a
store expression.
Is there a way I can use this function inside my query?
The simplest fix to this is to work out the time limit once before using LINQ:
DateTime limit = DateTime.Now.AddMonths(-1);
List<Entities.Subscriber> items = (from s in context.Subscribers
where s.Validated == false && s.ValidationEmailSent == true &&
s.SubscriptionDateTime < limit)
select s).ToList();
Or more readably IMO:
var items = context.Subscribers
.Where(s => !s.Validated &&
s.ValidationEmailSent &&
s.SubscriptionDateTime < limit)
.ToList();
There's no benefit in using a query expression here, and explicit comparisons with true and false are ugly IMO (unless your properties are of type Nullable<bool> of course).
Jon Skeet has already provided a simple fix, but if you want the DateTime.Now.AddMonths bit to run on the database, try the EntityFunctions.AddMonths method.
This is a more general approach that is especially useful when you cannot replicate the expression cheaply or correctly on the client.
You can change your code to:
DateTime oneMonth = DateTime.Now.AddMonths(-1)
List<Entities.Subscriber> items = (from s in context.Subscribers
where s.Validated == false && s.ValidationEmailSent == true && s.SubscriptionDateTime < oneMonth
select s).ToList();
You have to do this because AddMonth is a .NET function that can't be translated into SQL by Linq to Entities. Perform the calculation in your code and then use the resulting datetime will work.
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.