EF6 eager load single property of related entity - c#

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.

Related

The Include path expression must refer to a navigation property defined on the type (select data using LINQ)

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();

Entity Framework LINQ equivalent of TSQL to include count of child records

Using Entity Framework and LINQ, how might I achieve this TSQL:
SELECT Children.ChildCount, Parent.*
FROM Parent
LEFT JOIN (SELECT ParentID, COUNT(ChildID) AS ChildCount FROM Child GROUP BY ParentID) AS Children
ON Parent.ID = Children.ParentID
Note that this is just a small part of what is already a larger LINQ query that includes other related entities so using a RawSQL query is not an option. Also the Parent table has around 20 columns and I'm hoping not to have to specify each individually to keep the code maintainable.
EDIT: To clarify a couple of things, the model for the output of the query (very simplified) looks something like this:
public class MyEntity
{
public int ID {get; set;}
public string Name {get; set;}
public int ChildCount {get; set;}
// many other properties here including related records
}
So what I'm trying to do is get the ChildCount included in the result of the query so it is included in the EF entity.
You can use a Select Query to project the DB info onto an entity, something like:
var entity = db.Parent.Select(x =>
new MyEntity
{
Id = x.Id,
Name = x.Name,
ChildCount = x.Children
.Select(y => y.ParentId == x.Id)
.Count()
})
.SingleOrDefault(x => x.Id == IDYouNeedToQuery);
}
What this should do is return you 1 instance of your MyEntity class with the Name, ID, and ChildCount properties filled in. Your SQL won't quite match what is generated but this should get you what you want. BTW you can also sub the SingleOrDefault line with a filter of another type, or no filter in which case the entity variable becomes a collection of MyEntity.
For further reading on this technique and how to use AutoMapper to make it super easy to set up, check out this post from Jon P Smith, who literally wrote the book on Entity Framework Core.
Hope this helps.
For anyone who comes across this at a later date, I ended up using AutoMapper as suggested by Nik P (I was already using AutoMapper anyway) to map from db entities to DTOs.
In effect, in my AutoMapper mapping I have:
CreateMap<MyEntity, MyEntityDTO>()
.ForMember(d => d.ChildCount, opt => opt.MapFrom(src => src.ChildEntity.Count()))
Then in my Service layer I call:
IEnumerable<MyEntityDTO> results = await dbContext.MyEntities.ProjectTo<MyEntityDTO>(mapper.ConfigurationProvider).ToListAsync();

Access Object through foreign key c#

