Entity Framework 7 Include() not working as expected - entity-framework

EF7 fills contained navigation properties even when not requested. For example, I have the below entities.
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public int DepartmentId { get; set; }
public Department Department { get; set; }
}
public class Department
{
public int DepartmentId { get; set; }
public string Name { get; set; }
public ICollection<Employee> Employees { get; set; }
}
My fetch query is as below.
ctx.Employees.Where(e => e.Gender == "Male").Include(e => e.Department)
I get Department property of Employee object filled – which is as expected as I have an Include for Department. I find that Department.Employees is also filled but partially (only with male employees). I have not specified an Include for Department.Employees, but it is still getting populated. Is this behavior by design? Is there any way to avoid fetching Department.Employees in this scenario?
I am using EF7 NuGet package with version 7.0.0-rc1-final.

That is the normal behavior of EF. When you execute your query, all the entities you load is going to be attached to your context. So, EF is not executing another query and loading Department.Employees partially, those employees were loaded earlier when you execute your query. In summary, when you consult Department.Employees navigation property, EF is going to fill that property with the employees that you load with your query filtering by Gender.
Update:
As I pointed out in my comment above, Lazy Loading is not supported in EF7. If you want to avoid that Json.NET serializes that property, you can use the attribute JsonIgnore over that property or you can create a custom class (DTO) to project your query and fill only the properties that you need. I also recommend take a look to Automapper if you decide to use this last solution.

Related

Entity Framework Model with a max child record property

I have a couple of simple EF models with a parent child relationship. The children are essentially audit records. I am wanting to use the audits to get the min date to determine the "Create Date" of the parent.
The two models directly match the SQL Database as you would expect with the exception of the derived "CreatedDate" field that I was hoping for EF to translate into a simple "Max" subquery.
I am using the EF Repository Pattern with the Unit Of Work pattern and trying to keep things really simple with the SQL Server doing the MAX query.
I have tried using automapper to map in my service layer but this generates an SQL query per parent.
Any ideas?
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IEnumerable<Audit> Audits { get; set; }
//Read only create date and problem property
[NotMapped]
public DateTime CreatedDate
{
get
{
return Audits.Max(a => a.AuditDate);
}
}
}
public class Audit
{
public int Id { get; set; }
public int ItemId { get; set; }
public DateTime AuditDate { get; set; }
}
I could well be wrong but I don't think EF will support this; either you pull in all of the Audit records for an Item
e.g.
var item = _context.Items.Include("Audit").FirstOrDefault(i=>i.Id==1)
in which case the method will work.
Or, you could create a view mapped to a new entity (or QueryType if you are using EF Core) that contains Id, Name, and CreateDate only.
e.g
SELECT Id,Name=CreateDate=Max(CreateDate) from Items group by Id,Name
Now since I always use views for this kind of thing I do stand to be corrected on the first point!

EF creating an unwanted field in database

I've hit a snag while building a .net mvc site. I have 2 related objects and am struggling with properly linking them. Specifically:
public class Address
{
public int AddressId { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostCode { get; set; }
[ForeignKey("AddressCategory")] // <-- EF adds field to below object's table
public int AddressCategoryId { get; set; }
public virtual AddressCategory AddressCategory { get; set; }
}
public class AddressCategory
{
public int AddressCategoryId { get; set; }
public string Description { get; set; }
}
Adding the [ForeignKey] data annotation to the Address object results in EF adding an Address_AddressId column to the AddressCategory table, which I don't want (or need) to happen.
I've tried to omit the ForeignKey attribute, but then I run into other errors because .net can't link the tables (e.g. Unknown column 'Extent1.AddressId' in 'field list'). Additionally, I wouldn't be able to use:
var addresses = db.Addresses.Include(l => l.AddressCategory);
Is there any way to link the 2 tables without EF adding an additional column to the AddressCategory table?
Thank you to #cloudikka for responding. After much trial-and-error I seem to have gotten it to work simply by omitting any ForeignKey reference from either object. I let EF rebuild the database and perform all scaffolding (CRUD forms) and they have been created perfectly.
My take-away is that foreign key attributes should be used for parent-child relationships, but not for look-up tables. I clearly have much to learn about asp.net mvc!

Is this expected Entity Framework 7 / Core behaviour or a bug?

I have a simple model for the purposes of this post.
Two entities Role and Person.
public class Role : Entity
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Person> PeopleWithThisRole { get; set; }
}
public class Person : Entity
{
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Guid? RoleId { get; set; }
}
If I get the Roles from the EF context, then the PeopleWithThisRole collection is empty (unless I .Include them. As expected.
However if I get the Roles and I then get the People as below
var roles = _context.Roles.ToList();
var people = _context.People.ToList();
Then roles.PeopleWithThisRole collection is fully populated with the people without having to .Include it.
Is this the expected behaviour or should I raise this as a bug?
Thanks
UPDATE
With many thanks to #hvd below, I have decided to keep the entities clean and not use [JsonIgnore] attribute and instead map to DTOS (which exclude those properties I don't need in the JSON) - which is probably the correct way!
It's expected and also how earlier versions of EF worked.
Your _context keeps track of entities loaded inside that context, to allow for saving changes. Inside that context, Person objects have been loaded (at your request) and their RoleId values are known. Inside that same context, Role objects with those same Id values have been loaded (also at your request). EF links the objects in memory based on those IDs. If you trace the SQL queries sent to the server, you should find that no queries have been sent other than those that you requested.

Entity Framework with Proxy Creation and Lazy Loading disabled is still loading child objects

I'm having some issues with the Entity Framework using POCOs and I hope someone can tell me at a high level if the behaviour I'm seeing is expected or I need to dig deeper into why it's happening.
I have a class Customer and another CustomerType, so Customer has a property Type (of type CustomerType indicating the type) and CustomerType has property Customers which is a collection of Customers (All Customers that have that type) So these are basically the Navigation properties on both ends of an association, resulting in POCO code something like:
public partial class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public int TypeId { get; set; }
public CustomerType Type { get; set; }
}
public partial class CustomerType
{
public CustomerType()
{
this.Customers = new HashSet<CustomerType>();
}
public int Id { get; set; }
public string TypeName { get; set; }
public virtual ICollection<Customer> Customers { get; set; }
}
I have turned off Proxy creation and LazyLoading (i.e. both DbContext.Configuration.ProxyCreationEnabled=false and DbContext.Configuration.LazyLoadingEnabled=false) because they make Serialization a pain.
As expected when I get instances from the Customer set, the Type property on them is null by default.
But if I get instances from the Customer set with a .Include("Type") not only is it loading the Type properties, but it's also loading the children - i.e. the collection of Customers on each of these.
Is this expected?
It is semi expected. The Include extension affects the SQL that is run. Those CustomerTypes that ARE loaded (by virtue of being included in the Customer query) will be built into the object tree according to the CustomerType.ParentId column.
So if by some fluke both a parent and a child is loaded in the same query, the child will be stuffed into the parent.

