How to query child tables values - c#

I have 2 tables: POHeader and PODetail. I want to return all POHeaders that have an associated PODetail.ItemId = intItemId. How can I do this in LINQ?
This is what I've tried.
First I have a method in my Repository that uses the Include parameter to include the PODetails:
public IQueryable<POHeader> SearchForWithDetails(int intFacilityId)
{
return DbSet.Include("PODetails").Where(x => x.FacilityId == intFacilityId);
}
Then the result of that gets passed to:
public IQueryable<POHeader> SearchForPODetailsByItemId(IQueryable<POHeader> poHeaders, int intItemId)
{
//This returns a type of PODetail not POHeader
var q = poHeaders.SelectMany(c => c.PODetails).Where(c => c.ItemId == intItemId);
//In this case, I can't figure out the syntax :(
var p = from poHeader in poHeaders
let filteredPOs = from poDetail in poHeader.PODetails
where poDetail.ItemId == intItemId
select ????
return p;
}
What is the correct way to do this?
Also, I can foresee needing 2 results of this:
just return a IQueryable
return a joined table result.

Try this;
var result = poHeaders.Where(e => e.PODetails.Any(a => a.ItemId == intItemId));

Assuming your a Header->Detail is a 1-to-many relationship, and Detail has a navigation back to it's header called .Header:
public IQueryable<POHeader> SearchForPODetailsByItemId(IQueryable<POHeader> poHeaders, int intItemId)
{
var headersForThisItem = poHeaders.SelectMany(pod => pod.PODetails).Where(pod => pod.ItemId == intItemId)
.Select(pod=> pod.Header).Distinct();//.Distinct to eliminate duplicates when 2 Details have the same header. Not necessary if ItemId filter naturally provides distinct results.
return headersForThisItem ;
}
Untested, but I think that will give you what you want.

Related

How to Link two IDs from different classes in MVC5 to display certain information

I am trying to link up the RestaurantId in the RestaurantReservationEventsTbl with the RestaurantID in the RestaurantTbl to display reservations that are only made for the currently logged in restaurant.
I am receiving the following error in my code operator == cannot be applied to operands of type int and iqueryable int
Here is what I am doing in my home controller
var RestaurantIDRestaurantTbl = from r in db.Restaurants select r.RestaurantID;
//var listOfRestaurantsReservations = db.RestaurantReservationEvents.ToList();
var listOfRestaurantsReservations = db.RestaurantReservationEvents.Where(x => x.RestaurantID == RestaurantIDRestaurantTbl).ToList();
//return View(restaurants.Where(x => x.RestaurantEmailAddress == UserEmail).ToList());
//create partial view called _RestaurantReservation
return PartialView("_RestaurantReservations", listOfRestaurantsReservations);
You have to change your code to materialize the restaurantIds like this:
var RestaurantIDRestaurantTbl = (from r in db.Restaurants
select r.RestaurantID).ToList();
Then you may change the code as below for the comparison to work:
var listOfRestaurantsReservations = db.RestaurantReservationEvents.Where(x => RestaurantIDRestaurantTbl.Contains(x.RestaurantID)).ToList();
Anyway this is not the best solution. I will write another example for you, just try this example if it is working or not and let me know for the result.
I would considering changing the code as below to be much more efficient:
var listOfRestaurantsReservations = (from r in db.Restaurants
join e in db.RestaurantReservationEvents
on r.RestaurantID equals e.RestaurantID
//where r.RestaurantID == something //if where condition needed
select e).ToList();
If your tables are not connected with foreignkeys please consider to read this documentation here to make a better structure of the tables since they are related to each-other.
If your tables are related as in documentation article you might have something like that:
var RestaurantIDRestaurantTbl = db.Restaurants.SingleOrDefault(x => x.RestaurantID == something);
if(RestaurantIDRestaurantTbl != null)
{
var listOfRestaurantsReservations = RestaurantIDRestaurantTbl.RestaurantReservationEvents.ToList();
}
{
// This will give you a list of IDs
var RestaurantIDRestaurantTbl = db.Restaurants
.Select(p => p.RestaurantID)
.ToList();
// Using .Any() is a better choice instead of .Contains()
// .Contains is used to check if a list contains an item while .Any will look for an item in a list with a specific ID
var listOfRestaurantsReservations = db.RestaurantReservationEvents
.Where(p => RestaurantIDRestaurantTbl.Any(r => r.pRestaurantID == p))
.ToList();
}

Why is LINQ OrderByDescending not working with a view composed of a union between two tables?

