I have problem I cannot find a solution for: basically I would like how to turn off cascading update on a EF CodeFirst many to many association.
I have two classes List and Recipient.
public class List
{
public string DisplayName { get; set; }
public DateTime? LastSyncronized { get; set; }
public virtual ICollection<Recipient> Recipients { get; set; }
}
public class Recipient
{
public int Id { get; set; }
public String Name { get; set; }
public virtual ICollection<List> Subscriptions { get; set; }
}
During some processing I need to add to the Recipients property of a List a number of Recipient that I take from an external source.
Then I do my processing and at the end I have to same the List to update the LastSyncronized property.
Unfortunately, when I save, the automatic tracking of EF also saves into the database all the Recipients I've taken from the external source.
How can I configure the DbContext not to persist the new objects to the Database?
I tried removing them all from the collection, but even that they are added to the database anyway. In this case, the join table is unchanged, but the recipient one is added with the new recipients.
Thank you
Simone
Not sure this is the right solution to this, but I set, in the DbContext class:
Configuration.AutoDetectChangesEnabled = false;
Now object added are not added to the database.
Related
All,
Is it possible to use the same FK for two tables.
Probably it is not a good practice, but I have a two different classes that can be both booked:
public class Course {
public Course() {
BookingRefs = new HashSet<BookingRef>();
}
public long Id { get; set; }
public string Title { get; set; }
// other props ...
[InverseProperty(nameof(BookingRef.Course))]
public virtual ICollection<BookingRef> BookingRefs { get; set; }
}
public class GiftCard {
public GiftCard() {
BookingRefs = new HashSet<BookingRef>();
}
public long Id { get; set; }
public string Prop1 { get; set; }
public int Prop2 { get; set; }
// other props ...
[InverseProperty(nameof(BookingRef.Course))]
public virtual ICollection<BookingRef> BookingRefs { get; set; }
}
// this is the bookin reference for a Course or an GiftCard
public class BookingRef {
public BookingRef() {
}
public long Id { get; set; }
// other props ...
/// <summary>The item (usually the course but theoretically anything with a long id)</summary>
public long? ItemId { get; set; }
// maybe a generic Object?
[ForeignKey(nameof(ItemId))]
public Object GiftCard { get; set; }
// maybe 2 items possibly null?
[ForeignKey(nameof(ItemId))]
public Course Course { get; set; }
// maybe 2 items possibly null?
[ForeignKey(nameof(ItemId))]
public GiftCard GiftCard { get; set; }
}
Is it possible to use the same FK for two tables
No. The relational model doesn't allow that. You can introduce a superclass of all your bookable things and have a FK to that, but you shouldn't do that just get a single collection rather than multiple.
Think of it from the relational data perspective. How would the database know what table an "Item ID" pointed at? How would it index it?
This would be a case for using a null-able FK to each related table on the booking. These FKs do not need to reside in the entity, just the navigation properties. You can leverage .Map(x => x.MapKey) in EF6 or .HasForeignKey("") in EF Core to leverage a shadow property.
This does not enforce if you want a booking to only be associated to a course or a gift card but not both. That would need to be catered for at the application level, and I would recommend using a scheduled maintenance task to evaluate the data for violations to that rule. (Look for bookings holding both a course ID and a gift card ID for example)
You can alternatively keep the joins "loose" and evaluated by the application based on a discriminator similar to an inheritance model. (ItemId + ItemType) However you have to resolve the relationship load separately in your application based on the ItemType and lose out on any FK, indexing, and data integrity checks in the database. This could be a significant performance & maintenance cost to save adding a couple FKs.
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!
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.
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.
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;
});