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
})
})
Related
I was trying to select data using LINQ
and I have a list called "products" and I want just these items that exist in products list
var Owner = db.Owners
.Where(m => m.ID == id)
.Include(m => m.Products.Where(item1 => products.Any(item2 => item2.ProductID == item1.ProductID)).ToList())
.FirstOrDefault();
but I'm getting this error :
System.ArgumentException: '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'
Include is meant to fetch complete rows of a table, inclusive primary and foreign keys.
Usually it is not efficient to fetch the complete rows of a table. For example, suppose you have a database with Schools and Students. There is a one-to-many relation between Schools and Students: every School has zero or more Students, every Student attends exactly one School, namely the School that the foreign key refers to.
If you fetch School [10] with its 2000 Students, then every Student will have a foreign key SchoolId with a value 10. If you use Include and fetch complete Student rows you will transfer this value 10 over 2000 times. What a waste of processing power!
A DbContext has a ChangeTracker object. Whenever you fetch data without using Select, so if you fetch complete rows, then the fetched rows are stored in the ChangeTracker, together with a Clone of it. You get a reference to the Clone (or the original, doesn't matter). When you change properties of the fetched data, you change the value in the Clone. When you call SaveChanges, the values of all properties of all originals in the ChangeTracker are compared with the values in the Clones. The items that are changed are updated in the database.
So if you fetch School [10] with its 2000 Students, you are not only fetching way more data than you will ever use, but you will also store all these Students in the ChangeTracker together with a Cloned Student. If you call SaveChanges for something completely different (change of the telephone number of the School for instance), then all Students are compared by value property by property with their Clones.
Generic rule:
Whenever you fetch data using Entity Framework, always use Select, and Select only the properties that you actually plan to use. Only fetch complete rows and only use Include if you plan to update the fetched data.
Using Select will also solve your problem:
int ownerId = ...
IEnumerable<Product> products = ...
var Owner = db.Owners.Where(owner => owner.ID == ownerId)
.Select(owner => new
{
// Select only the Owner properties that you actually plan to use
Id = owner.Id,
Name = owner.Name,
// get the Products of this Owner that are in variable products
Products = owner.Products
.Where(product => products.Any(p => p.ProductId == product.ProductId)
.Select(product => new
{
// Select only the Product properties that you plan to use
Id = product.Id,
Price = product.Price,
...
// No need to fetch the foreign key, you already fetched the value
// OwnerId = product.OwnerId,
})
.ToList(),
...
})
.FirstOrDefault();
I used automatic types (new {...}). If you really want to create Owner and Properties, use:
var Owner = db.Owners.Where(...)
.Select(owner => new Owner
{
Id = owner.Id,
...
Products = owner.Products.Where(...).Select(product => new Product
{
Id = product.Id,
...
})
.ToList(),
})
.FirstOrDefault();
Try the following:
var productIds = products.Select(x => x.ProductID);
var Owner = db.Owners
.Where(m => m.ID == id)
.Include(m => m.Products.Where(product => productIds.Contains(product.ProductID))
.FirstOrDefault();
I have existing code to update. I want to join another table called Table3.
Since the query has an include to Table2, I want to add another .Include with a conditional filter and avoid a left join.
When I add the .include with the .where, I can't access t3Id. Intellisense just shows the table, Table3 and not the Id field.
Did I miss a syntax? Thanks.
Table1 has a key called t1Id.
var query = (from e in ctx.Table1
.Include(r => r.Table2.Select(p => p.name))
.Include(rj => rj.Table3).Where(s => s.t1Id == t3Id)
select e).ToList();
Table1 will have the following:
Id name
1 Joe
2 Mary
3 Harry
Table3 will have the following:
t1Id title
3 staff
3 fulltime
Expected Outcome:
1 Joe
2 Mary
3 Harry [{2, staff}, {3, fulltime}]
Since Harry has a record in the mapping table, he will have an array of Table3 rows.
I just noticed your EF comment. The EF property Table1.Table3 should already load the related entities without using a where clause when you use the include.
Try this extension method
public static IEnumerable<TEntity> GetAllIncluding(this params Expression<Func<TEntity, object>>[] includesProperties)
{
return includesProperties.Aggregate<Expression<Func<TEntity, object>>,
IQueryable<TEntity>>(_dbSet, (current, includeProperty)
=> current.Include(includeProperty));
}
To use the function
var data = table1.GetAllIncluding(x => x.Table2, y => y.Table3);
This should already load the related entities without having to filter.
It is better to use Select instead of Include whenever you can. Select will allow you to query only the properties you really plan to use, making the transfer of the selected data from the database management system to your process faster.
For instance, if you query "Schools with their Students", ever Student will have a foreign key with a value equal to the School's primary key. So if you have School 10, you'll now that all its 5000 Students will have a SchoolId with a value 10. It is a bit of a waste to send this same value 10 over 5000 times.
When querying data, always use Select. Only use Include if you plan to update the fetched data.
Your query (in method syntax):
var result = dbContext.Table1
.Where(table1Element => ...) // only if you don't want all elements of Table1
.Select(table1Element => new
{
// only select the table1 elements that you plan to use:
Id = table1Element.Id,
Name = table1Element.Name,
// Select the items that you want from Table 2:
Table2Items = table1Element.Table2
.Where(table2Element => ...) // only if you don't want all table2 elements
.Select(table2Element => new
{
// Select only the properties of table2 you plan to use:
Id = table2Element.Id,
Name = table2Element.Name,
...
// the following not needed, you already know the value:
// Table1Id = table2Element.table1Id, // foreign key
})
.ToList(),
// Table3: your new code:
Table3Items = table1Element.Table3
.Select(table3Element => new
{
// again: only the properties you plan to use
Id = table3Element.Id,
...
// the following not needed, you already know the value:
// Table1Id = table3Element.table1Id, // foreign key
})
.ToList(),
});
You see that it is much easier for the reader to see which properties he gets? If one of the tables is expanded, then the new properties are not fetched in this query, after all: the user apparently didn't need the new properties. They are also not described in the specifications of this function.
Note: Because I used new, my types were anonymous types. You can only use them within your function. If you need to return the fetched data, put the data in a known class:
.Select(table1Element => new Table1Class()
{
Id = table1Element.Id,
Name = table1Element.Name,
...
});
Again: consider not to fill the foreign keys, as you probably won't use them
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);
Let's say I have an entity and a db table named Client.
Client has a foreign key - CountryId that connected to the Countries table.
ClientA, ClientB and ClientC inherit from Client.
I want to list all the clients in my system (including those with null countryId), along with their concrete types (the discriminator value) and the country name.
My query looks something like this:
from client in DbContext.Set<Client>()
from country in DbContext.Set<Country>().Where(x => x.Id == client.CountryId).DefaultIfEmpty()
where !(client is Salon)
select new
{
Id = client.Id,
Name = client.ClientName,
ClientClass = "TODO",
CountryName = country.CountryName
};
My question: How can I select the discriminator value? Note the "TODO" in the code where I'm stuck at the moment..
Thanks!
discriminator column is used internally by Code First and you cannnot read/write its values from an inheritance mapping standpoint.
to get decriminator you have to create custom SQL query
context.Database.SqlQuery<T>("SELECT client.Id, client.ClientName, client.Discriminator, country.CountryName FROM Countries country LEFT JOIN Clients client ON country.Id = client.CountryId WHERE client.Discriminator <> 'salon'").ToList()
I'm currently using EF Code-First, and I'd like to SELECT data from many tables in the database. This query is a customizable query, and hence I can't predict what kind of data I will be retrieving.
At first, when I tried running ctx.Database.SqlQuery<string>(sql, param), I ran into an exception when facing a DateTime value. I'd like to do this without casting it on the server side.
Does anybody have any idea how I can go about doing it? It can be in LINQ, LINQ-SQL, or purely SQL--so long as it gets the job done! Thanks guys...
You will not get it. Linq-to-entities will not make transformation to list of strings. Your best chance is executing normal queries and do conversion and transformation your application.
Argument that you don't know which columns user selects just means you need more dynamic solution - Linq-to-entities is not a good tool for you (except if you try to use Dynamic Linq or build expression trees manually). Use ESQL or SQL directly.
When selecting data from many tables, use anonymous types to encapsulate the properties(fields) you want to select from these tables into a new entity, something like:
var query = _db.Categories.Join(
_db.Products,
c => c.CategoryId,
p => p.CategoryId,
(category, product) =>
new
{
ProductName = product.Name,
CategoryName = category.Name,
ExpiryDate = product.ExpiryDate
});
You can achieve string values by casting your data fields to string in this way:
var query = _db.Categories.Join(
_db.Products,
c => c.CategoryId,
p => p.CategoryId,
(category, product) =>
new
{
ProductName = product.Name.toString(),
CategoryName = category.Name.toString(),
ExpiryDate = product.ExpiryDate.toString()
});