I'm currently working on an ASP.NET MVC 4.5 application. I have one question for the Linq Gurus please.
I want to use the .Where Filter only if the importing parameter initOfferList = false. The problem: the PagedList gets only filtered for the first page (10 entries here).
My Linq query looks as follows:
public IPagedList<OfferListVM> GetOfferList(OfferDateSearchVM offerDateSearch, bool initOfferList, int page)
{
var offerList = Db.Offer
.Select(x => new OfferListVM
{
OfferId = x.OfferId,
CreatedDate = x.CreatedDate,
Label = x.OfferData.Label,
})
.OrderBy(x => x.OfferId)
.ToPagedList(page, 10);
if (!initOfferList)
{
offerList = offerList
.Where(x => x.CreatedDate >= offerDateSearch.FromDate && x.CreatedDate <= offerDateSearch.ToDate)
.OrderBy(x => x.OfferId)
.ToPagedList(page, 10);
}
return offerList;
}
How can I modify my query to properly use the .Where clause on all entries, but only when the importing parameter initOfferList = false?
Thank you!
Try:
public IPagedList<OfferListVM> GetOfferList(OfferDateSearchVM offerDateSearch, bool initOfferList, int page)
{
var offerListQuery = Db.Offer.OrderBy(x => x.OfferId);
if (!initOfferList)
{
offerListQuery = offerListQuery
.Where(x => x.CreatedDate >= offerDateSearch.FromDate &&
x.CreatedDate <= offerDateSearch.ToDate
);
}
var offerList = offerListQuery
.Select(x => new OfferListVM
{
OfferId = x.OfferId,
CreatedDate = x.CreatedDate,
Label = x.OfferData.Label,
})
.ToPagedList(page, 10);
return offerList;
}
If I understand correctly, the following should take your boolean flag into account in a single Linq, thus applying the where to the full list before filtering.
var offerList = Db.Offer
.Where(x => initOfferList == true || (initOfferList == false && x.CreatedDate >= offerDateSearch.FromDate && x.CreatedDate <= offerDateSearch.ToDate))
.Select(x => new OfferListVM
{
OfferId = x.OfferId,
CreatedDate = x.CreatedDate,
Label = x.OfferData.Label,
})
.OrderBy(x => x.OfferId)
.ToPagedList(page, 10);
Related
I have a list of FlightTicketRequestDocument used for both Patients and their Escorts requesting a plane ticket. Theses are grouped with a GUID named GroupId.
I'm trying to group them inside this object:
public class FlightTicketRequestDocumentGroup
{
public Guid GroupId { get; set; }
public FlightTicketRequestDocument PrincipalDocument { get; set; }
public List<FlightTicketRequestDocument> GroupDocuments { get; set; }
}
PrincipalDocument holds the Patient if there's one. (If there isn't we just want the first Escort)
GroupDocuments holds the Escorts. (Except the first one that is placed in PrincipalDocument)
I'm attempting to use different expressions to set my values inside a Select New statement depending if there's a Patient or not but I'm getting the error:
The nested query is not supported. Operation1='Case' Operation2='VarRef'
Here's the code (Note: StayEscortId is null when the person is a Patient.):
var indexGroups = await _db.FlightTicketRequests
.OrderBy(f => f.FirstName)
.ProjectTo<FlightTicketRequestDocument>()
.GroupBy(f => f.GroupId)
.Select(g => new FlightTicketRequestDocumentGroup
{
GroupId = g.Key,
PrincipalDocument = g.Any(f => f.StayEscortId == null)
? g.FirstOrDefault(f => f.StayEscortId == null)
: g.FirstOrDefault(),
GroupDocuments = g.Any(f => f.StayEscortId == null)
? g.Where(c => c.StayEscortId != null).ToList()
: g.Where(c => c.StayEscortId != null).OrderBy(f => f.FullName).Skip(1).ToList()
})
.ToListAsync(cancellationToken);
What can I use instead of the conditional operator ?: ?
The solution that I ended up using:
As specified by #mjwills, since I simply want the values where StayEscordId is null first, all I need to do is to sort as a boolean (false values end up at the top).
For the rest of the group, I do the same operation while Skipping 1 (the first one I chose).
Modified code:
var indexGroups = await _db.FlightTicketRequests
.OrderBy(f => f.FirstName)
.ProjectTo<FlightTicketRequestDocument>()
.GroupBy(f => f.GroupId)
.Select(g => new FlightTicketRequestDocumentGroup
{
GroupId = g.Key,
PrincipalDocument = g.OrderBy(f => f.StayEscortId != null).ThenBy(f => f.FullName).FirstOrDefault(),
GroupDocuments = g.OrderBy(f => f.StayEscortId != null).ThenBy(f => f.FullName).Skip(1).ToList()
})
.ToListAsync(cancellationToken);
Here's what the query would look like if you were to use if/else instead of the ternary conditions. You would just use a code block inside of the .Select() to return the results:
var flightTicketRequestDocuments = await _db.FlightTicketRequests
.OrderBy(f => f.FirstName)
.ProjectTo<FlightTicketRequestDocument>()
.GroupBy(f => f.GroupId)
.ToListAsync(cancellationToken);
var indexGroups = flightTicketRequestDocuments
.Select(g =>
{
FlightTicketRequestDocument principalDocument;
List<FlightTicketRequestDocument> groupDocuments;
if (g.Any(f => f.StayEscortId == null))
{
principalDocument = g.FirstOrDefault(f => f.StayEscortId == null);
groupDocuments = g.Where(c => c.StayEscortId != null).ToList();
}
else
{
principalDocument = g.FirstOrDefault();
groupDocuments = g.Where(c => c.StayEscortId != null).OrderBy(f => f.FullName).Skip(1).ToList();
}
return new FlightTicketRequestDocumentGroup
{
GroupId = g.Key,
PrincipalDocument = principalDocument,
GroupDocuments = groupDocuments
};
})
.ToList();
Note that this approach requires enumerating the IQueryable before performing the .Select() so that it's applied against an IEnumerable.
Needs a check or two (or a tweak or two) but if i remember correctly expression trees accept null coalescence operator you may try to swap in the place of the ternary.
Starting (and BASING) from your example, maybe something like the following?
var indexGroups = await _db.FlightTicketRequests
.OrderBy(f => f.FirstName)
.ProjectTo<FlightTicketRequestDocument>()
.GroupBy(f => f.GroupId)
.Select(g => new FlightTicketRequestDocumentGroup
{
GroupId = g.Key,
PrincipalDocument = g.FirstOrDefault(f => f.StayEscortId == null) ?? g.OrderBy(f => f.FullName).FirstOrDefault(),
GroupDocuments = g.Where(c => c.StayEscortId != null).OrderBy(f => f.FullName).Skip(Math.Max(x.Count(y => y.StayEscortId == null), 1)).ToList()
})
.ToListAsync(cancellationToken);
How can i replace x.Demographic.AgeRange with any other field?
Eg
var field_to_check = "Country";
x.Demographic.ReflectedProperty(field_to_check)=="USA"
var field_to_check = "AgeRnage";
x.Demographic.ReflectedProperty(field_to_check)=="20-30"
I Tried with a refelcted property. but cant succeed.
Favourability = db.Questions
.OrderByDescending(x => x.Responces.Count(y => y.Responseval == Constants.options.Agree || y.Responseval == Constants.options.Tend_to_Agree))
.Select(z => new
{
z.QuestionTitle,
Count = z.Responces.Where(x =>
x.Demographic.AgeRange == repval &&
(x.Responseval == Constants.options.Agree || x.Responseval == Constants.options.Tend_to_Agree)
)
.Count()
})
.Select(z => new
{
z.QuestionTitle,
z.Count,
Perc = ((z.Count / totresponcecount) * 100)
}
)
.ToList();
so that i can write only one linq statement as dynamic filtering rather than switch statements for all required properties.
You can construct the expression tree from multiple different ones. Use LinqKit, and write:
Expression<Func<Demography, string>> fieldSpec = d => d.AgeRange;
And then in your expession:
var exp = db.Questions.AsExpadable() ... fieldSpec.Expand(x.Demographic) == repval ...
Now all you need to do is construct the fieldSpec dynamically, which is an excercise for you :)
This works fine.g.Key is not null and has appropriate data:
var result = db.JournalEntries.Include(je => je.JournalRecords.Select(jr => jr.Account).Select(j => j.AccountParticulars))
.Where(je => je.Date >= existingLedgerTransaction.From && je.Date <= existingLedgerTransaction.To)
.SelectMany(s => s.JournalRecords)
.GroupBy(d => d.AccountParticular.Account.AccountCategory)
.Select(g => new { Name = g.Key.Name });
But this does not work as g.Key is null:
var DateFilter = new Func<JournalEntry, bool>(je => je.Date >= existingLedgerTransaction.From && je.Date <= existingLedgerTransaction.To);
var result = db.JournalEntries.Include(je => je.JournalRecords.Select(jr => jr.Account).Select(j => j.AccountParticulars))
.Where(DateFilter)
.SelectMany(s => s.JournalRecords)
.GroupBy(d => d.AccountParticular.Account.AccountCategory)
.Select(g => new { Name = g.Key.Name });
I tried the same thing in a simple console app with static collection and passing in predicate works fine. What could be the problem here?
NOTE: Lazy loading/dynamic proxy is disabled
Try
var DateFilter = new Expression<Func<JournalEntry, bool>>(je => je.Date >= existingLedgerTransaction.From && je.Date <= existingLedgerTransaction.To);
as you need to pass an expression tree to EF
I'm trying to translate this sql statement
SELECT row, SUM(value) as VarSum, AVG(value) as VarAve, COUNT(value) as TotalCount
FROM MDNumeric
WHERE collectionid = 6 and varname in ('C3INEV1', 'C3INEVA2', 'C3INEVA3', 'C3INVA11', 'C3INVA17', 'C3INVA19')
GROUP BY row
into an EF 4 query using lambda expressions and am missing something.
I have:
sumvars = sv.staticvararraylist.Split(',');
var aavresult = _myIFR.MDNumerics
.Where(r => r.collectionid == _collid)
.Where(r => sumvars.Contains(r.varname))
.GroupBy(r1 =>r1.row)
.Select(rg =>
new
{
Row = rg.Key,
VarSum = rg.Sum(p => p.value),
VarAve = rg.Average(p => p.value),
TotalCount = rg.Count()
});
where the staticvararraylist has the string 'C3INEV1', 'C3INEVA2', 'C3INEVA3', 'C3INVA11', 'C3INVA17', 'C3INVA19' (without single quotes) and the _collid variable = 6.
While I'm getting the correct grouping, my sum, average, & count values aren't correct.
You didn't post your error message, but I suspect it's related to Contains. I've found that Any works just as well.
This should get you quite close:
var result =
from i in _myIFR.MDNumerics
where i.collectionid == _collid && sumvars.Any(v => i.varname == v)
group i by i.row into g
select new {
row = g.Key,
VarSum = g.Sum(p => p.value),
VarAve = g.Average(p => p.value),
TotalCount = g.Count()
};
Try this:
var aavresult = _myIFR.MDNumerics
.Where(r => r.collectionid == _collid && sumvars.Contains(r.varname))
.GroupBy(r1 =>r1.row,
(key,res) => new
{
Row = key,
VarSum = res.Sum(r1 => r1.value),
VarAve = res.Average(r1 => r1.value),
TotalCount = res.Count()
});
In Linqpad, i can see the correct list. But in code, after putting in the list collection, order by doesn't work for BeginDate. If i use BeginDate with Max, it works. I don't understand where i am wrong?
var templist = contentRepository
.Get(q => (q.Status == (int)StatusEnum.Active) &&
(q.CategoryId == category.GetHashCode() || q.Category.ParentId == category.GetHashCode())
&& q.MinorVersion == 0
&& q.MajorVersion > 0)
.GroupBy(q => q.VersionId)
.OrderByDescending(q => q.Key)
.Select(q => new
{
VersionId = q.Key,
Id = q.Max(x => x.Id),
MajorVersion = q.Max(x => x.MajorVersion),
UpdatedAt = q.Max(x => x.UpdatedAt),
//BeginDate = q.Max(x=>x.BeginDate),
BeginDate = (q.OrderByDescending(x => x.Id).Take(1).Select(x=>x.BeginDate)).First(),
Title = (q.OrderByDescending(x => x.Id).Take(1).Select(x => x.Title)).First(),
ShowOnHomePage = (q.OrderByDescending(x => x.Id).Take(1).Select(x=>x.ShowOnHomePage)).First()
})
.OrderByDescending(x => x.BeginDate)
.Take(maxItemCount)
.ToList();
List<ContentEntity> contents = new List<ContentEntity>();
templist.ForEach(q => contents.Add(
contentRepository
.Get(x => x.VersionId == q.VersionId && x.MajorVersion == q.MajorVersion && x.MinorVersion == 0)
.FirstOrDefault()
));
return contents.Where(q => q.ShowOnHomePage == true)
.OrderByDescending(q => q.MajorVersion)
.OrderByDescending(q => q.BeginDate)
.Take(maxItemCount)
.ToList();
You are ordering by Id, not by BeginDate. Equivalent code for
q.Max(x => x.BeginDate)
Will be
q.OrderByDescending(x => x.BeginDate).Take(1).Select(x => x.BeginDate).First()
Or simplified
q.OrderByDescending(x => x.BeginDate).First().BeginDate