简体   繁体   中英

C# EF6 entity mapping

I am currently working on a project (WCF Service) that should utilize EF6 with a MySql database. That part itself is already working as it should, but I am having huge problems with the mapping.

I have 3 entities like following:

Employee - ManyToMany - Project - OneToMany - ProjectStep

My database model is set up like this:

Employee table:

CREATE TABLE `employee` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `Name` varchar(255) DEFAULT NULL,
  `JobDescription` varchar(255) DEFAULT NULL,
  `Department` varchar(255) DEFAULT NULL,
  `DirectDialing` varchar(255) DEFAULT NULL,
  `Status` bit(1) DEFAULT NULL,
  PRIMARY KEY (`ID`)
)

Project table:

CREATE TABLE `project` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `Titel` varchar(255) DEFAULT NULL,
  `StartDate` datetime DEFAULT NULL,
  `EndDate` datetime DEFAULT NULL,
  `Description` varchar(255) DEFAULT NULL,
  `ProjectLeader` int(11) DEFAULT NULL,
  `Status` int(11) DEFAULT NULL,
  PRIMARY KEY (`ID`),
  CONSTRAINT `project_fk` FOREIGN KEY (`ProjectLeader`) REFERENCES `employee` (`ID`)
)

ProjectStep table:

CREATE TABLE `project_step` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `Description` text,
  `StartDate` datetime DEFAULT NULL,
  `EndDate` datetime DEFAULT NULL,
  `Project` int(11) DEFAULT NULL,
  PRIMARY KEY (`ID`),
  CONSTRAINT `project_step_fk` FOREIGN KEY (`Project`) REFERENCES `project` (`ID`)
)

Mapping table Employee - Project

CREATE TABLE `employee_project` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `EmployeeId` int(11) DEFAULT NULL,
  `ProjectId` int(11) DEFAULT NULL,
  PRIMARY KEY (`ID`),
  CONSTRAINT `projectId_fk` FOREIGN KEY (`ProjectId`) REFERENCES `project` (`ID`)
  CONSTRAINT `employeeId_fk` FOREIGN KEY (`EmployeeId`) REFERENCES `employee` (`ID`)
)

I then created my entities as following:

[DataContract(Namespace = "Shared")]
public class Employee
{
    public Employee()
    {
        this.Projects = new List<Project>();
    }

    [DataMember]
    [Key]
    public int ID { get; set; }

    [DataMember]
    public String Name { get; set; }

    [DataMember]
    public String Berufsbezeichnung { get; set; }

    [DataMember]
    public String Abteilung { get; set; }

    [DataMember]
    public String Durchwahl { get; set; }

    [DataMember]
    public bool Status { get; set; }

    [DataMember]
    public virtual ICollection<Project> Projects { get; set; }
}

[DataContract(Namespace = "Shared")]
public class Project
{

    public Project()
    {
        this.EmployeesWorkingOnProject = new List<Employee>();
        this.ProjectSteps = new List<ProjectStep>();
    }

    [DataMember]
    [Key]
    public int ID { get; set; }

    [DataMember]
    public String Titel { get; set; }

    [DataMember]
    public DateTime StartDatum { get; set; }

    [DataMember]
    public DateTime EndDatum { get; set; }

    [DataMember]
    public String Beschreibung { get; set; }

    [DataMember]
    [Column("Projektleiter")]
    public Employee Projektleiter { get; set; }

    [DataMember]
    public bool Status { get; set; }

    [DataMember]
    public virtual ICollection<Employee> EmployeesWorkingOnProject { get; set; }

    [DataMember]
    [ForeignKey("Project")]
    public virtual ICollection<ProjectStep> ProjectSteps { get; set; }
}

[DataContract(Namespace = "Shared")]
public class ProjectStep
{
    [DataMember]
    public int ID { get; set; }

    [DataMember]
    public String Beschreibung { get; set; }

    [DataMember]
    public DateTime StartDatum { get; set; }

    [DataMember]
    public DateTime EndDatum { get; set; }

    [DataMember]
    public Project Project { get; set; }
}

and used Fluent API to create the ManyToMany relationship as well as the OneToMany :

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

    modelBuilder.Entity<Employee>().HasMany(e => e.Projects).WithMany(t => t.EmployeesWorkingOnProject).Map(m =>
    {
        m.MapLeftKey("MitarbeiterID");
        m.MapRightKey("ProjektID");
        m.ToTable("mitarbeiter_projekte");
    });

    modelBuilder.Entity<ProjectStep>().HasRequired<Project>(p => p.Project).WithMany(p => p.ProjectSteps);
}

I am really at a loss, because when I am trying to insert a new Project , I get the error:

The ForeignKeyAttribute on property 'ProjectSteps' on type 'Shared.Project' is not valid. The foreign key name 'Project' was not found on the dependent type 'Shared.ProjectStep'. The Name value should be a comma separated list of foreign key property names.

I think that I have a few problems in my mapping which are the reason for this error. I am completely at a loss as to where my mapping is wrong as well as if I even used the right way.

So my question is, should one even mix the DataAnnotations with Fluent API , and could someone help me getting this mapping right?

Please note, that this is my very first C#/EF6 project and that I am not very common with everything so some explanation is welcome.

Data annotations and fluent configurations can be mixed. But especially for relationships I find fluent configuration much more intuitive. The problem with ForeignKey attribute is that it could be applied to the FK property, in which case it is used to specify the navigation property name. Or it could be applied to the navigation property, in which case it should contain the FK property name (or comma separated list of composite FK property names).

The important thing to know is that it cannot be used to specify the FK column name. The column names are controlled by the Column attribute, hence it can be used only if you have explicit FK property (which you don't).

Fluent API has no such limitations. You either use HasForeignKey when you have explicit FK property, or Map with MapKey when you don't.

In your case, in order to specify your unusual "Project" as FK column name, you can use the following fluent configuration (and don't forget to remove the invalid ForeignKey attribute from ProjectSteps ):

modelBuilder.Entity<ProjectStep>()
    .HasRequired(p => p.Project)
    .WithMany(s => s.ProjectSteps)
    .Map(m => m.MapKey("Project"));

In order to be able to add new "Project" to db, you'll need to remove foreign key annotation from "ProjectSteps" property in "Project" class. "Foreign Key" annotation can be applied only to properties which are not collection type.

[DataContract(Namespace = "Shared")]
public class Project
{

    public Project()
    {
        this.EmployeesWorkingOnProject = new List<Employee>();
        this.ProjectSteps = new List<ProjectStep>();
    }

    [DataMember]
    [Key]
    public int ID { get; set; }

    [DataMember]
    public String Titel { get; set; }

    [DataMember]
    public DateTime StartDatum { get; set; }

    [DataMember]
    public DateTime EndDatum { get; set; }

    [DataMember]
    public String Beschreibung { get; set; }

    [DataMember]
    [Column("Projektleiter")]
    public Employee Projektleiter { get; set; }

    [DataMember]
    public bool Status { get; set; }

    [DataMember]
    public virtual ICollection<Employee> EmployeesWorkingOnProject { get; set; }

    [DataMember]
    public virtual ICollection<ProjectStep> ProjectSteps { get; set; }
}

In addtition answering you question:

So my question is, should one even mix the DataAnnotations with Fluent API, and could someone help me getting this mapping right?

Mixing is not a good practice. Imho Fluent API is always better, because it gives much more mapping options.

For more info about EF mappings, check out THIS

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM