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()
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();
IN EF6, i have an entity Customer, with a navigation property to entity Address. Address entity contains a property "City".
I can eager load the Address entity while getting all Customers like this:
_dbSet.Customers.Include(customer => customer.Address);
This gives me all the customers, with all the Address properties eager loaded.
Of course this works fine, but the only thing i need from the Address table is the field "City", and it does not feel good to fetch all the address properties from the persistent data store (SQL Server) while not needing them.
I tried the following:
_dbSet.Customers.Include(customer => customer.Address.City);
...but this gives me a runtime exception:
An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll
Additional information: A specified Include path is not valid. The EntityType 'MyModel.Address'does not declare a navigation property with the name 'City'.
I understand this, since City is just a field, and not a relation to another table / entity.
But is there another way to accomplish what i want, or is it best practice to just include the whole Address entity, even if i only need the city field???
What i want is that i can use myCustomer.Address.City, without having an extra query to the database, but for examle when i use myCustomer.Address.Street, the Street property is not eager loaded, and should be additionally fetched from the database...
Select only the properties you want, EF will only load what's needed.
var query = _dbSet.Customers.Include(customer => customer.Address);
var data = query.Select(c => new { Customer = c, City = c.Address.City });
If you are really set on using the same entity throughout your code base, then you could get around the issue using something similar to what Stef proposed:
var query = _dbSet.Customers.Include(customer => customer.Address);
var data = query
.Select(c => new { Customer = c, City = c.Address.City })
.ToList() //executes the IQueryable, and fetches the Customer and City (only) from the DB
.ForEach(x => x.Customer.Address = new Address { City = x.City })
.Select(x => x.Customer)
.ToList();
I am very much in favour of DTOs and not using entity objects in the whole code base, but the above will give you a list of Customers which have Address objects with only the City field populated. Obviously, I make the assumption that your objects have public setters, which entity objects typically do have.
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
})
})
I am creating App using Entity Framework.I have loaded the Database model from Database.here In the Course table Department is the Navigation Property,DepartmentID is the foreign key.I have created a gridview and set its Datasource to Course Table,but I want to see the Deaptment Name instead of department ID.I can use the Navigation properties like Department.count but how can I use the navigation property to get data (Department name) from Department Table.
Any one who can help me with this
THIS IS MY CODE
var result = (from o in ctx.Courses.Include("Department")
where o.Title == TextBox2.Text
select o
).First();
//GridView4.DataSourceID="";
GridView3.DataSource=result;
GridView3.DataBind();
If I dont use the First Function then i can't access the Department Name attribute,If i use the First() It say that
Data source is an invalid type. It must be either an IListSource, IEnumerable, or IDataSource.
please tell me how i can solve it?
I think Products and Categories tables in Northwind are similar than your need. I would write a query like this:
var ctx = new NorthwindEntities();
var query = from prod in ctx.Products
where prod.ProductName.StartsWith("C")
select new { prod.ProductName, prod.UnitPrice, prod.Category.CategoryName };
var result = (from c in dbContext.Course
select c).First();
if(!result.Department.IsLoaded)
{
result.Department.Load(); //this will load the course.Department navigation property
}
//Assuming you have 1 - 1 relationship with course to department
string departmentName = result.Department.Name;
or if you have 1 - M relationship with the department then:
foreach(Department d in result.Department)
{
Console.WriteLine(d.Name);
}
EDIT:
Instead of trying to load Department do the following
if(!result.DepartmentReference.IsLoaded)
{
result.DepartmentReference.Load()
}
How to: Explicitly Load Related Objects
I have a small ASP.NET MVC application with the following entity objects:
Person
PersonId
Name (string)
FirstName (string)
Country (Country)
Country
CountryId
Name
I can add and delete the entity's this works fine. I can also update name, firstname.
But how can i update the country property with another country.
i was trying
p.Country = (from c in db.Country
where c.CountryId == countryId
select c).First();
but this fires an exception {"An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key."}"
even before i call SaveChanges on the datacontext.
Can someone explaind how i can update this property?
kind regards
Dieter
Is db your context? You should be able to do:
p.Country = ctx.Country.First(c => c.CountryId == countryId);
Or, if you don't want to query the database to get the foreign key entity you can also use an EntityKey to the same effect:
p.CountryReference.EntityKey = new EntityKey("MyDb.Country", "CountryId", countryId);
Code similar to this has worked for me when updating navigation properties.
Country country = new Country{CountryId = theNewCountryID };
db.AttachTo("Countries", country);
p.Country = country;
db.Savechanges();
Create a stub of the new country then attach it to the countries EntitySet, assign the new country to your Entity's navigational country property and call SaveChanges().
Use your country EntitySet name in the AttachTo call.
This is what worked for me. In the model:
<%= Html.Textbox("Country.CountryId", Model.Countries) %> // I'm using a form model view
In the controller:
Person originalPerson = (from p in db.PersonSet
where p.PersonId == updatedPerson.PersonId
select p).First();
Country country = (from c in db.CountrySet
where c.CountryId == updatePerson.Country.CountryId
select c).First();
db.Attach(country);
originalPerson.Country = country;
db.ApplyPropertyChanges(originalPerson.EntityKey.EntitySetName, updatedPerson);
db.Savechanges();
I used the second solution and I got no exception, but the CountryId wasn't changed in the database even after I called AcceptChanges.