EF Linq - how to select children in same query? - c#

I have Oracle db with EF 5 on top of it.
Lets say I have tables Company and Orders. I have EF corresponding entities where Company has field
List<Orders> Orders.
I dont want to create an association.
I have a query which is IQuerable and I need to fill the Orders for each company using
order.CompanyId == company.Id
I cant wrap my head around this atm.. Any help will be appreciated.
EDIT: not every company has orders. The orders list could be empty.

I would consider using the .Join() method to include the Orders table. I write this from memory, so please forgive syntax errors.
//I only use this bc I don't know the full schema.
class DTOCompany {
public int ID {get;set;}
public List<Orders> Orders {get;set;}
}
public List<Companies> GetCompaniesOrders() {
using (var db = new Entities()) {
return db.Companies
.AsEnumerable() //may not be needed.
.Join(db.Orders,
c => CompanyId,
o => o.CompanyId,
(c,o) => new { Company = c, Orders = o})
)
.Select(co => new DTOCompany() {
ID = co.Companies.CompanyId,
Orders = co.Orders.ToList()
})
.ToList();
}
}

Related

Selecting fields from different table

I have 3 tables. Order, Orderlines and Stores. In Order table has StoreId which is a foreign key from Stores table and In Orderlines table has OrderId a foreign key from Order table. I only need to get the fields Order.OrderId, Order.WarehouseOrderId, Store.StoreName and the count of Orderlines per Order. So currently this is my implementation:
public List<Order> GetOrdersDashboardWithoutStatus1(SearchDashboardCriteria cri)
{
using (var ctx = CreateDbContext())
{
var orders = (from items in ctx.Orders select items);
if (cri.MerchantId != 0)
{
orders = orders.Where(x => x.Store.MerchantId == cri.MerchantId);
}
return orders.Where(x => (DbFunctions.TruncateTime(x.DateImported) >= DbFunctions.TruncateTime(cri.StartDate))
&& (DbFunctions.TruncateTime(x.DateImported) <= DbFunctions.TruncateTime(cri.EndDate)))
.Include(x => x.Store).Include(x => x.OrderLines).ToList();
}
}
Through the currently implementation is working but the performance is very slow and sometimes it gets a Time Out error because a records may have 30K+ orders.
You could try something like this
using (var ctx = CreateDbContext())
{
var orders = (from x in ctx.Orders
where x.Store.MerchantId == cri.MerchantId
&& add your other where condition
select NewDataModel
{
orderId = x.OrderId,
wareHouse = x.WarehouseOrderId,
storeName = x.Store.StoreName
}).ToArray();
}
Anonymous projection or projection to your custom model only retrieve columns that are included in the projection.
Instead of Select * from Orders ... it will be Select OrderId,WareHouseOrderId from Orders
you can also try for paging (Take,skip) if your use case support it.
Similar answer for anonymous projection

Avoiding for loops to bring table related information for grid display

I have the following database schema for which I am developing the screen for it.
I want to display the information in a grid ,for example Desc should be repeated in more than one row and for each row there has to be columns from
tableC,tableB and tableA related information.
Right now I am getting every table in the controller layer using linq "Include" related entities and using many foreach loops I am getting the customTable class created and then binding it to the kendo grid.
foreach(var a in table C)
{
foreach(var b in tableB)
{
CustomTable c = new CustomTable {
tableDesc = b.Desc,
tableBDesc = a.Desc
}
}
}
class CustomTable
{
public string tableDDesc{get;set;}
public string tableBDesc {get;set;}
}
I am thinking is there any better approach for this /Linq syntax to build the customClass in Data access layer itself. Any inputs please?
If you using EF you can do it this way:
_context.TableC.Select(x => x.TableB)
.Select(x => new CustomTable
{
tableDesc = x.TableC.Desc,
tableBDesc = x.Desc
});
This code will generate SELECT with JOIN and return you List of your CustomTable objects.
It will get from TableB only rows that have filled tableC_id field (NOT NULL).
How about using link projects to bring back results as CustomTable class. Something like this
var results = (from tableC in context.TableCs
from tableB in context.TableBs
where tableB.Id == tableC.Id
select new { tableDDesc = tableB.Description, tableBDesc = tableC.Description }
).ToList();
results.ForEach(obj => new CustomTable{tableDDesc = obj.tableDDesc, tableBDesc = obj.tableBDesc});
you can remove the where tableB.Id == tableC.Id if not needed

Deleting selected relations in many-to-many relationship in EF?

