EF Core two data models on two separate projects / dlls with a relationship

Again stuck on my learn / dream project WorkNxt, what i though is a very simple task turn out to be a bit tricky.

as i am trying to separate each module and load things at runtime and use DI, i encountered a problem that one of the data models i created under one module need to have EF relationship with another data model on another module, each of course was on a separate project / dll.

ideally i didn't want to reference the other module project but to make things even more difficult this was one-to-one relationship and i needed it from both sides and i needed to use it on a ThenInclude as i want to include data from one of the models on the results of the other one.

So even if i was ok with cross modules reference which i was trying to avoid i will fall for circular reference trap as you would need to use each class on the other to create the relationship.

After many hours of banging my head against the wall, and trying a couple of suggestions from EF core team through the issue i submitted, i was about to give up to the idea that this is only doable if a specific  feature request for EF Core was done!

but only then i got it to work! and here is how, Please note that type configurations get loaded at runtime.

let's say first data model was


public class SecurityUser
    {
        public Guid SecurityUserId { get; set; }
        [Required]
        public string UserName { get; set; }
        [NotMapped]
        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }
        public string Hash { get; set; }
        public string Salt { get; set; }
        public byte[] RowVersion { get; set; }
        public virtual List<securitygroupsusers> SecurityGroups { get; set; }

        public Guid? PersonId { get; set; }

        public dynamic SecurityPerson { get; set; }
    }

    public class SecurityUserEfConfig : IEntityTypeConfiguration<securityuser>
    {
        public void Configure(EntityTypeBuilder<securityuser> builder)
        {
            builder.ToTable("SecurityUser");
            builder.Property(e => e.SecurityUserId).HasColumnName("SecurityUserId");
            builder.HasKey(e => new { e.SecurityUserId });

            builder.HasIndex(e => e.UserName).IsUnique();

            builder.Property(e => e.RowVersion).IsConcurrencyToken().ValueGeneratedOnAddOrUpdate();

            builder.HasMany(u => u.SecurityGroups).WithOne(sg => sg.User).HasForeignKey(sg => sg.UserId);

            if (OtherModules.Contacts_Person != null)
            {
                builder.HasOne(OtherModules.Contacts_Person, "SecurityPerson").WithOne().HasForeignKey(typeof(SecurityUser), new string[]{ "PersonId" });
            }
            
        }
    }

note that PersonId is the foreign key and SecurityPerson will be later the navigation property for the other data model entity.

the important part that you should notice is 



if (OtherModules.Contacts_Person != null)
            {
                builder.HasOne(OtherModules.Contacts_Person, "SecurityPerson").WithOne().HasForeignKey(typeof(SecurityUser), new string[]{ "PersonId" });
            }

in case you wonder what is OtherModules.Contacts_Person it's just another static class that grab the other data model type by name


public static class OtherModules
    {
        public static Type Contacts_Person
        {
            get
            {
                return Type.GetType("WorkNxt.Contacts.Models.Person,WorkNxt.Contacts.Models");
            }
        }
    }

and the same can be done to the other class.

What do you think?

Popular posts from this blog

add extra files on deploy ASP Core web app using publish profile

how to copy NuGet packages from one project to another

Blazor how to generate that SHA256 check