I have a view in my SQL Server database that is a UNION ALL of two tables. When I retrieve all the rows from the view for a specific member, this is my call.
public IEnumerable<MemberTransaction> GetMemberTransactions(string socSecNo)
{
var query = Manager.MemberTransactions
.Where(m => m.SocSecNo == socSecNo)
.OrderByDescending(m => m.TranDate);
var results = query.Execute();
return results;
}
I call the Get method from my code and assign the returned collection to the datasource propery of a grid. The rows are not displayed as per the order by statement in the Get method. they are displayed in random order.
I am forced to do this to get the rows to display properly.
IEnumerable<MemberTransaction> transactions = FetchMemberTransactions(currentMember.SocSecNo);
MemberTransactionBS.DataSource = transactions.OrderByDescending(t => t.TranDate);
MemberTransactionsGrid.Refresh();
public IEnumerable<MemberTransaction> FetchMemberTransactions(string socSecNo)
{
var transactions = Repository.GetMemberTransactions(socSecNo);
return transactions;
}
Is there an issue with returning rows from a union? Why is the OrderByDescending not working as expected?
If the order is being returned correctly from the database (test this first like other answers sugest) try returning an IOrderedEnumerable<MemberTransaction> instead of IEnumerable<MemberTransaction>.
You said First Way doesn't work but it worked on me! So try Second Way please.
// First Way
public List<MemberTransaction> GetMemberTransactions(string socSecNo)
{
var orderedListOfData = Manager.MemberTransactions
.Where(m => m.SocSecNo == socSecNo)
.OrderByDescending(m => m.TranDate).ToList();
return orderedListOfData;
}
// Second Way
public List<MemberTransaction> GetMemberTransactions(string socSecNo)
{
var orderedListOfData = (from m in Manager.MemberTransactions
where m.SocSecNo == socSecNo
orderby m.TranDate descending
select m).ToList();
return orderedListOfData;
}

What is the best way to return a projected query to add filter afterward

I need to make a query that return all items with the current price and the current reduction if any.
I tried a few solutions but none seem to work or respect the patterns as i understand them.
The dynamic solution:
I tried to return the data as a dynamic that would be an IQueryable where T would be (Item, CurrentItemPrice, CurrentItemStateIfAny)
public ItemRepository(CoconutEntities context) : base(context){}
public dynamic GetAllCurrentItems(){
var items = (from item in context.Items
select new {
Item = item,
CurrentItemPrice = item.ItemPrices.Where(x => item.ItemPrices.Max(y => y.EffectiveDate) == x.EffectiveDate),
CurrentItemState = item.ItemReductions.Where(x => x.StartDate <= DateTime.Now && DateTime.Now <= x.EndDate)});
return items;
}
But when i try this and i need to add filter, i can't add them the way i was expecting.
public dynamic GetCurrentItems(string filter = "", int categoryId = 1) {
dynamic result;
var categoryServices = new CategoryServices();
IEnumerable<int> categoryIdAndChildCategoriesId = categoryServices.GetCategoryIdAndChildsId(categoryId);
if (!string.IsNullOrWhiteSpace(filter))
{
result = this.GetAllCurrentItems().Where(x => ((string)(x.Item.Name)) == filter);
}
else if(categoryId != 1)
{
result = this.GetAllCurrentItems().Where(x => x.Item.ItemCategories.Any(x => categoryIdAndChildCategoriesId.Contains(x.CategoryId)));
}
return result;
}
Solution 2 : I also tried with Tuple where i should have been able to do somthing like this but i can't create Tuples from Linq to Entities if i understood in an other post. I would need to query all the item first, then use linq to object to create my tuples.
Solution 3 : I can create a viewmodel or a new model that would represent the data i need. I know this would work but i don't understand where it would stand between the two. If it is not a view model, this information won't go to the view it an other way to see an item with only the current information.
In short, there are probably many solutions to this problem, but i need help to understand which solution would be the best and why.
As I understood you you want to do as much as possible on the database - that is good. You might achieve that with tuples like that:
public IEnumerable<Tuple<Item,decimal, decimal>> GetAllCurrentItems(Expression<Func<Item, bool>> filterExpression){
using(MyContext context = new MyContext())
{
var items = context.Items
.Where(filterExpression)
.Select(item => new Tuple<Item,decimal, decimal> (
item,
item.ItemPrices.Where(x => item.ItemPrices.Max(y => y.EffectiveDate) == x.EffectiveDate),
item.ItemReductions.Where(x => x.StartDate <= DateTime.Now && DateTime.Now <= x.EndDate)});
return items;
}
}
And calling it like that:
public IEnumerable<Tuple<Item,decimal, decimal>> GetCurrentItems(string filter = "", int categoryId = 1) {
dynamic result;
var categoryServices = new CategoryServices();
IEnumerable<int> categoryIdAndChildCategoriesId = categoryServices.GetCategoryIdAndChildsId(categoryId);
if (!string.IsNullOrWhiteSpace(filter))
{
result = this.GetAllCurrentItems(x => ((string)(x.Item.Name)) == filter);
}
else if(categoryId != 1)
{
result = this.GetAllCurrentItems(x => x.Item.ItemCategories.Any(x => categoryIdAndChildCategoriesId.Contains(x.CategoryId)));
}
return result;
}

