How to filter the OrderBy - entity-framework

i am retrieving data from database on the base of there id, as can be seen,
public ActionResult loadEpisodes(int id, string name, int epId)
{
ViewBag.LoadEps = db.VideoEpisode
.Where(ve => ve.VideoId == id && ve.IsActive == true)
.OrderByDescending(c => c.Sequence);
//some other code removed for the ease
return view();
}
It will return the episodes of some 'id' and in descending sequence value.
My question is, if there is way to retrieve the data in descending order but order must starts from the episode id 'epId'
I have tried the above method but failed
ViewBag.LoadEps = db.VideoEpisode
.Where(ve => ve.VideoId == id && ve.IsActive == true)
.OrderBy(c=>c.Sequence)
.First(c=> c.VideoEpisodeId ==epId);

EDIT: If I understand the problem correctly, you want the order to be a given episode with Id epId first, then a list ordered by sequence.
I'm not sure it can be done in one Linq query.
How about creating a new list, putting your chosen VideoEpisode in as the first element, and then appending a sorted list of the other episodes after that. Something like:
var Loadeps = new List<VideoEpisode>();
Loadeps.Add(db.VideoEpisode.First(c=> c.VideoEpisodeId ==epId));
Loadeps.Add(db.VideoEpisode
.Where(ve => ve.VideoId == id && ve.IsActive == true && c.VideoEpisodeId != epId)
.OrderByDescending(c => c.Sequence)).toList();

Related

How to get results from another query list if first linq query returns empty?

