Подтвердить что ты не робот

Код Framework Entity Сначала: как комментировать внешний ключ для значения "По умолчанию"?

У меня есть 2 класса: клиент и опрос.

Каждый клиент может иметь множество опросов, но только один опрос по умолчанию.

Я определил классы следующим образом:

public class Client
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

    public string ClientName { get; set; }

    public Nullable<int> DefaultSurveyID { get; set; }

    [ForeignKey("DefaultSurveyID")]
    public virtual Survey DefaultSurvey { get; set; }

    public virtual ICollection<Survey> Surveys { get; set; }
}

public class Survey
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

    public string SurveyName { get; set; }

    [Required]
    public int ClientID { get; set; }

    [ForeignKey("ClientID")]
    public virtual Client Client { get; set; }
}

Это создает таблицу клиента, как я ожидаю:

[dbo].[Clients]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[ClientName] [nvarchar](max) NULL,
[DefaultSurveyID] [int] NULL
)

Но таблица Survey имеет дополнительный внешний ключ:

[dbo].[Surveys]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[SurveyName] [nvarchar](max) NULL,
[ClientID] [int] NOT NULL,
[Client_ID] [int] NULL
)

Почему Code First генерирует это отношение и как мне сказать это не?

4b9b3361

Ответ 1

Проблема заключается в том, что когда у вас есть несколько отношений между двумя объектами, EF Code First не может найти, какие свойства навигации совпадают, если вы не скажете, как это выглядит, вот код:

public class Client
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

    public string ClientName { get; set; }

    /****Change Nullable<int> by int?, looks better****/
    public int? DefaultSurveyID { get; set; }

    /****You need to add this attribute****/
    [InverseProperty("ID")]
    [ForeignKey("DefaultSurveyID")]
    public virtual Survey DefaultSurvey { get; set; }

    public virtual ICollection<Survey> Surveys { get; set; }
}

С вашей предыдущей версией EF создавал дополнительные отношения, потому что не знал, что свойство DefaultSurvey ссылалось на ID класса Survey, но вы можете сообщить ему, что добавление атрибута InverseProperty, параметром которого является имя свойства в Survey, вам нужно DefaultSurvey для соответствия с.

Ответ 2

Вы можете сделать это, используя код сначала, но не являясь первым экспертом кода, которого я обманул: -)

1) Я создал таблицы и отношения (как указано выше без дополнительного Client_ID) в базе данных с помощью SMS

2) Я использовал Обратный код инженера Сначала, чтобы создать необходимые классы и сопоставления

3) Я удалил базу данных и воссоздал ее с помощью context.Database.Create()

Исходная таблица defs:

CREATE TABLE [dbo].[Client](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NULL,
    [DefaultSurveyId] [int] NULL,
     CONSTRAINT [PK_dbo.Client] PRIMARY KEY NONCLUSTERED 
    (
        [Id] ASC
    )
)

CREATE TABLE [dbo].[Survey](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NULL,
    [ClientId] [int] NULL,
     CONSTRAINT [PK_dbo.Survey] PRIMARY KEY NONCLUSTERED 
    (
        [Id] ASC
    )
)

Плюс внешние клавиши

ALTER TABLE [dbo].[Survey]  WITH CHECK 
    ADD CONSTRAINT [FK_dbo.Survey_dbo.Client_ClientId] FOREIGN KEY([ClientId])
    REFERENCES [dbo].[Client] ([Id])

ALTER TABLE [dbo].[Client]  WITH CHECK 
    ADD CONSTRAINT [FK_dbo.Client_dbo.Survey_DefaultSurveyId] 
    FOREIGN KEY([DefaultSurveyId]) REFERENCES [dbo].[Survey] ([Id])

Код, созданный с помощью обратной инженерии:

public partial class Client
{
    public Client()
    {
        this.Surveys = new List<Survey>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public int? DefaultSurveyId { get; set; }
    public virtual Survey DefaultSurvey { get; set; }
    public virtual ICollection<Survey> Surveys { get; set; }
}

public partial class Survey
{
    public Survey()
    {
        this.Clients = new List<Client>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public int? ClientId { get; set; }
    public virtual ICollection<Client> Clients { get; set; }
    public virtual Client Client { get; set; }
}

public class ClientMap : EntityTypeConfiguration<Client>
{
    #region Constructors and Destructors

    public ClientMap()
    {
        // Primary Key
        this.HasKey(t => t.Id);

        // Properties
        this.Property(t => t.Name).HasMaxLength(50);

        // Table & Column Mappings
        this.ToTable("Client");
        this.Property(t => t.Id).HasColumnName("Id");
        this.Property(t => t.Name).HasColumnName("Name");
        this.Property(t => t.DefaultSurveyId).HasColumnName("DefaultSurveyId");

        // Relationships
        this.HasOptional(t => t.DefaultSurvey)
            .WithMany(t => t.Clients).HasForeignKey(d => d.DefaultSurveyId);
    }

    #endregion
}

public class SurveyMap : EntityTypeConfiguration<Survey>
{
    #region Constructors and Destructors

    public SurveyMap()
    {
        // Primary Key
        this.HasKey(t => t.Id);

        // Properties
        this.Property(t => t.Name).HasMaxLength(50);

        // Table & Column Mappings
        this.ToTable("Survey");
        this.Property(t => t.Id).HasColumnName("Id");
        this.Property(t => t.Name).HasColumnName("Name");
        this.Property(t => t.ClientId).HasColumnName("ClientId");

        // Relationships
        this.HasOptional(t => t.Client)
            .WithMany(t => t.Surveys).HasForeignKey(d => d.ClientId);
    }

    #endregion
}

Ответ 3

Entity Framework делает именно то, что он сказал. То, что вы сказали, это то, что между Клиентами и Обзорами существуют отношения "один ко многим" и "один к одному". Он сгенерировал оба FK в таблице Survey, чтобы сопоставить оба запрошенных вами отношения. Он не подозревает, что вы пытаетесь связать эти два отношения вместе, и я не думаю, что у него есть возможность справиться с этим.

В качестве альтернативы вам может потребоваться добавить поле IsDefaultSurvey объекта Survey, чтобы вы могли запросить опрос по умолчанию с помощью коллекции Surveys, которую вы имеете на объекте Client. Вы даже можете сделать еще один шаг и поместить его в качестве свойства NotMapped на объект Client, чтобы вы могли использовать Client.DefaultSurvey для получения правильного опроса и не должны изменять какой-либо другой код следующим образом:

[NotMapped]
public Survey DefaultSurvey
{
  get { return this.Surveys.First(s => s.IsDefaultSurvey); }
}

Ответ 4

Обратите внимание, что при добавлении кода ниже вы устраните проблему.

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext() : base("DefaultConnection")
    {

    }
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
          modelBuilder.Entity<Client>()
                      .HasOptional(x => x.DefaultSurvey)
                      .WithMany(x => x.Surveys);
                      .HasForeignKey(p => p.DefaultSurveyID);
    {
}