I have several entites involved in one-to-many and many-to-many relationships. Entity framework doesn't expose models for the generated junction tables, so I'm trying to figure out how to use navigation properties to produce the equivalent result to this query:
select p.Name, p.Description, p.Version, p.Filename, f.Name as Platform, p.ReleaseNotesURL
from packages p
inner join Platforms f on (f.ID = p.PlatformID)
inner join PackageGroups pg on (pg.Package_ID = p.ID)
inner join Groups g on (g.ID = pg.Group_ID)
inner join GroupCustomers gc on (gc.Group_ID = g.id)
where gc.Customer_ID = #customerId AND p.IsPublished = 1
SOLVED: Thanks to octavioccl I got the solution I was after:
var request = HttpContext.Request;
var appUrl = HttpRuntime.AppDomainAppVirtualPath;
var baseUrl = string.Format("{0}://{1}{2}", request.Url.Scheme, request.Url.Authority, appUrl);
var items = from s in db.Packages
join p in db.Platforms on s.PlatformID equals p.ID
from g in s.Groups
from c in g.Customers
where c.ID == customer.ID && s.IsPublished
select new
{
Name = s.Name,
Description = s.Description,
Version = s.Version,
PackageURL = baseUrl + "/PackageFiles/" + s.Filename,
Platform = p.Name,
ReleaseNotesURL = s.ReleaseNotesURL
};
return Json(items.ToList(), JsonRequestBehavior.AllowGet);
I don't know the names of your entities and navigation properties but I think you could do something like this:
int id=10;
var items = from s in db.Packages
join p in db.Platforms on s.PlatformID equals p.ID
from g in s.Groups
from c in g.Customers
where c.Id==id && s.Published==1
select new {Name=s.Name,
Description=s.Description,
Version= s.Version,
FileName= s.Filename,
PlatformName=p.Name,
ReleaseNoteUrl=p.ReleaseNoteUrl};
In OnModelCreating try something like this:
modelBuilder.Entity<PackageGroup>()
.HasMany(x => x.Groups)
.WithRequired(x => x.PackageGroups)
.HasForeignKey(x => x.Group_ID)
.WillCascadeOnDelete(false);
modelBuilder.Entity<PackageGroup>()
.HasMany(x => x.Packages)
.WithRequired(x => x.PackageGroups)
.HasForeignKey(x => x.Package_ID)
.WillCascadeOnDelete(false);
Edit: Having defined the relationships described above, you can then use a query like this (psudocode):
var query =
from g in db.groups
from pg in g.packageGroups
from p in pg.packages
where g.Name = "Something" && p.Version = 1
select new { yada yada }
Related
I trying to translate SQL Query to Linq statement:
SELECT f.BarcodeNumber,m.Name, f.Model, SUM(f.Quantity) as FoundedAssetsQty, ISNULL(a.Quantity,0) as AssetQty
FROM [InventoryDB].[dbo].[FoundedAssets] f
join [InventoryDB].[dbo].[PhisicalStockCheckSheets] p on p.ID = f.PhisicalStockCheckSheetId
join [InventoryDB].[dbo].[Inventories] i on i.ID = p.InventoryId
left join [InventoryDB].[dbo].[Assets] a on a.BarcodeNumber = f.BarcodeNumber
join [InventoryDB].[dbo].[Manufacturers] m on m.ID = f.ManufacturerId
where p.InventoryId = 10
group by f.BarcodeNumber, a.Quantity, f.Model, m.Name
I have no idea how to do it. I tried many ways but I fail. Could anyone help me?
I tried to use Linqer, but when I configure the connection it fails, so I write the linq instruction myself. Finally I found the answer. I have not mentioned the relations between entities but it is not important here.
var summary = _context.FoundedAssets.Include(f => f.Manufacturer).
Include(f => f.Asset).
Include(f => f.PhisicalStockCheckSheet).ThenInclude(f => f.Inventory).
Where(f => f.PhisicalStockCheckSheet.Inventory.ID == id).
Select(x => new InventorySummaryModel()
{
BarcodeNumber = x.BarcodeNumber.Value,
ManufacturerName = x.Manufacturer.Name,
Model = x.Model,
AssetsQuantity = x.Asset.Quantity,
FoundedAssetQuantity = x.Quantity
}).ToList();
var groupedSummary = summary.GroupBy(x => x.BarcodeNumber).Select(x => new InventorySummaryModel()
{
BarcodeNumber = x.First().BarcodeNumber,
ManufacturerName = x.First().ManufacturerName,
Model = x.First().Model,
FoundedAssetQuantity = x.Sum(a => a.FoundedAssetQuantity),
AssetsQuantity = x.First().AssetsQuantity
}).ToList();
Maybe exists any easier approach but this one works properly.
I have troubles creating this query in LINQ:
USE Northwind
GO
SELECT emp.FirstName, emp.LastName, tr.TerritoryDescription, reg.RegionDescription
FROM Employees emp
INNER JOIN EmployeeTerritories empt ON empt.EmployeeID = emp.EmployeeID
INNER JOIN Territories tr ON tr.TerritoryID = empt.TerritoryID
INNER JOIN Region reg ON reg.RegionID = tr.RegionID
This is my current creation:
var query = await context
.Employees
.Select(x => new
{
x.FirstName,
x.LastName,
TerritoryId = x.EmployeeTerritories. //cannot access properties
})
.ToListAsync();
But i can't easily access EmployeeTerritories properties, since it's not 1:1 relationship. I accept both clues and full solution to this problem.
Edit
So this is what i currently have:
var query = await context
.Employees
.Select(x => new
{
x.FirstName,
x.LastName,
TerritoryDescription = x.EmployeeTerritories
.Select(et => et.Territory.TerritoryDescription)
.ToList(),
RegionDesicription = x.EmployeeTerritories
.Select(et => et.Territory.Region.RegionDescription)
.ToList()
})
.ToListAsync();
Is there a way to optimize it? RegionDescription is now a list that contains one element, but i don't know how to do it the better way.
Try something like this (assuming you have corresponding relations):
var query = await context
.Employees
.Select(x => new
{
x.Employee.FirstName,
x.Employee.LastName,
TerritoryDescription = x.EmployeeTerritories
.Select(et => et.Territory.TerritoryDescription)
.ToList(),
})
.ToListAsync();
UPD
To flatten in your particular case you can use solution posted by #dhrumil shah(it is more generic one) or try something like that, if you have EmployeeTerritories set up in your context :
var query = await context
.EmployeeTerritories
.Select(et => new
{
et.Employee.FirstName,
et.Employee.LastName,
et.Territory.TerritoryDescription,
et.Territory.Region.RegionDescription
})
.ToListAsync();
(from emp in context.Employees
join empt in context.EmployeeTerritories
on emp.EmployeeID equals empt.EmployeeID
join tr in context.EmployeeTerritories
on empt.TerritoryID equals tr.EmployeeID
join reg in context.Region
on reg.RegionID equals tr.RegionID
select new {
emp.FirstName,
emp.LastName,
tr.TerritoryDescription,
reg.RegionDescription
}).ToList();
I have two tables Clients and ClientEvaluations, those tables are connected through a foreign key. Each ClientEvaluation has FK to a single Client Entity.
Now i need to query all clients with their last evaluation, no more than one valuation per client. Note that each evaluation has a date.
This code here achieves that in SQL.
SELECT C.Id, MAX(E.EvaluationDate) FROM [dbo].[Clients] as C
JOIN [dbo].[ClientEvaluations] AS E ON E.ClientId = C.Id
GROUP BY C.Id
I have also tried this but the problem with what I'm trying to achieve is that i need to get back from this query the Client entity properties as well.
var lastEvaluations = _db.ClientEvaluations.GroupBy(x => x.ClientId, (x, y) => new { ClientId = x, EvaluationDate = y.Max(z => z.EvaluationDate), }).ToList();
But the query here of course only returns the ClientId and the date, how can i include the whole client entity?
I hope you have configured _dbContext correctly. Then you can use include to do the join operation.
var results = _dbcontext
.Clients
.Include(x => x.ClientEvaluations) //join
.GroupBy(y => y.Id) // group by
.Select(z => new
{
Id = z.Key.Value,
Max = z.Max(x => x.EvaluationDate),
}).ToList();
Or
var results = from c in _dbcontext.Clients
join e in _dbcontext.ClientEvaluations
on c.Id equals e.ClientId
group c by c.Id into cg
select new
{
Id = Id = cg.FirstOrDefault().Id,
Max = cg.Max(x => x.EvaluationDate),
}).ToList();
I am trying to do a join inside a groupjoin using Linq. Here's the scenario - for each lineitem in an order, I want to get some attributes from the variant for that line item(such as variant options and imageid which are stored in variant and product databases). i want to join lineitem, variant and product databases to get these extra information for the lineitem and then groupjoin this lineitem with the order table. i have done this and got the desired result using two queries but I am not sure how many database queries are run. And in the first case(ie in the join, a limit cannot be specified so I just want to make sure that the first iqueryable in the code below is evaluated only when .ToListAsync() is called). Is this the right way to do it? Any help would be greatly appreciated!
public async Task<dynamic> GetOrdersAsync(User user, int pageNumber = 1)
{
var perPage = 10;
var lineItemAndVariant = from l in _context.LineItems
join v in _context.Variants
on l.EcomPlatformVariantId equals v.EcomPlatformVariantId
join p in _context.Products
on v.ProductId equals p.Id
where p.UserId == user.Id
select new
{
l.Id,
l.OrderId,
l.Price,
l.Quantity,
l.EcomPlatformLineItemId,
l.EcomPlatformVariantId,
l.Title,
ImageId = v.ImageId ?? p.ThumbnailId, // for adding image
p.MyVendor,
p.MyVendorId,
v.Option1,
v.Option2,
v.Option3,
p.VariantOption1Label,
p.VariantOption2Label,
p.VariantOption3Label
};
var orders = await _context.Orders
.Where(o => o.UserId == user.Id)
.OrderByDescending(o => o.Id)
.Skip(perPage * (pageNumber - 1)).Take(perPage)
.GroupJoin(lineItemAndVariant, o => o.Id, l => l.OrderId,
(o, lineItems) => new
{
o.Id,
o.EcomPlatformOrderId,
o.CustomerEmail,
o.FirstName,
o.LastName,
o.Address1,
o.Address2,
o.City,
o.Company,
o.Country,
o.Currency,
o.OrderNumber,
o.Phone,
o.Province,
o.TotalPrice,
o.UserId,
o.Zip,
o.CreatedAt,
LineItems = lineItems
})
.AsNoTracking().ToListAsync();
return orders;
}
Suppose I have this data structure in my db.
Parent_table
ParentID
someOtherProp
having 1:N relation with Child_table as shown below
ChildID
ParentID
This Child_table further has a child table with 1:N relationship as SubChild_table
SubChildID
ChildID
Now, I have SubChildID. How can I get access to Parent_table's someOtherProp? I tried with .Include(), but I am really not sure how to write. So far I have something like this:
var foo = _db.Parent_table
.Include(c => c.Child_table
.Select(d => d.SubChild_table
.Where(f => f.SubChildID == subChildID)))
.Select(r => r.someOtherProp)
.SingleOrDefault();
The error I get is:
The Include path expression must refer to a navigation property
defined on the type. Use dotted paths for reference navigation
properties and the Select operator for collection navigation
properties. Parameter name: path
You don't need .Include unless you want to return the included properties. You're free to access all properties of all objects in the hierarchy while constructing the query.
So, I think what you want is:
var foo = (from p in _db.Parent_table
join c in _db.Child_table on p.ParentId equals c.ParentId
join s in _db.SubChild_table on c.ChildId equals s.ChildId
where s.SubChildId == subChildId
select p.someOtherProp).SingleOrDefault();
Providing you have the foreign key relationships set
var foo = _db.SubChild_tables.Where(sc => sc.SubChildId == id).Single().Child_table.Parent_Table.SomeOtherProp;
A guess ..
var foo = from pt in Parent_table
join ct in Child_table
on pt.ParentID equals ct.ParentID
join sct in SubChild_table
on ct.ChildID equals sct.ChildID
where sct.SubChildID == "YourValue"
select new {
pt.SomeOtherProp
};
Or
var foo = Parent_table
.Join(Child_table, p => p.ParentID, c => c.ParentID, (p, c) => new { p, c })
.Join(SubChild_table, sc => sc.c.ChildID, c => c.ChildID, (sc, c) => new { sc, c })
.Where(sc.SubCHildID == "YourValue")
.Select(m => new {
m.p.someOtherProp
});