Как я могу назвать свойство навигации именем, отличным от имени его сущности в моем EF POCO?

У меня есть объект POCO с именем Сотрудник. И затем у меня есть вторая сущность POCO с именем Case.

Я хочу, чтобы свойство навигации выглядело так:

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

как это:

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

Я хочу назвать свое свойство InitialContact. Не Сотрудник.

Но я получаю эту ошибку, когда EF пытается создать базу данных:

Не удалось определить связь, представленную свойством навигации Case.InitialContact типа «Сотрудник». Либо вручную настройте отношение, либо проигнорируйте это свойство модели.

Обновление 1:

У меня получилось так:

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; }

}

Первичный ключ — это идентификатор в моем BaseEntity. Не идентификатор сотрудника.

Но у меня есть вторая часть моего вопроса.

Вот мой полный сотрудник 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";
   }
}

Я хочу, чтобы у моего сотрудника было много дел:

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

Вот мои полные чехлы 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; }

}

Итак, теперь моя БД выглядит так:

введите здесь описание изображения

Итак, у меня есть InitialContactId в случаях в порядке.

Но теперь мне нужно, чтобы в моем кейсе было много сотрудников.

Поэтому я добавляю это в свой случай POCO:

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

Теперь это выглядит так:

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; }

}

Теперь, когда я запускаю его, я снова получаю эту ошибку:

Не удалось определить связь, представленную свойством навигации Case.InitialContact типа «Сотрудник». Либо вручную настройте отношение, либо проигнорируйте это свойство модели.

Обновление 2:

Я нашел эту статью об отношениях «многие-многие» в .Net Core 1: http://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration

Итак, теперь у меня есть объект поиска моста:

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; }

}

Сотрудник ПОКО:

Измененный:

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

to:

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

Дело ПОКО:

Измененный:

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

to:

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

В моем 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

    }

Теперь, когда я запускаю, я получаю эту ошибку:

Исключение типа «System.Data.SqlClient.SqlException» возникло в Microsoft.EntityFrameworkCore.Relational.dll, но не было обработано в пользовательском коде.

Дополнительная информация: введение ограничения FOREIGN KEY «FK_EmployeeCase_Employees_EmployeeId» в таблице «EmployeeCase» может вызвать циклы или множественные каскадные пути. Укажите ON DELETE NO ACTION или ON UPDATE NO ACTION или измените другие ограничения FOREIGN KEY.

Не удалось создать ограничение или индекс. См. предыдущие ошибки.

Обновление 3:

Наконец-то я получил свои таблицы так, как хочу, с помощью этого фрагмента кода из: cascade-paths">Введение ограничения FOREIGN KEY может вызвать циклы или несколько каскадных путей - почему?

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);
    }

Обновление 4:

Это не сработало в конце концов. Удаление поведения удаления для всего портит мои другие отношения, и я получаю ошибки.

Как я могу это исправить? Это отвратительно. Так жаль, что я не пошел Core.


person Sam    schedule 31.03.2017    source источник
comment
Изменение вопроса при получении ответов на самом деле не ценится. Теперь вы превратили его в вопрос без четкой проблемы, и у него есть ответы, на которые неясно, к какому этапу вопроса они относятся. Я думаю, что по крайней мере один ответ был достаточно полезным, чтобы его приняли.   -  person Gert Arnold    schedule 31.03.2017
comment
Фактический ответ, если вам не нравятся развивающиеся вопросы, на самом деле находится почти в верхней части обновления 1.   -  person Sam    schedule 31.03.2017


Ответы (3)


Entity Framework использует соглашения, чтобы угадать, как сопоставить модель C# с объектами базы данных.

В вашем случае вы нарушаете соглашение по пользовательскому имени, поэтому вы должны объяснить Entity Framework, как сопоставить этот материал.

Есть два возможных пути: атрибуты и свободный API. Я бы предложил использовать последний.

См. раздел «Настройка имени внешнего ключа, которое не соответствует соглашению Code First» здесь: Entity Framework Fluent API — взаимосвязи

person Oleksandr Kobylianskyi    schedule 31.03.2017

Я сделал привычкой явно определять свои отношения, поскольку EF не всегда делает их такими, как я хочу. Мне нравится создавать папку Mapping, содержащую мои карты объектов. Fluent API отлично подходит для этого и наследуется от EntityTypeConfiguration.

Попробуй это. public class CaseMap : EntityTypeConfiguration<Case> { public CaseMap() { HasKey(m => m.Id) HasRequired(m => m.InitialContact) .WithMany(e => e.Cases) .HasForeignKey(m => m.InitialContactId); } }

Почти забыл. Вам нужно сообщить вашему DbContext, где найти эти сопоставления. Добавьте это в свой метод DbContexts OnModelCreating.

modelBuilder.Configurations.AddFromAssembly(typeof(MyContext).Assembly);
person trevorc    schedule 31.03.2017
comment
Спасибо - я видел это где-то в учебнике. Вероятно, придется это сделать. - person Sam; 31.03.2017
comment
Похоже, .Net Core еще не поддерживает WithMany. - person Sam; 31.03.2017
comment
Да, это делает. Entity‹Case›().HasOne().WithMany().HasForeignKey(). - person trevorc; 31.03.2017
comment
Мне нужно это на Entity‹EmployeeCase›. Здесь не получается. - person Sam; 31.03.2017

Вот что в конечном итоге сработало для циклических ссылок каскадного удаления на многие ко многим в 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;
        }

Вы должны получить объект, представляющий только поиск «многие ко многим». И оттуда ограничьте DeleteBehavior.

person Sam    schedule 31.03.2017