Serialize one to many relationships in Json.net - entity-framework

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

Related

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 7 Include() not working as expected

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.

Entity Framework 5 inheritance generating as TPT instead of TPH?

I'm trying to implement various tables that inherent a groups table. When I generate the database from the model it comes out as type-per-table instead of type-per-inheritance like I would like.
I have:
Group set to abstract
Each group type table is mapped to Group conditionally when type(column) = a different int for each table
Can anyone point me in the right direction for what I need to do to change this to type per inheritance?
EDIT: by request via comment here is my db set for group, and none of the group types of a dbset
public DbSet<Group> Groups { get; set; }
Here are the generated classes:
Group:
public abstract partial class Group
{
public Group()
{
this.GroupHierarchies = new HashSet<GroupHierarchy>();
this.GroupHierarchies1 = new HashSet<GroupHierarchy>();
this.NetworkActions = new HashSet<NetworkAction>();
this.PermissionAssignments = new HashSet<PermissionAssignment>();
this.UserProfiles = new HashSet<UserProfile>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Acronym { get; set; }
public string Description { get; set; }
public Nullable<System.DateTime> CreatedDate { get; set; }
public virtual ICollection<GroupHierarchy> GroupHierarchies { get; set; }
public virtual ICollection<GroupHierarchy> GroupHierarchies1 { get; set; }
public virtual ICollection<NetworkAction> NetworkActions { get; set; }
public virtual ICollection<PermissionAssignment> PermissionAssignments { get; set; }
public virtual ICollection<UserProfile> UserProfiles { get; set; }
}
One of the group types:
public partial class HoaManagementCompany : Group
{
public string Address { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
The other group type, there will be many more in the future but only these two until I get it to work.
public partial class HoaAttorney : Group
{
public string Address { get; set; }
}
When I generate the database from the model...
Are you using Model-First strategy? This unfortunately would make it difficult to get TPH inheritance for your model (which would be easy for Code-First or Database-First strategy).
(Default inheritance mapping for Code-First is TPH, so you should not have your problem with Code-First.)
Out of the box TPH is not available with Model-First. The default inheritance strategy for Model-First is TPT and there is no easy way to switch to TPH in the model designer:
It is possible to map to a TPH inheritance using Model First but you
would have to write your own database generation workflow which is
complex. You would then assign this workflow to the Database
Generation Workflow property in the EF Designer. An easier alternative
is to use Code First.
There is an additional tool from Microsoft - the Entity Designer Database Generation Power Pack - which supports TPH database generation workflow for Model-First. But the problem is that it doesn't look very well maintained (last update from May 2012) and it doesn't support Visual Studio 2012. But if you use VS 2010 you can try it.
You should only use your Groups DBSet for TPH.
Also make sure you aren't adding Table annotations to the poco classes
Try following this blog, it worked for me in the past.
Inheritance with EF Code First: Part 1 – Table per Hierarchy (TPH).
Also talks about Table per Type (TPT) and Table per Concrete class (TPC) Inheritances.

Do all associated objects have to be accessed (lazyloaded) before an existing object can be saved?

I'm learning EF Code First and am having trouble when updating existing records. I've boiled it down to this simple example:
This works:
using(var db = new DataContext()){
var p = db.People.Find(1);
p.Name="New Name";
Console.WriteLine(p.Gender.Name); //<--Unnecessary property access
db.SaveChanges(); //Success
}
...but this fails (when the WriteLine is removed):
using(var db = new DataContext()){
var p = db.People.Find(1);
p.Name="New Name";
db.SaveChanges(); //DbValidationError "Gender field is required."
}
Why do I have to access/load the Gender propery if I'm not using it and the data is already correctly stored in the database? I just want to change the Name on an existing record. In this example, Gender is a one-to-many association stored as Gender_Id in the People table. The classes are defined like this:
public class Person
{
[Key]
public int PersonId { get; set; }
[Required, MaxLength(50)]
public string Name { get; set; }
[Required, Column("Gender")]
virtual public GenderCode Gender { get; set; }
}
public class GenderCode
{
[Key]
public int Id { get; set; }
[Required, MaxLength(10)]
public string Name { get; set; }
}
public class DataContext:DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<GenderCode> GenderCodes { get; set; }
}
Of course, the fully defined classes are to have many more fields. I'd rather not have to access every dependant property every time I want to modify an unrelated value.
Is there a way to load an object, change a field, and save it without loading all related objects first?
Yes, this is necessary because of some horrible design mistakes in EF.
Check out my similar question, EF: Validation failing on update when using lazy-loaded, required properties
One trick is declaring FK properties along with the OO relations:
[ForeignKey("GenderId"), Column("Gender")]
virtual public GenderCode Gender { get; set; }
[Required]
public int GenderId { get; set; }
It is because you are using data annotations and Required attribute has also meaning for validation. Once you set navigation property as Required by data annotation it must be filled / loaded when you are going to persist entity to the database.