Serialize one to many relationships in Json.net

I am using the Entity Framework code first for data access and I have a Company class which has a collection of Employees. The Employee class also has a Company property.
I would like to be able to serialize a Company and include the list of employees in the serialization.
Here is Company:
public class Company
{
public long Id { get; set; }
public string Name { get; set; }
public DateTime? Established { get; set; }
public virtual IList<Employee> Employees { get; set; }
public DateTime? DateCreated { get; set; }
public DateTime? DateUpdated { get; set; }
}
Here is Employee
public class Employee
{
public long Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public virtual Company Company { get; set; }
public DateTime? DateCreated { get; set; }
public DateTime? DateUpdated { get; set; }
}
I get a serialization Exception "Self referencing loop detected for type" when I try to serialize a Company object.
Thanks.
I think they have fixed this in the latest version.
Check out the help docs under the section "Serializing and Deserializing JSON -> Serialization and Preserving Object References".
Set this setting when initializing the JSON.Net Serializer:
PreserveReferencesHandling = PreserveReferencesHandling.Objects;
So an example would be this:
var serializerSettings = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects };
string json = JsonConvert.SerializeObject(people, Formatting.Indented, serializerSettings);
I verified that this works with my code first solution, and a circular reference in the navigation properties. If you look at the resulting JSON it should have "$id" and "$ref" properties everywhere.
Updated Answer
You can either:
reconfigure json.net to ignore
selfreference loops
use the [JsonIgnore] Attribute
use a custom converter that remove
the navigation in the child
or you can use Data Transfer Objects
In case this helps anyone, I thought I'd document how we resolved this same error for our purposes when using Entity Framework 4.3.1 and JSON.Net 4.5.3.
We are using the Database First DbContext approach. For our needs, we could resolve it using the [JsonIgnore] attribute. The trick is just that since changes to the automatically generated entity classes are overwritten when you refresh from the database, with Database First you can add the attributes using the "metadata buddy class" approach given in this StackOverflow post.
Below is a code excerpt. We had a "Query" object (class Query) that had relationships to "Company" and "User" objects. In a new class file, we declare the partial class with a [MetadataType] attribute, and then in the QueryMetadata class we specified, we annotate the members we want to ignore— namely the public virtual members that EF4.x adds to express the relationships (a.k.a. navigation properties).
The Query entity also has foreign key fields (named FK_User and FK_Company in our case). These fields do not need the [JsonIgnore] attribute— they can be serialized with their foreign key values.
[MetadataType(typeof(QueryMetadata))]
public partial class Query
{
}
public class QueryMetadata
{
[JsonIgnore]
public virtual Company company { get; set; }
[JsonIgnore]
public virtual User user { get; set; }
}
However, if we actually had to also serialize the related Company or User objects, we'd hit a brick wall! The approach suggested by John Bubriski here wouldn't work for us since we want to rely on Entity Framework change tracking.
If you are getting this error using WebApi you can put the following in WebApiConfig.cs so json.net ignores circular refs
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
Microsoft : Loop Reference handling in Web API
If you're using WebAPI EntityFrameworkCore 2.0 this solution doesn't work, you need to set it on Startup.cs->ConfigureServices:
.AddJsonOptions(options => {
var settings = options.SerializerSettings;
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});