In SQL, I have:
[Bands] 1 ---- * [BandsGenres] * ---- 1 [Genres]
In my EF layer, it translates to:
[Bands] * ---- * [Genres]
(BandsGenres is BandId and GenreId)
My problem is, I can't seem to figure out how to delete just the selected relationships I need. Basically, I'm trying to update a list of type Genre for a band. A list of Guids is passed in and I'm able to get those using joins. However, the .Remove() method isn't removing the old genres and adding the new ones. I tried using .Attach(g) and .DeleteObject(g) but that removed the actual Genre (from Genres) instead of just the relationship.
public void UpdateBandGenres(Guid bandId, IEnumerable<Guid> genres)
{
using (var ctx = new OpenGroovesEntities())
{
var newGenres = from g in ctx.Genres
join t in genres on g.GenreId equals t
select g;
//var bandGenres = ctx.Genres.Include("Bands").Where(g => g.Bands.Any(b => b.BandId == bandId));
var bandGenres = ctx.Bands.SingleOrDefault(b => b.BandId == bandId).Genres;
bandGenres.ToList().ForEach(g =>
{
bandGenres.Remove(g);
});
newGenres.ToList().ForEach(g => bandGenres.Add(g));
ctx.SaveChanges();
}
}
How can I delete/add or update my list of genre relationships for a band, given a list of genre IDs? Thanks.
If I understand your problem correcly, the genres collection contains all the Genre objects that should be in the Band genre list as a result of running the UpdateBandGenres method (and not just the list to add or list to delete). In that case the simplest would be removing all the genres from the collection and adding all the genres with the Guids from your genres collection.
First of all, you don't need joins to grab your newGenres:
var newGenres = ctx.Genres.Where(g => genres.Contains(g.GenreId));
Secondly, you need to fetch the Band object, since modifying its Genres collection will tell EF to modify the BandGenres table in SQL:
Band band = ctx.Bands.SingleOrDefault(b => b.BandId == bandId);
After that you can clear the band.Genres collection and add the newGenres on top. As a result you code will look like:
public void UpdateBandGenres(Guid bandId, IEnumerable<Guid> newGenreIds)
{
using (var ctx = new OpenGroovesEntities())
{
List<Genre> newGenres = ctx.
Genres.
Where(g => newGenreIds.Contains(g.GenreId)).
ToList();
Band band = ctx.Bands.Single(b => b.BandId == bandId);
band.Genres.Clear();
newGenres.ForEach(band.Genres.Add);
ctx.SaveChanges();
}
}
Btw, I'd also recommend being consistent with naming your variables - e.g. IEnumerable<Guid> genres might be a bit misleading, since it's actually a collection of Guids, and not a collection of Genre objects. Therefore I named it newGenreIds to be consistent with your Guid bandId variable name.

Query common contacts (self many to many relationship) in entity framework

