Retrieving entities from a one to many relationship (Odata) - c#

Summarizing, I have two main tables: Company and Employees, with a one-to-many relationship between them: employees belongs to a company.
The Company entity has a property called Employees, which allows to get the employees who belongs to the specific Company.
If I type in the browser this URL, it works and I get an employees list:
http://domain.com/DynamicsNAV80/OData/Company('whatever')/Employees
Now, I want to retrieve the employees using a Linq query, how can I do it?
I have tried this:
var dataServiceQuery = (DataServiceQuery<Company>)from comp in _context.Company.Expand(comp => comp.WhseEmployee)
where comp.Name == "whatever"
select comp.WhseEmployee;
But this is not working for me.

What does that query return, an error or just not the data your looking for? Im not sure if the syntax for querying Odata is different but this is how i would do it any other time.
var dataServiceQuery = from comp in _context.Company.Expand("WhseEmployees")
where comp.Name == "whatever"
select comp;

Which version of OData are you using?
If it is V4. You can try following code.
var employees = _context.Company.ByKey("whatever").WhseEmployee;
Please refer to Client Delayed Query
If it is V3. You need to query the company first, and then use LoadProperty to send request to /Company('whatever')/WsheEmployee.
var company = _context.Company.Where(c=>c.Name="whatever").First();
dsc.LoadProperty(company, "WsheEmployee");

Finally I could do with this query:
var dataServiceQuery = (DataServiceQuery<WhseEmployee>)_context.Company.Where(c => c.Name == companyName)
.SelectMany(c => c.WhseEmployee);

Related

C# LINQ, script with JOIN and GroupBy throwing exception

I am working on the LINQ script that is on .NET CORE 5 platform along with Entity Framework Core 5.0.8
The script simple left join along with group but getting exception, If don't apply group then I can see result... not sure what I am missing from the puzzle
exception
could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'
code
var a1 =
(from site in db.Sites
join machine in db.Machines on site.SiteId equals machine.SiteId into sm
from siteMachines in sm.DefaultIfEmpty()
where site.SiteId == SiteId
group siteMachines by site into groupedSiteMachines
select new
{
listedSite = groupedSiteMachines.Key,
SiteMachines = groupedSiteMachines.FirstOrDefault() == null? null : groupedSiteMachines
}
).ToList() ;
You cannot get first element of grouped items in LINQ to Entities. Consider to rewrite query in the following way:
var query =
from site in db.Sites
where site.SiteId == SiteId
from siteMachines in db.Machines.Where(machine => site.SiteId == machine.SiteId)
.Take(1)
.DefaultIfEmpty()
select new
{
listedSite = site,
SiteMachines = siteMachines
};
So you have Sites and Machines, and there is a one-to-many relation between Sites and Machines: every Site has zero or more Machines on it, and every Machine is on exactly one Site, namely the Site that the foreign key SiteId refers to.
It seems to me, that you have a value siteId, and you want the Site with this value for primary key and all Machines on this Site.
Whenever you have a one-to-many relation, like Schools with their zero or more Students, Customers with their zero or more Orders, or in your case, Sites with their Machines, consider to use one of the overloads of Queryable.GroupJoin
int siteId = 42;
var siteWithItsMachines = dbContext.Sites
// keep only the site with siteId
.Where(site => site.Id == siteId)
// To get the Machines on each Site, do a GroupJoin:
.GroupJoin(dbContext.Machines,
site => site.Id // from each Site take the primary key
machine => machine.SiteId, // from each Machine take the foreign key
// parameter resultSelector: from every Site with all Machines on this Site
// make one new object:
(site, machinesOnThisSite) => new
{
// Select the Site properties that you plan to use:
Id = site.Id,
Name = site.Name,
Location = site.Location,
...
Machines = machinesOnThisSite.Select(machine => new
{
// select the Machine properties that you plan to use:
Id = machine.Id,
Type = machine.Type,
...
// not needed, you already got the value
// SiteId = machine.SiteId,
})
.ToList(),
});
For efficiency I don't select the complete Site nor the complete Machine, but only the properties that I plan to use.
In .NET 5, GroupBy isn't translated to SQL query and you should use AsEnumerable or .ToList before GroupBy statement.
First you should read data from the SQL without any GroupBy statement and when data receive in your memory, use the GroupBy.
Microsoft Reference

Better way to join tables with Entity Framework

Good morning,
I have inherited a database with no foreign key relations and the project is such that i have to ignore this major issue and work around it. Obviously this eliminates some of the cooler features of Entity Framework providing me related entities automatically.
So i have been forced to do something like this:
using (var db = new MyEntities())
{
Entities.Info record = db.Infoes.Where(x => x.UserId == authInfo.User.Id).FirstOrDefault();
//Get all the accounts for the user
List<Entities.AcctSummary> accounts = db.AcctSummaries.Where(x => x.InfoId == record.Id).ToList();
//Loop through each account
foreach (Entities.AcctSummary account in accounts)
{
//pull records for account
List<Entities.Records> records= db.Records.Where(x => x.AcctSummaryId == account.Id).ToList();
}
}
If there a better way to join the "record" and "accounts" Entities, or perhaps a more efficient way for getting "records" in a single query?
TIA
Are you just looking for the .Join() extension method? As an example, joining Infoes and Accounts might look like this:
var accounts = db.Infoes.Join(db.Accounts,
i => i.Id,
a => a.InfoId,
(info, account) => new { info, account });
This would result in accounts being an enumeration of an anonymous type with two properties, one being the Info record and the other being the Account record. The collection would be the full superset of the joined records.
You can of course return something other than new { info, account }, it works just like anything you'd put into a .Select() clause. Whatever you select from these joined tables would be what you have an enumeration of in accounts. You can further join more tables by changing .Join() extensions, returning whatever you want from each.

