0

I have a POCO Entity named Employee. And then I have a second POCO Entity named Case.

I want a navigation property that looks like instead this:

public class Case : BaseEntity
{
    public long EmployeeId { get; set; }
    public virtual Employee Employee{ get; set; }

like this:

public class Case : BaseEntity
{
    public long InitialContactId { get; set; }
    public virtual Employee InitialContact { get; set; }

I want to name my property InitialContact. Not Employee.

But I get this error when EF tries to create the Database:

Unable to determine the relationship represented by navigation property 'Case.InitialContact' of type 'Employee'. Either manually configure the relationship, or ignore this property from the model.

Update 1:

I got it to work like this:

public class Case : BaseEntity
{
    public long InitialContactId { get; set; }
    [ForeignKey("Id")]
    public virtual Employee InitialContact { get; set; }
    public DateTime InitalConsultDate { get; set; }
    public Guid AppUserId { get; set; }
    public virtual AppUser LerSpecialist { get; set; }

}

The primary key is ID in my BaseEntity. Not EmployeeId.

But I have second part to my question.

Here is my Complete Employee POCO:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using Hrsa.Core.Generic.Model.Framework.Concrete;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace Hrsa.Core.Generic.Model.Lerd
{
    public class Employee : BaseEntity
    {
        [BindNever]
        public string Email { get; set; }
        [BindNever]
        public long OrganizationId { get; set; }
        [BindNever]
        public string Supervisor { get; set; }
        [BindNever]
        public string SupervisorEmail { get; set; }
        [BindNever]
        public string FirstName { get; set; }
        [BindNever]
        public string LastName { get; set; }
        public string Notes { get; set; }
        [BindNever]
        public  long BargainingUnitId { get; set; }
        [BindNever]
        public long PayPlanId { get; set; }
        [BindNever]
        public long GradeRankId { get; set; }
        [BindNever]
        public long PositionTitleId { get; set; }
        [BindNever]
        public long SeriesId { get; set; }
        public bool IsUnionEmployee { get; set; }
        public virtual Organization Organization { get; set; }
        public virtual BargainingUnit BargainingUnit { get; set; }
        public virtual PayPlan PayPlan { get; set; }
        public virtual GradeRank GradeRank { get; set; }
        public virtual PositionTitle PositionTitle { get; set; }
        public virtual  Series Series { get; set; }
        public virtual ICollection<UnionHours> UnionHours { get; set; }
        public virtual ICollection<Case> Cases { get; set; }
        [NotMapped]
        public string UnionEmployeeYesNo => (IsUnionEmployee) ? "Yes" : "No";
   }
}

I want my Employee to have many Cases:

public virtual ICollection<Case> Cases { get; set; }

Here is my complete Cases POCO:

public class Case : BaseEntity
{
    public long InitialContactId { get; set; }
    [ForeignKey("Id")]
    public virtual Employee InitialContact { get; set; }
    public DateTime InitalConsultDate { get; set; }
    public Guid AppUserId { get; set; }
    public virtual AppUser LerSpecialist { get; set; }

}

So now my DB looks like this:

enter image description here

So I have my InitialContactId in Cases ok.

But now I need my Case to have many Employees.

So I add this in to my Case POCO:

public virtual ICollection<Employee> Employees { get; set; }

Now it looks like this:

public class Case : BaseEntity
{
    public long InitialContactId { get; set; }
    [ForeignKey("Id")]
    public virtual Employee InitialContact { get; set; }
    public DateTime InitalConsultDate { get; set; }
    public Guid AppUserId { get; set; }
    public virtual AppUser LerSpecialist { get; set; }

    public virtual ICollection<Employee> Employees { get; set; }

}

Now when I run it, I get this error again:

Unable to determine the relationship represented by navigation property 'Case.InitialContact' of type 'Employee'. Either manually configure the relationship, or ignore this property from the model.

Update 2:

I found this article for a Many-Many relationship in .Net Core 1: http://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration

So now I have a bridge lookup entity:

public class EmployeeCase
{
    [ForeignKey("Id")]
    public long EmployeeId { get; set; }
    public Employee Employee { get; set; }
    [ForeignKey("Id")]
    public long CaseId { get; set; }
    public Case Case { get; set; }

}

Employee POCO:

Changed:

public virtual ICollection<Case> Cases { get; set; }

to:

// Mapping - Collection of Cases
public virtual ICollection<EmployeeCase> EmployeeCases { get; set; }

Case POCO:

Changed:

public virtual ICollection<Employee> Employees { get; set; }

to:

 // Mapping - Collection of Employees
public virtual ICollection<EmployeeCase> EmployeeCases { get; set; }

In my AppDbContext

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        #region Many-to-Many Employees Cases
        modelBuilder.Entity<EmployeeCase>()
            .HasKey(ec => new { ec.EmployeeId, ec.CaseId });

        modelBuilder.Entity<EmployeeCase>()
            .HasOne(ec => ec.Employee)
            .WithMany(e => e.EmployeeCases)
            .HasForeignKey(ec => ec.EmployeeId);

        modelBuilder.Entity<EmployeeCase>()
            .HasOne(ec => ec.Case)
            .WithMany(c => c.EmployeeCases)
            .HasForeignKey(ec => ec.CaseId);
        #endregion

    }

Now when I run I get this error:

An exception of type 'System.Data.SqlClient.SqlException' occurred in Microsoft.EntityFrameworkCore.Relational.dll but was not handled in user code

Additional information: Introducing FOREIGN KEY constraint 'FK_EmployeeCase_Employees_EmployeeId' on table 'EmployeeCase' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.

Could not create constraint or index. See previous errors.

Update 3:

Finally got my tables the way I want with this piece of code from: Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths - why?

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Get rid of Cascading Circular error on ModelBuilding
        foreach (var relationShip in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
        {
            relationShip.DeleteBehavior = DeleteBehavior.Restrict;
        }

        #region Many-to-Many Employees Cases
        modelBuilder.Entity<EmployeeCase>()
            .HasKey(ec => new { ec.EmployeeId, ec.CaseId });

        modelBuilder.Entity<EmployeeCase>()
            .HasOne(ec => ec.Employee)
            .WithMany(e => e.EmployeeCases)
            .HasForeignKey(ec => ec.EmployeeId);

        modelBuilder.Entity<EmployeeCase>()
            .HasOne(ec => ec.Case)
            .WithMany(c => c.EmployeeCases)
            .HasForeignKey(ec => ec.CaseId);
        #endregion

        base.OnModelCreating(modelBuilder);
    }

Update 4:

This did not work after all. Remvoving the delete behavior for everything messes up my other relationships and I get errors.

How can I fix this? This is disgusting. So wishing I did not go Core.

Community
  • 1
  • 1
Sam
  • 3,203
  • 6
  • 37
  • 67
  • Changing the question while getting answers is [not really appreciated](https://meta.stackoverflow.com/q/309237/861716). Now you turned it into a question without a clear issue and it's got answers of which it's unclear to which stage of the question they apply. I think at least one answer was helpful enough to get accepted. – Gert Arnold Mar 31 '17 at 18:47
  • The actual answer if you don't like evolving questions is actually really near the top on Update 1. – Sam Mar 31 '17 at 20:32

3 Answers3

0

Entity Framework uses conventions to guess how to map your C# model to database objects.

In your case you violate convention by custom name, so you should explain Entity Framework how to map this stuff.

There are two possible ways: attributes and fluent API. I'd suggest to use the latter one.

See section "Configuring a Foreign Key Name That Does Not Follow the Code First Convention" here: Entity Framework Fluent API - Relationships

0

I have made it a habit of explicitly defining my relationships as EF does not always get them the way I want. I like to create a Mapping folder that contains my entity maps. The fluent api works great for this and inherits from EntityTypeConfiguration.

Try this. public class CaseMap : EntityTypeConfiguration<Case> { public CaseMap() { HasKey(m => m.Id) HasRequired(m => m.InitialContact) .WithMany(e => e.Cases) .HasForeignKey(m => m.InitialContactId); } }

Almost forgot. You need to tell your DbContext where to find these mappings. Add this to your DbContexts OnModelCreating method.

modelBuilder.Configurations.AddFromAssembly(typeof(MyContext).Assembly);
trevorc
  • 2,893
  • 4
  • 27
  • 45
0

This is what worked finally for the Cascading Delete circular references on the many-to-many in EF Core:

        // Get rid of Cascading Delete Circular references error.
        var type = modelBuilder.Model.GetEntityTypes().Single(t => t.Name == "Hrsa.Core.Generic.Model.Lerd.EmployeeCase");
        foreach (var relationship in type.GetForeignKeys())
        {
            relationship.DeleteBehavior = DeleteBehavior.Restrict;
        }

You have to get the Entity representing the many to many lookup only. And from there restrict the DeleteBehavior.

Sam
  • 3,203
  • 6
  • 37
  • 67