I have this classic scenario where I have a User table and a Contact table containing only UserId and ContactId columns (so it is a self many to many relationshsip). What I would like is a query that gives me a list of userIds with number of common contacts with the specified User. In plain old SQL I have the following query (contacts of user and user itself is filtered out to get facebook like friend suggestions):
SELECT COUNT(c1.ContactId) as CommonContact, c2.UserId
from Contacts as c1
inner join Contacts as c2 on c1.ContactId = c2.ContactId
Where c1.UserId = #Id AND c2.UserId != #Id
AND c2.UserId NOT IN (SELECT ContactId from Contacts Where UserId = #Id)
Group By c2.UserId
ORDER BY CommonContact Desc
This simple query works great but I can not figure out how to write the same query in LINQ to Entity, because in the Entity Framework model I have User entity that entity have Contact navigation property but the connection table is not there directly....
Thanks a lot for any help...
Didn't have time and try to run it but something like this should work.
public class Test
{
//simulate an IQueryable
private readonly IQueryable<Person> _people = new List<Person>().AsQueryable();
public void FindContactMatchCount(Guid personId)
{
//we'll need the list of id's of the users contacts for comparison, we don't need to resolve this yet though so
//we'll leave it as an IQueryable and not turn it into a collection
IQueryable<Guid> idsOfContacts = _people.Where(x => x.Id == personId).SelectMany(x => x.Contacts.Select(v => v.Id));
//find all the people who have a contact id that matches the selected users list of contact id's
//then project the results, this anonymous projection has two properties, the person and the contact count
var usersWithMatches = _people
.Where(x => idsOfContacts.Contains(x.Id))
.Select(z => new
{
Person = z, //this is the person record from the database, we'll need to extract display information
SharedContactCount = z.Contacts.Count(v => idsOfContacts.Contains(v.Id)) //
}).OrderBy(z => z.SharedContactCount)
.ToList();
}
}

How can I query this hierarchical data using LINQ?

I have 3 kinds of objects: Agency, BusinessUnit and Client (each with their own respective table)
In terms of hierarchy, Agencies own BusinessUnits, and BusinessUnits own Clients.
I have 3 C# POCO Objects to represent them (I usually select new {} into them, rather than use the LINQ generated classes):
public class Agency
{
public IEnumerable<BusinessUnit> BusinessUnits { get; set; }
}
public class BusinessUnit
{
public IEnumerable<Client> Clients { get; set; }
}
public class Client
{
public int NumberOfAccounts { get; set; }
public Decimal AmountOfPlacement { get; set; }
public Decimal AvgBalance { get; set; }
public Double NeuPlacementScore { get; set; }
}
You can see that Agencies contain a list of BusinessUnits, and BusinessUnits contain a list of Clients.
I also have a mapping table called BAC_Map in the database which says which owns which, and it looks something like this:
How can I construct a query, so I can query for and return a list of Agencies? Meaning that, I want each Agency to have its list of BusinessUnit objects set, and I want the list of BusinessObjects to have its list of Clients set.
I can do basic LINQ queries, but this is a little over my head concerning the Map table and the multiple? queries.
How could I construct a method like GetAllAgencies() which would query, for not only all agencies, but populate its BusinessUnits that Agency owns, and the Clients those BusinessUnits own?
Edit: Any tips or info is appreciated. Do I need to do joins? Does this need to be multiple queries to return an Agency list, with its submembers populated?
If you drop all four tables (Agency, BusinessUnit, Client, Map) on the linq to sql designer, and draw relationships from Map to the other three, there will be some useful properties on Map.
//construct a query to fetch the row/column shaped results.
var query =
from m in db.map
//where m.... ?
let a = m.Agency
let b = m.BusinessUnit
let c = m.Client
// where something about a or b or c ?
select new {
AgencyID = a.AgencyID,
AgencyName = a.Name,
BusinessUnitID = b.BusinessUnitID,
ClientID = c.ClientID,
NumberOfAccounts = c.NumberOfAccounts,
Score = c.Score
};
//hit the database
var rawRecords = query.ToList();
//shape the results further into a hierarchy.
List<Agency> results = rawRecords
.GroupBy(x => x.AgencyID)
.Select(g => new Agency()
{
Name = g.First().AgencyName,
BusinessUnits = g
.GroupBy(y => y.BusinessUnitID)
.Select(g2 => new BusinessUnit()
{
Clients = g2
.Select(z => new Client()
{
NumberOfAccounts = z.NumberOfAccounts,
Score = z.Score
})
})
})
.ToList();
If approriate filters are supplied (see the commented out where clauses), then only the needed portions of the tables will be pulled into memory. This is standard SQL joining at work here.
I created your tables in a SQL Server database, and tried to recreate your scenario in LinqPad. I ended up with the following LINQ statements, which basically result in the same structure of your POCO classes:
var map = from bac in BAC_Maps
join a in Agencies on bac.Agency_ID equals a.Agency_ID
join b in BusinessUnits on bac.Business_Unit_ID equals b.Business_Unit_ID
join c in Clients on bac.Client_ID equals c.Client_ID
select new
{
AgencyID = a.Agency_ID,
BusinessUnitID = b.Business_Unit_ID,
Client = c
};
var results = from m in map.ToList()
group m by m.AgencyID into g
select new
{
BusinessUnits = from m2 in g
group m2 by m2.BusinessUnitID into g2
select new
{
Clients = from m3 in g2
select m3.Client
}
};
results.Dump();
Note that I called map.ToList() in the second query. This actually resulted in a single, efficient query. My initial attempt did not include .ToList(), and resulted in nine separate queries to produce the same results. The query generated by the .ToList() version is as follows:
SELECT [t1].[Agency_ID] AS [AgencyID], [t2].[Business_Unit_ID] AS [BusinessUnitID], [t3].[Client_ID], [t3].[NumberOfAccounts], [t3].[AmountOfPlacement], [t3].[AvgBalance], [t3].[NeuPlacementScore]
FROM [BAC_Map] AS [t0]
INNER JOIN [Agencies] AS [t1] ON [t0].[Agency_ID] = [t1].[Agency_ID]
INNER JOIN [BusinessUnits] AS [t2] ON [t0].[Business_Unit_ID] = [t2].[Business_Unit_ID]
INNER JOIN [Clients] AS [t3] ON [t0].[Client_ID] = [t3].[Client_ID]
Here is a screenshot of the results:
alt text http://img411.imageshack.us/img411/5003/agencybusinessunitclien.png
If you are doing this with direct LINQ to SQL, there is no way to do this without some kind of recursion, whether you do it yourself or you hide it behind an extension method. Recursive SQL is very bad (many round trips, many single queries).
There are two options here. One is to pull the entire table(s) with the hierarchy into memory and use LINQ to Objects on it. Leave the "details" tables in SQL. If you have less than several thousand entities, this is probably the most efficient way to go. You can keep a single copy of the table(s) in cache and refresh them when necessary. When you need to fetch more detailed data from the DB for a single record, you can reattach that entity from your cached hierarchy to a new DataContext and fetch it.
The other option is to use a more complex relationship model in your database. Storing parent only by nature demands recursion, but you can use the adjacency list model to construct a single query which can span many levels of inheritance. This will mean your LINQ to SQL queries become less intuitive (querying against Entity.Right and Entity.Left isn't quite as pretty as Parent or Children...) but you can do in one query what might take hundreds or thousands in the literal recursive approach.

Categories

Resources