How can I get multiple data?

I can show just one customer, I know the problem is because I use FirstOrDefault in my LINQ.
How can get another customer? I still don't understand the concept of IQueryable or IEnumerable.
public int getNota(DateTime dt, int lap)
{
DataClassesPelleDataContext myDb = new DataClassesPelleDataContext();
var nota = (from u in myDb.TBL_TRANSAKSI_SEWA_LAPANGAN_REGULERs
where u.TGL_PEMAKAIAN.Value.Date == dt.Date && u.ID_LAPANGAN == lap
select u.ID_SEWA).FirstOrDefault();
return nota;
}
I dont know for sure what you are trying to achive
But you can use
var notasIds = yDb.TBL_TRANSAKSI_SEWA_LAPANGAN_REGULERs
.Where(u => u.TGL_PEMAKAIAN.Value.Date == dt.Date && u.ID_LAPANGAN == lap)
.Select(n => n.ID_SEWA)
.ToList();
and then loop over the notas with
foreach (var sewaId in notasIds)
{
// to logic here
}
You can also comment .Select() call and get whole objects.
Regars

Adding a where/order by clause to an IQueryable

I have ths function to query a set of records from the DB:
public IQueryable<PointTransactionViewModel> GetPointTransactions(int UserID)
{
return
(
from PointTransaction p in entities.PointTransaction
join ActivityLog a in entities.ActivityLog
on p.TransactionID equals a.TransactionID
where p.UserID == UserID
select new PointTransactionViewModel
{
ID = p.TransactionID,
Balance = p.Balance,
Points = p.Amount,
RelatedActivityID = a.ID,
When = p.When,
Sender = p.SenderUserInfo.CompleteName
}
);
}
I wish to add an additional cause, like this
var entries = GetPointTransaction(1);
return entries.OrderbyDescending.Where( x => x.When >= start && w.When <= end).
( x => x.When);
However, I seem to need to create a new query from the existing one for this to work. But, I have seem this work before without creating a new query, in the code snippet before:
public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize)
{
PageIndex = pageIndex;
PageSize = pageSize;
TotalCount = source.Count();
TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);
this.AddRange(source.Skip(PageIndex * PageSize).Take(PageSize));
}
Does the code above somehow doesn't need a new query to be created for the IQueryable source object? Was a temporary object created?
Edit
It's strange, but to get it to work I have to do the following:
IQueryable<ActivityLogEntry> log = activityRepo.GetPointTransaction(userID).
Where(x => x.PointsEarned == 50);
return log.ToList();
The following will not work:
var log = = activityRepo.GetPointTransaction(userID);
log.Where( x => x.PointsEarned == 50);
return log.ToList();
There is no error message, just that the where clause seems to be ignored (it is also returning all data which PointsEarned is not 50)
Your entries is of IQueryable type, that's enough and you can add any number of clauses before fetching the data, e.g. before calling the ToList() function.
It doesn't execute the SQL code, just an expression tree will be created until you fetch the whole data with one of the existing methods (again, e.g. the ToList() function).
var query = context.Where(x=>x.id == test);
query = query.Where(anotherCondition1);
query = query.Where(anotherCondition2);
...
var result = query.ToList();
it's equal to
var result = context.Where(x=>x.id == test)
.Where(anotherCondition1)
.Where(anotherCondition2)
....
.ToList()
This is called deferred execution, for more details see the MSDN blog post on LINQ and Deferred Execution.
You do need to create a new object. IQueryable is immutable. Don't worry this is how you are supposed to do it. This is how the queries are formed internally. All the extension methods like "Where" don't actually change the object. They just return a new one.
The code that you claim works should not work. The method doesn't even have a type.
i mean you can write this sample :
opportunites = from opp in oppDC.Opportunities
join org in oppDC.Organizations on opp.OrganizationID equals org.OrgnizationID
select new
{
opp.OpportunityID,
opp.Title,
opp.PostedBy,
opp.Address1,
opp.CreatedDate,
org.OrganizationName
};
if(condition)
{
opportunites = opportunites.Where(opp => opp.Title.StartsWith(title));
}
//------Other Condition you need
if(!String.IsNullOrEmpty(title))
{
opportunites = opportunites.Where(.....);
}
if(!String.IsNullOrEmpty(name))
{
opportunites = opportunites.Where(.....);
}
As others have pointed out, you do not need a new object. Your syntax for OrderByDescending is wrong though, you need to specify the key selector.
var entries = GetPointTransaction(1);
return entries.Where(x => x.When >= start && w.When <= end).OrderbyDescending(x => x.When);

Categories