How to query Entity Framework objects where select is a list

I am trying to do something simple, but I can't figure it out. Using EF6, I have 2 tables which are associated by an intermediate associative table, giving a many-to-many relationship:
I want to query all users who belong to a list of families. So I get an array of families:
var db = new MyProjectEntities();
User user = GetUserById((int)HttpContext.Current.Session["CurrentUserId"]);
var families = db.Users.Where(u => u.UserId == user.UserId).First().Families.ToArray();
Then I want to query all users belonging to these families:
var users = db.Users.Where(u => families.Contains(u.Families));
But I get this error:
Instance argument: cannot convert from 'Database.Family[]' to 'System.Linq.IQueryable>'
Thanks in advance.
You can use Any method:
var users = db.Users.Where(u => u.Families.Any(fam => families.Contains(fam))).AsEnumerable();
I think this is what you're searching for.

Nhibernate Subquery - Could not resolve property of type x

I need some help figuring out a query which should filter based on one to many relationship. I have a tables 'Product' and 'ProductType'. Where 'Product' has many 'ProductType'. These models are working great I just can't figure out how to filter 'Product' records based on 'ProductType' records.
In this example I would like to select all a 'Product' where Product.Name == "somename" && Product.Version == "9.6". I want to filter these results where ProductType.Type == "someType".
The following is throwing an exception:
could not resolve property: ProductType of: my.namespace.ProductType
I am using examples from Ayende's blog: Query Examples
var product = _session.CreateCriteria<Product>()
.Add(Restrictions.Eq("Name", "somename"))
.Add(Restrictions.Eq("Version", "9.6"))
.Add(Subqueries.PropertyIn("Id",
DetachedCriteria.For<ProductType>()
.SetProjection(Projections.Property("Product.Id"))
.CreateCriteria("ProductType")
.Add(Restrictions.Eq("Type", "someType"))
)).List<Product>().SingleOrDefault();
Am I close? Can someone offer me some help? Thanks!
Edit
This has me close. If I remove the second CreateCriteria I get back a product that has the Types populated. As soon as I add the join back.. I get 0 results.
var products = _session.CreateCriteria("Product")
.Add(Restrictions.Eq("Name", "somename"))
.Add(Restrictions.Eq("Version", "9.6")) //This works
.CreateCriteria("Types", "productTypes", JoinType.LeftOuterJoin)
.Add(Restrictions.Eq("Type", "typename")).List<Product>();
return products.FirstOrDefault();
Very close - you just need to tell CreateCriteria how to get to ProductType. For example:
var product = _session.CreateCriteria<Product>()
.Add(Restrictions.Eq("Name", "somename"))
.Add(Restrictions.Eq("Version", "9.6"))
.Add(Subqueries.PropertyIn("Id",
DetachedCriteria.For<ProductType>()
.SetProjection(Projections.Property("Product.Id"))
.CreateCriteria("Product.ProductType", JoinType.InnerJoin)
.Add(Restrictions.Eq("Type", "someType"))
))
.List<Product>()
.SingleOrDefault();

Data services - find entity by child value - retrieve some properties only

I'm using WCF Data services with Entity Framework 4.1
I have 2 tables with many to many relationship. I'm trying to get an entity based on a child value.
Table 1 (Suppliers) -> ID (PrimaryKey) ,Name ,isEnabled ,Timestamp
Table 2 (Categories) -> RGID (PrimaryKey) , Name, etc....
I've tried to to write the URI like this. My goal is to retrieve all Suppliers - Name and Timestamp - that has a Category with RGID = 3.
so far I've done
http://localhost/joybaservice/joybadataservice.svc/Categories(3)?$expand=Suppliers
now I'm left with the task of selecting the properties I want. Name + Timestamp.
Thank you
Use $select query option. For example with your query above using the expand, let's say I want to get the Name property from the Category, the Name property from the Suppliers and the Timestamp property from the Suppliers. The query could look like:
~/Categories(3)?$expand=Suppliers&$select=Name,Suppliers/Name,Suppliers/Timestamp
If you want to do this using the LINQ on the client (using WCF Data Services Client library) then you can't add $select (or any other $ query option) using the AddQueryOption, instead you need to express the intent of the query using LINQ (that's what it's for afterall). So for example the above might look like this (depends on the layout of your classes though).
ctx.Categories
.Where(c => c.ID == 3)
.Select(c => new Category()
{
Name = c.Name,
Suppliers = c.Suppliers.Select(s => new Supplier()
{
Name = s.Name,
Timestamp = s.Timestamp
})
})

Categories

Resources