Как исправить: количество свойств в зависимых и основных ролях в ограничении отношений должно быть одинаковым?
Я использую Entity Framework 4.3.1 для базы данных SQL Server 2012, и я использую подход POCO. Я получаю следующую ошибку, и мне интересно, может ли кто-нибудь объяснить, как ее исправить:
ModelValidationException
Во время генерации модели были обнаружены одна или несколько ошибок проверки: \ tSystem.Data.Entity.Edm.EdmAssociationConstraint:: Число свойств в зависимых и основных ролях в ограничении отношений должно быть идентичным.
Для любой дополнительной информации нет InnerException
.
Я не могу изменить схему базы данных, и она немного странная, но вот она...
- ** - первичный ключ (обратите внимание, что у меня есть составные первичные ключи)
- (FK) Обозначает внешний ключ
Вот таблицы (если это помогает, я могу опубликовать SQL для их создания, но я не думаю, что на самом деле эти таблицы являются проблемой, поскольку исключение находится в проверке модели):
One
-
**OneId int not null
**TwoId int not null (FK)
**ThreeId int not null (FK)
Name nvarchar(50) not null
Two
-
**TwoId int not null
**ThreeId int not null (FK)
Name nvarchar(50) not null
Three
-
**ThreeId not null
Name nvarchar(50) not null
Вот сущности (обратите внимание, что я включаю в себя внешние ключи в модели, но не такие стандартные):
public class Three
{
public int ThreeId { get; set; }
public string Name { get; set; }
public virtual ICollection<Two> Twos { get; private set; }
public virtual ICollection<One> Ones { get; private set; }
public void AddOne(One one)
{
if (one == null)
throw new ArgumentNullException("two");
if (Ones == null)
Ones = new List<One>();
if (!Ones.Contains(one))
Ones.Add(one);
one.Three = this;
}
public void AddTwo(Two two)
{
if (two == null)
throw new ArgumentNullException("two");
if (Twos == null)
Twos = new List<Two>();
if (!Twos.Contains(two))
Twos.Add(two);
two.Three = this;
}
}
public class Two
{
public int TwoId { get; set; }
public int ThreeId { get; set; }
public string Name { get; set; }
public virtual Three Three { get; set; }
public virtual ICollection<One> Ones { get; private set; }
public void AddOne(One one)
{
if (one == null)
throw new ArgumentNullException("two");
if (Ones == null)
Ones = new List<One>();
if (!Ones.Contains(one))
Ones.Add(one);
one.Two = this;
}
}
public class One
{
public int OneId { get; set; }
public int TwoId { get; set; }
public int ThreeId { get; set; }
public virtual Two Two { get; set; }
public virtual Three Three { get; set; }
}
И вот контекст данных:
public class DbCtx : DbContext
{
public DbCtx(string connectionString)
: base(connectionString)
{
Ones = Set<One>();
Twos = Set<Two>();
Threes = Set<Three>();
}
public DbSet<One> Ones { get; private set; }
public DbSet<Two> Twos { get; private set; }
public DbSet<Three> Threes { get; private set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var one = modelBuilder.Entity<One>();
one.ToTable("One");
one.HasKey(d => new
{
d.OneId,
d.TwoId,
d.ThreeId
});
one.Property(d => d.OneId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
one.HasRequired(t => t.Two)
.WithMany(s => s.Ones)
.HasForeignKey(t => t.TwoId);
one.HasRequired(t => t.Three)
.WithMany(s => s.Ones)
.HasForeignKey(t => t.ThreeId);
var two = modelBuilder.Entity<Two>();
two.ToTable("Two");
two.HasKey(d => new
{
d.TwoId,
d.ThreeId
});
two.Property(p => p.TwoId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
two.HasRequired(t => t.Three)
.WithMany(s => s.Twos)
.HasForeignKey(t => t.ThreeId);
var three = modelBuilder.Entity<Three>();
three.ToTable("Three");
three.HasKey(s => s.ThreeId);
three.Property(p => p.ThreeId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
base.OnModelCreating(modelBuilder);
}
}
Наконец, это фрагмент кода, вызывающий исключение:
using (var ctx = new DbCtx(@"....."))
{
Console.WriteLine(ctx.Twos.Count());
}
Ответы
Ответ 1
Причиной ошибки являются неверно настроенные отношения в вашей модели. Это неверно:
one.HasRequired(t => t.Two)
.WithMany(s => s.Ones)
.HasForeignKey(t => t.TwoId);
one.HasRequired(t => t.Three)
.WithMany(s => s.Ones)
.HasForeignKey(t => t.ThreeId);
Это должно быть:
one.HasRequired(t => t.Two)
.WithMany(s => s.Ones)
.HasForeignKey(t => new { t.TwoId, t.ThreeId });
Поскольку зависимый FK должен содержать все столбцы основного PK. Вы также должны удалить свойство навигации с Three
до One
.
Ответ 2
Примечание для EF5 +:
.HasForeignKey устарел из EF 5: Список доступных методов (https://msdn.microsoft.com/en-us/library/system.data.entity.modelconfiguration.configuration.manytomanyassociationmappingconfiguration_methods(v=vs.103).aspx)
- MapLeftKey
- MapRightKey
- ToTable
Если кому-то понадобится много для многих, где одно "много" относится к сущности с композитным ключом:
one.HasKey(t => new { t.TwoId, t.ThreeId });
one.HasRequired(t => t.Two)
.WithMany(s => s.Ones)
.Map(m=>m.MapLeftKey("OneId").MapRIghtKey(new string[]{"TwoId", "ThreeId"}))
Ответ 3
Это также может быть вызвано Код сначала из базы данных.
У меня было несколько просмотров, которые я привел, у которых не было очевидного ключевого поля в соответствии с соглашениями Entity Framework. Созданный код помещает атрибут [Key]
в неправильное поле. Фактически, он не мог обнаружить никакой уникальности, поэтому он помещает атрибут [Key]
во все поля.
Мне удалось удалить все дополнительные атрибуты Key, чтобы ошибка исчезла.