In this EF query, for the contacts list, I'm trying to query to get records for ContactTypeA and populate results in contacts list. If there are no records for ContactTypeA, then I want it to query for records for ContactTypeB and populate results in contacts list. I tried to use DefaultIfEmpty but that method only accepts single value and not a list. 'contacts' is a List object. Any ideas or even an alternative to DefaultIfEmpty? Thanks.
select(i => new transaction{
....
contacts = contactRepository.All.Where(c => c.AccountId == i.Account.Id && contactTypeRepository.All.Any(ct => ct.ContactId == c.Id && ct.Type == ContactType.ContactTypeA)).ToList().DefaultIfEmpty((contactRepository.All.Where(c => c.AccountId == i.Account.Id && contactTypeRepository.All.Any(ct => ct.ContactId == c.Id && ct.Type == ContactType.ContactTypeB)).ToList()
}
)
Firstly, make absolutely sure your contactRepository.All() method returns IQueryable<Contact> and not IEnumerable<Contact>, IList<Contact> or the like, otherwise you are automatically loading all contacts into memory.
From there, don't be afraid to simplify your query across multiple statements to make it a lot easier to understand. You should also leverage navigation properties rather than relying on completely disconnected entities and generic repositories and manually joining these all up in huge expressions.
Ideally an Account could have a collection of Contacts, but if not, at a minimum a Contact should have ContactType references:
var accountContactsQuery = contactRepository.All
.Where(c => c.AccountId == i.AccountId); //Remains IQueryable
var contacts = accountContactsQuery
.Where(c => c.ContactTypes.Any(ct => c.Type == ContactType.ContactTypeA)
.ToList(); // Gets List of Contacts where contains at least 1 ContactTypeA type.
// If we have none, replace with results for ContactTypeB
if (!contacts.Any())
contacts = accountContactsQuery
.Where(c => c.ContactTypes.Any(ct => c.Type == ContactType.ContactTypeB)
.ToList();
This looked a bit odd in that your ContactType appears to have a ContactId, (As opposed to Contact containing a ContactTypeId?) But the above reflects the relationship in your example.
With Account containing Contacts collection:
var accountContactsQuery = accountRepoitory.All
.Where(a => true /* replace with relevant criteria */);
var contacts = accountContactsQuery
.SelectMany(a => a.Contacts)
.Where(c => c.ContactTypes.Any(ct => c.Type == ContactType.ContactTypeA)
.ToList(); // Gets List of Contacts where contains at least 1 ContactTypeA type.
// If we have none, replace with results for ContactTypeB
if (!contacts.Any())
contacts = accountContactsQuery
.SelectMany(a => a.Contacts)
.Where(c => c.ContactTypes.Any(ct => c.Type == ContactType.ContactTypeB)
.ToList();
When dealing with conditions in expressions, I can suggest returning all relevant details, then building your final "payload" based on the conditions.
For example, if querying accounts to build transactions but wanting to load ContactAs if available and Bs for each account if not available:
var transactionData = accountRepoitory.All
.Where(a => true /* replace with relevant criteria */);
.Select(a => new
{
a.AccountId,
/* populate account and common details.. */
ContactAs = a.Contacts
.Where(c => c.ContactTypes.Any(ct => c.Type == ContactType.ContactTypeA).ToList(), // Consider a Select to get relevant details...
ContactBs = a.Contacts
.Where(c => c.ContactTypes.Any(ct => c.Type == ContactType.ContactTypeB).ToList()
}).ToList(); // Executes query against DB to load relevant data...
var transactions = transactionData
.Select( t => new Transaction
{
AccountId = t.AccountId,
/* Other fields */
Contacts = t.ContactAs.Any() ? t.ContactAs : t.ContactBs
}).ToList();
Essentially use the EF Linq expressions to load the possible data, so include results for both ContactA and ContactB, then afterwards build your final projection using that data and conditionally use the ContactA or B as suited. Generally I don't advise passing Entities back (Actual Contact entities) but project into minimally viable view models in the first EF query using Select.

EF Core: Get entities matching a filter, but only those that have the latest datetime property in their grouping by a composite key

I have a table of entities of class Pricing.
Previously the Pricing table had a unique index based on 3 foreign keys: InstitutionId, SubmissionTypeId and FeeTypeId.
Now it needs to have multiple possible Pricing rows per that same index, but I should only use the one with the latest value of a DateTime column ActiveFrom, i.e. I should only use the latest active Pricing for the given composite index.
My repository method needs to return all currently active Pricings matching two foreign key values (InstitutionId and SubmissionTypeId).
I tried doing it like this:
DateTime today = DateTime.Today;
IQueryable<Pricing> filteredActivePricings = Context.Pricings
.Where(pricing =>
pricing.InstitutionId == institutionId
&& pricing.SubmissionTypeId == submissionTypeId
&& pricing.ActiveFrom <= today
)
.GroupBy(pricing => new { pricing.InstitutionId, pricing.SubmissionTypeId, pricing.FeeTypeId })
.Select(pricingGroup => pricingGroup
.OrderByDescending(pricing => pricing.ActiveFrom)
.First()
);
IList<Pricing> result = await filteredActivePricings.ToListAsync();
But EF Core says it can't convert that syntax into a valid SQL query.
I've found a solution using pure SQL code, but I'd like to do it in the EF Core's LINQ method syntax, since the rest of my codebase is written in it.
So, I figured out my problem and a solution.
The problem was, as #ivan-stoev pointed out, I wrongly thought that IGrouping<K, T> (the result of IQueryable<T>.GroupBy) contained a collection of T entities grouped by the key K, and that I had to somehow process that collection to get my single entity T.
So here's my solution which utilizes the IQueryable<T>.GroupBy method properly, to get a collection of key values and dates which are then used in a Join back to the table, to get the full data of the entities I need.
DateTime today = DateTime.Today;
IQueryable<ActivePricingKey> filteredActivePricingKeyQuery = Context.Pricings
.Where(pricing =>
pricing.InstitutionId == institutionId
&& pricing.SubmissionTypeId == submissionTypeId
&& pricing.ActiveFrom <= today
)
.GroupBy<Pricing, PricingKey, ActivePricingKey>(
(Pricing pricing)
=> new PricingKey
{
InstitutionId = pricing.InstitutionId,
SubmissionTypeId = pricing.SubmissionTypeId,
FeeTypeId = pricing.FeeTypeId
},
(PricingKey key, IEnumerable<Pricing> pricings)
=> new ActivePricingKey
{
InstitutionId = key.InstitutionId,
SubmissionTypeId = key.SubmissionTypeId,
FeeTypeId = key.FeeTypeId,
ActiveFrom = pricings.Max(pricing => pricing.ActiveFrom)
}
);
IQueryable<Pricing> filteredActivePricingQuery = filteredActivePricingKeyQuery
.Join(
Context.Pricings,
(ActivePricingKey activePricingKey)
=> new
{
activePricingKey.InstitutionId,
activePricingKey.SubmissionTypeId,
activePricingKey.FeeTypeId,
activePricingKey.ActiveFrom
},
(Pricing pricing)
=> new
{
pricing.InstitutionId,
pricing.SubmissionTypeId,
pricing.FeeTypeId,
pricing.ActiveFrom
},
(ActivePricingKey activePricingKey, Pricing pricing) => pricing
);
ICollection<Pricing> pricings = await filteredActivePricingQuery.ToListAsync();

Conditional WHERE clause on an Entity Framework context

objRecord = await _context.Persons
.Where(tbl => tbl.DeletedFlag == false)
.ToListAsync();
This is the EF code I've got which successfully gets all the records from the Person table where DeletedFlag is false.
I want to add another where criteria that if a surname has been passed in, then add the extra where clause
.Where(tbl => tbl.Surname.Contains(theSurname))
I've tried IQueryable and some other options but can't figure out how to do the equivalent of
string theSurname = "";
objRecord = await _context.Persons
.Where(tbl => tbl.DeletedFlag == false)
if ( theSurname != "") {
.Where(tbl => tbl.Surname.Contains(theSurname))
}
.ToListAsync();
which obviously doesn't work as you can't put an if statement in an EF call.
I can add a criteria afterwards that limits objRecord, but I don't want to retrieve all the records, then cut it down, I'd rather only get the records I need.
You can combine conditions in the Where method by just adding tbl.Surname.Contains(theSurname) so your final query will look like below:
objRecord = await _context.Persons
.Where(tbl => tbl.DeletedFlag == false &&
tbl.Surname.Contains(theSurname))
.ToListAsync();
You have to apply logical AND (&&) with the existing condition in Where clause i.e. tbl.Surname.Contains(theSurname);
So your query would be
.Where(tbl => tbl.DeletedFlag == false && tbl.Surname.Contains(theSurname));

How to access the items inside the result of my query?

here is a snippet of my code:
using (var uow = new UnitOfWork())
{
//ItemType itself
ItemType itemType1 = uow.ItemTypeRepository.Get(i => i.Name == "ServerTypeList").FirstOrDefault();
Assert.IsTrue(itemType1.ID != null);
var itemType2 = uow.ItemTypeRepository.Get(i => i.Name == "ServerTypeList", orderBy: o => o.OrderBy(d => d.Name), includeProperties: "Items");
//itemType2[0].
...
I am trying to list all the items inside itemType2 ("Get" method returns an IEnumerable):
What's wrong with itemType2.First().Items[0]?
This works because itemType2 is a sequence. Based on what you're seeing in the debugger, it has only one element. If it always has only one element, then you should use itemType2.Single().Items[0] instead.
Every element of this sequence has an Items property, which appears to be a list or array or something else that can be indexed, so once you get there, you can index into it, as above, or you can iterate over it:
foreach (var item in itemType2.Single().Items)
{
// Do something with each item in the sequence
}

How to do a search with EF CodeFirst

Currently, to do a search using EF CodeFirst and a repository pattern, based on user input to multiple text boxes on an mvc search view/page, I do something like the following:
public PagedList<Entity1> PlayerUserSearch(Entity1SearchParameters searchParameters, int? pageSize, int? startEntity, Func<Entity1, object> sortOrder, bool sortDesc)
{
IQueryable<Entity1> query = from entities in this.DataContext.Entity1s.Include("Entity2List")
where entities.Entity2List.Any()
select entities;
if (searchParameters.Entity2PrimaryKeyId.HasValue)
query = query.Where(e => e.Id == searchParameters.Entity2PrimaryKeyId.Value);
if (searchParameters.HasStats.HasValue)
{
if (searchParameters.HasStats.Value)
query = query.Where(u => u.Entity2List.Any(e => e.Stat != null));
else
query = query.Where(u => u.Entity2List.Any(e => e.Stat == null));
}
if (searchParameters.Entity2OtherField.HasValue)
query = query.Where(u => u.Entity2List.Any(e => e.Event.Entity2OtherField == searchParameters.Entity2OtherField));
if (searchParameters.Entity2OtherField2.HasValue)
query = query.Where(u => u.Entity2List.Any(e => e.Event.Entity2OtherField2 == searchParameters.Entity2OtherField2));
if (searchParameters.Active.HasValue)
query = query.Where(e => e.Active == searchParameters.Active.Value);
return this.GetPageByStartEntity(pageSize.Value, startEntity.Value, query, sortOrder, sortDesc);
}
The problem with this is that for every time I add on another where that checks the child of Entity1 (Entity2) for a certain field, it takes on a new " AND EXISTS" clause to the sql statement generated, so that it is doing an exists and checking table Entity2 all over again for every different field checked, rather than doing a single EXISTS on Entity in the query, and checking all fields I tacked on to the query (i.e. EntityOtherField1 and EntityOtherField2). I haven't been able to find a better way to do a search based on user inputs than constantly checking for the input being there (add to the search parameters)) and then tacking on a new where to the current query. Can anyone tell me if there is a better way to do this? Thanks!
I think what you're looking for is using Specification Pattern.
var spec = new specification<Entity2>(s => true);
if (searchParameters.HasStats.Value)
{
spec = spec.And(e => e.Stat != null);
}
if (searchParameters.Entity2OtherField2.HasValue)
{
spec = spec.And(e => e.Event.Entity2OtherField2 == searchParameters.Entity2OtherField2);
}
query = query.Where(u => u.Entity2List.Any(spec));
Or I believe you can make it more standard by separating the filter logic and using spec.IsStatisfiedBy method.
A good framework for repository/specification implemetation on Entity Framework can be found here.