I have the following code and would like to know if there is a way to refactor in order to remove duplicated logic.
This results current user with eager loading.
var currentEmployee = RosterContext.Employees
.Where(e => e.User.Id == id)
.Include(e => e.Job.Department).FirstOrDefault();
.
var job = RosterContext.Employees.Where(e=>e.Job.Department.Id == currentEmployee.Job.DepartmentId).ToList();
I created another same context which compares the first line of code to result all employee names who work in same department. My question is, as I am using two linq expression that uses the same context (Employees) am i able to combine both linq queries into one?
It may become a long linq expression but it should serve on getting the current user object followed by comparing user object to get all employees that share the same department id?
It makes sense to try an ORM framework, such as Entity Framework or NHibernate.
ORM framewok will model database FK relationship as a scalar property on one side and vector property (collection) on the other side of the relationship.
For instance Department would have a collection property Jobs, and a Job entity would have a scalar Department property.
DB queries with joins on FK become just dependency property navigation, for example - to access the list of employees in current department you would just return something like employee.Department.Employees - that is, assuming your entities are all loaded (which is rather simple to achieve in EF, using include statement)
In Entity Framework you have the using clause to attach children. So for example in pure EF you could do:
var department = context.Department.Include(d => d.Jobs).First(d => d.DepartmentId == departmentId);
https://msdn.microsoft.com/en-us/library/gg671236%28v=vs.103%29.aspx#Anchor_1
With a repository, you may need to do something like this:
EF Including Other Entities (Generic Repository pattern)
EF Code First supports relationships out of the box. You can either use the conventions or explicitly specify the relationship (for example, if the foreign key property is named something weird). See here for example: https://msdn.microsoft.com/en-us/data/hh134698.aspx
When you've configured your models right, you should be able to access department like so:
var currentUser = _unitOfWork.Employee.GetEmployeeByID(loggedInUser.GetUser(user).Id);
var job = currentUser.Job;
var department = job.Department;
// or
var department = _unitOfWork.Employee.GetEmployeeByID(loggedInUser.GetUser(user).Id).Job.Department;
To show all employees that work in the same department:
var coworkers = department.Jobs.SelectMany(j => j.Employees);
Update
To use eager loading with a single repository class (you shouldn't need multiple repository classes in this instance, and therefore don't need to use Unit of Work):
public class EmployeeRepository {
private readonly MyContext _context = new MyContext(); // Or whatever...
public IList<Employee> GetCoworkers(int userId) {
var currentEmployee = _context.Employees
.Where(e => e.UserId == userId)
.Include(e => e.Job.Department) // Use eager loading; this will fetch Job and Department rows for this user
.FirstOrDefault();
var department = currentEmployee.Job.Department;
var coworkers = department.Jobs.SelectMany(j => j.Employees);
return coworkers;
}
}
And call it like so...
var repo = new EmployeeRepository();
var coworkers = repo.GetCoworkers(loggedInUser.GetUser(user).Id);
You probably would be able to make the repository query more efficient by selecting the job and department of the current user (like I've done) and then the related jobs and employees when coming back the other way. I'll leave that up to you.

How to update related data using entity framework 6

I have an entity Person, which has a related entity Hobby. This is a many to many relationship where a person can have several hobbies, and a hobby can be associated with many people. I want to create a ViewModel that allows new hobbies to be added and/or existing hobbies to be removed from a given Person entity.
The code below works, but I imagine it is rather inefficient.
var newHobbies = new List<Hobby>();
foreach (Hobby hobby in vm.Hobbies)
{
var originalHobby = db.Hobbies.Find(hobby.HobbyID);
newHobbies.Add(originalHobby);
}
originalPerson.Hobbies = newHobbies;
I prefer to do something like this:
var newHobbies = db.Hobbies.Where(x => vm.Hobbies.All(y => x.HobbyID == y.HobbyID)).ToList();
originalPerson.Hobbies = newHobbies;
But I get an error:
Only primitive types or enumeration types are supported in this
context.
How can I update related data without going to the database multiple times?
To avoid that exception you can select first the Ids from the vm.Hobbies collection and after that filter the hobbies you need:
var Ids=vm.Hobbies.Select(x=>x.HobbyID);
var newHobbies = db.Hobbies.Where(x => Ids.Contains(x.HobbyID)).ToList();
// another option could be: db.Hobbies.Where(x => Ids.Any(id=>id==x.HobbyID)).ToList();
originalPerson.Hobbies = newHobbies;
The message says that you can't use vm.Hobbies inside a LINQ-to-Entities query, only primitive types. This is what you can do:
var hobbyIds = vm.Hobbies.Select(h => h.HobbyId).ToList();
var newHobbies = db.Hobbies.Where(x => hobbyIds.Contains(x.HobbyID)).ToList();
The overall construction would be like such:
Your view takes the view model which displays the list of hobbies
You have an action to add a new Hobby passing a Hobby entity back as part of the action
You simply make a call back to the database to create the new association between the Person and Hobby
Add the Hobby entity to the collection in Person
Return passing the view model back to the view
In general EF and MVC are much easier to work with when you try to interact with your entities as whole entities and don't try to micromanage yourself things such as IDs, entity hydration, and so on.
You could join by ID instead of fetching data from the DB:
originalPerson.Hobbies = db.Hobbies.Join(vm.Hobbies, h => h.ID,
v => v.HobbyID, (h, v) => h).ToList();

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