How to work with true self-referencing objects in EF5 code first?

There are several questions on this topic, but my question is very specific for true self-reference. All examples for other questions are circular links, and this does not help me in this case.

Let's say I have this model:

public class User { [Key] public int Id { get; set; } ... public int CreatedByUserId { get; set; } } 

and this mapping:

 public class UserMap : EntityTypeConfiguration<User> { public UserMap() { this.HasRequired(a => a.CreatedByUser) .WithMany() .HasForeignKey(u => u.CreatedByUserId); } } 

After the migration, a database with this code is created, I can manually add the user to SQL Management Studio with the identifier Id = 1 and CreatedByUserId = 1 so that he tells me that such links can work.

However, when using EF to create a user, I run into the problem of "unable to determine the valid order for dependent operations." There are many questions on this subject that include a new object that refers to another new object that has a foreign key for the first object, which is a circular reference. The solution in these cases either saves one of the entities first, or has an identifier with a zero value for the foreign key of the circular entity. I can not do any of them, because the first would be impossible, and the second - an external restriction, that I can not have identifiers with zero values.

So, seeing how I can achieve this by adding a manual entry, I can assume that this is an EF5 limitation. What work around?

+7
c # sql entity-framework
source share
2 answers

You can still satisfy your interface and save the save first, and then set the method by adding another property to act as a nullable third-party element for CreatedByUserId :

 public class User : ICreatable { [Key] public int Id { get; set; } ... public int CreatedByUserId { get { if (!_CreatedByUserId.HasValue) //throw new exception, something went wrong. return _CreatedByUserId; } set { _CreatedByUserId = value; } } int? _CreatedByUserId { get; set; } } 
+1
source share

You might want to rethink the possibility that the user can create it himself ...

However, if you really want to do this, then there is a solution. The main problem is that your column is an IDENTITY column, which means that EF does not indicate an identifier, the SQL server provides each row with an automatically indexed identifier. Any value specified as an identifier is ignored. You do not necessarily know when you perform INSERT that the next identifier will be such that you cannot create a link to a string that does not yet exist.

Change the display code to the following:

 this.Property(x => x.Id) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None); this.HasRequired(x => x.CreatedByUser) .WithMany(); 

You do not need to specify a foreign key if the name pattern matches (e.g. CreatedByUser and CreatedByUserId).

Now when you insert the user, you can specify Id and CreatedById. Note that now you should always specify the Id to insert a new user. This is common practice if you use GUIDs as identifiers, because you can simply generate a new GUID without having to first request the next "available" identifier before creating a new object.

+1
source share

All Articles