tutorial mvc migrations framework first español code c# database entity-framework ef-code-first entity-framework-6

c# - mvc - Código EF: primera relación uno a uno: la multiplicidad no es válida en el rol*en la relación



entity framework database first español (3)

Estoy intentando hacer lo siguiente:

public class class1 { public int Id {get;set;} [ForeignKey("Class2")] public int Class2Id {get;set;} public virtual Class2 Class2 {get;set;} } public class class2 { public int Id { get; set;} [Required] public virtual int Class1Id {get;set;} [Required] [ForeignKey("Class1Id")] public Class1 Class1 {get;set;} }

Sin embargo, cada vez que intento migrar mi base de datos aparece el siguiente error:

Class1_Class2_Target:: La multiplicidad no es válida en el rol ''Class2_Class1_Target'' en la relación ''Class2_Class1''. Debido a que las propiedades del Rol dependiente no son las propiedades clave, el límite superior de la multiplicidad del Rol dependiente debe ser ''*''.

¿Cuál podría ser el problema aquí?


Su modelo no es una asociación 1: 1. Todavía puede tener muchos objetos Class2 que se refieren al mismo objeto Class1 . Además, su modelo no garantiza que un objeto Class1 haga referencia a una Class2 referencia a una Class1 . Class1 puede referirse a cualquier objeto Class2 .

¿Cómo configurar 1: 1?

La forma común de garantizar (más o menos) una asociación 1: 1 en SQL es tener una tabla para la entidad principal y otra para la entidad dependiente donde la clave primaria en la tabla dependiente también es una clave externa para la principal:

(Aquí la Class1 es la principal)

Ahora en una base de datos relacional, esto todavía no garantiza una asociación 1: 1 (es por eso que dije ''tipo de''). Es una asociación 1: 0..1 . Puede haber una Class1 sin una Class2 . La verdad es que las asociaciones genuinas 1: 1 son imposibles en SQL, porque no hay una construcción de lenguaje que inserte dos filas en diferentes tablas sincrónicamente. 1: 0..1 es lo más cercano que tenemos.

Mapeo fluido

Para modelar esta asociación en EF, puede usar la API fluida. Aquí está la forma estándar de hacerlo:

class Class1Map : EntityTypeConfiguration<Class1> { public Class1Map() { this.HasKey(c => c.Id); this.Property(c => c.Id) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); this.HasRequired(c1 => c1.Class2).WithRequiredPrincipal(c2 => c2.Class1); } }

Y en el contexto:

protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new Class1Map()); }

Y esto queda de tus clases:

public class Class1 { public int Id {get;set;} public virtual Class2 Class2 {get;set;} } public class Class2 { public int Id {get;set;} public virtual Class1 Class1 {get;set;} }

No hay forma de configurar propiedades de clave externa alternativas en el modelo, porque la única FK involucrada tiene que ser la clave principal del dependiente.

Lo extraño de este modelo es que EF no te impide crear (y guardar) un objeto de class2 sin una class2 . Creo que EF debería ser capaz de validar este requisito antes de guardar los cambios, pero, aparentemente, no lo hace. Del mismo modo, hay formas de eliminar un objeto de class2 sin eliminar su padre class1 . Por lo tanto, este par HasRequired - WithRequired no es tan estricto como parece (y debería ser).

Anotaciones de datos

La única forma de hacerlo bien en el código es mediante anotaciones de datos. (Por supuesto, el modelo de base de datos aún no podrá aplicar 1: 1)

public class Class1 { public int Id {get;set;} [Required] public virtual Class2 Class2 {get;set;} } public class Class2 { [Key, ForeignKey("Class1")] public int Id {get;set;} [Required] public virtual Class1 Class1 {get;set;} }

La anotación [Key, ForeignKey("Class1")] le dice a EF que Class1 es la entidad principal.

Las anotaciones de datos juegan un papel en muchas API, lo que puede ser una maldición, porque cada API elige su propio subconjunto para implementar, pero aquí resulta útil, porque ahora EF no solo las usa para diseñar el modelo de datos, sino también para validar entidades . Ahora, si intenta guardar un objeto de class2 sin una class2 , obtendrá un error de validación.


Tuve exactamente el mismo problema. Lo que quería es que el esquema de DB tuviera 2 tablas que se crucen entre sí con [clave externa] -> [clave primaria]. Finalmente encontré el camino: Digamos que tenemos 2 clases: Libros y Autores. La clase Libro debería tener una clave externa para el autor que lo creó y la clase Autor debería tener una clave externa para el último libro que escribió. La manera de hacer que EF entienda esto usando el código primero es: (Tenga en cuenta que esto se hace usando una mezcla de anotaciones de datos y API fluida)

public class Book { ... public Guid BookId ... public Guid AuthorId { get; set; } [ForeignKey("AuthorId")] public virtual Author author { get; set; } } public class Author { ... public Guid AuthorId ... public Guid? LatestBookId { get; set; } [ForeignKey("LatestBookId")] public virtual Book book { get; set; } public virtual ICollection<Book> books { get; set; } } // using fluent API class BookConfiguration : EntityTypeConfiguration<Book> { public BookConfiguration() { this.HasRequired(b => b.author) .WithMany(a => a.books); } }

Esto funciona y crea el esquema de base de datos exacto que quería. En SQL crearía tablas y claves foráneas correspondientes al siguiente código:

CREATE TABLE [dbo].[Book]( [BookId] [uniqueidentifier] NOT NULL, [AuthorId] [uniqueidentifier] NOT NULL, ... CONSTRAINT [PK_dbo.Book] PRIMARY KEY CLUSTERED ( [BookId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] ... GO ALTER TABLE [dbo].[Book] WITH CHECK ADD CONSTRAINT [FK_dbo.Book.Author_AuthorId] FOREIGN KEY([AuthorId]) REFERENCES [dbo].[Author] ([AuthorId]) GO ... CREATE TABLE [dbo].[Author]( [AuthorId] [uniqueidentifier] NOT NULL, [LatestBookId] [uniqueidentifier] NULL, ... CONSTRAINT [PK_dbo.Author] PRIMARY KEY CLUSTERED ( [AuthorId] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] ... GO ALTER TABLE [dbo].[Author] WITH CHECK ADD CONSTRAINT [FK_dbo.Author_dbo.Book_LatestBookId] FOREIGN KEY([LatestBookId]) REFERENCES [dbo].[Book] ([BookId]) GO ...


Una de las dos clases debe crearse antes que la otra y, por lo tanto, requiere la anotación [Obligatoria]. Si Class2 depende de Class1, especifique [Obligatorio, ForeignKey ("Class1")]. También puede usar una API fluida para configurar esto en su clase de contexto también.