EF Core 2: Problem with creating mapping with deletebehaviour.Restrict - ef-code-first

I have the following two tables:
-Table1 (Principal)
-Table2
Entities:
public partial class Table2
{
public int Table2Id{ get; set; }
public int Tabl1Id{ get; set; }
public virtual Table1 Table1 { get; set; }
}
public partial class Table1
{
public virtual ICollection<Table2> Table2Items { get; set; }
}
For this I create the following mapping:
modelBuilder.Entity<Table2>()
.HasOne(e => e.Table1 )
.WithMany(e => e.Table2Items)
.HasForeignKey(e => e.Table1Id)
.OnDelete(DeleteBehavior.Restrict);
This gives me the following piece of code in the migration file:
migrationBuilder.AddForeignKey(
name: "FK_Table2_Table1_Table1Id",
table: "Table2",
column: "Table1Id",
principalTable: "Table1",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
How come that my migration is still resolving the cascade behaviour for this relationship?

Related

EF core optional relationship not working

I have the following (unorthodox) setup:
public class OrderShipment
{
public Guid Id { get; set; }
...
public string Number { get; set; }
}
public class LogisticsAdministrationLine
{
public Guid Id { get; set; }
...
public string OrderShipmentNumber { get; set; }
public OrderShipment OrderShipment { get; set; }
}
modelBuilder.Entity<OrderShipment>(e =>
{
e.HasMany(x => x.LogisticsAdministrationLines)
.WithOne(x => x.OrderShipment)
.HasForeignKey(x => x.OrderShipmentNumber)
.HasPrincipalKey(x => x.Number)
.OnDelete(DeleteBehavior.NoAction);
});
Since both keys are string which is nullable this should be an optional relationship that shouldn't really be enforced in the database.
My issue here is that OrderShipments are frequently deleted and recreated, but the LogisticsAdministrationLines remain. This is why i want an optional relationship.
Unfortunately when now deleting an Ordershipment i get the following error:
Microsoft.Data.SqlClient.SqlException (0x80131904):
The DELETE statement conflicted with the REFERENCE constraint "FK_LogisticsAdministrationLines_OrderShipments_OrderShipmentNumber".
The conflict occurred in database "REDACTED", table "dbo.LogisticsAdministrationLines", column 'OrderShipmentNumber'.
How can i properly create an optional relationship between my 2 entities?

Entity Framework Core 3.1 Code-First Model - Define model of having multiple many to many relation with self

I like to create a DB model in ASP.Net Core 3.1. I am using Code First approach with EF Core 3.1.
I like to create a model for this relationship-
So, there is one Employee table and every employee has multiple bosses and each has multiple sub-ordinates. But every boss and every subordinate are employees also. What I have done is something like this-
Employee Model-
public class Employee
{
[HiddenInput(DisplayValue = false), Display(Name = "ID")]
[Key()]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column(Order = 1)]
public Guid? Id { get; set; } = Guid.NewGuid();
[Column("Name"), Required(ErrorMessage = "Term Name should be given"), Display(Name = "Term Name", Prompt = "Please Give Term Name")]
[DataType(DataType.Text)]
public string Name { get; set; }
public ICollection<Boss> Bosses { get; set; }
public ICollection<Subordinate> Subordinates { get; set; }
............
............
}
But I am getting this error during creating the DB model by the command Add-Migration <MigrationName>-
Unable to determine the relationship represented by navigation property 'Employee.Bosses' of type 'ICollection'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Can anyone please help?
direct many-to-many relations are not supported with ef core 3.1.
See this: https://learn.microsoft.com/de-de/ef/core/what-is-new/ef-core-5.0/whatsnew
If you cannot use ef core >= 5, than you have to create a navigation property to the EmployeeBoss entity.
Try this:
public partial class Employee
{
public Employee()
{
EmployeeBossEmployees = new HashSet<EmpoyeeBoss>();
EmployeeBossBosses = new HashSet<EmpoyeeBoss>();
}
[Key]
public int Id { get; set; }
[InverseProperty(nameof(EmpoyeeBoss.Employee))]
public virtual ICollection<EmpoyeeBoss> EmployeeBossEmployees { get; set; }
[InverseProperty(nameof(EmpoyeeBoss.Boss))]
public virtual ICollection<EmpoyeeBoss> EmployeeBossBosses { get; set; }
}
public partial class EmpoyeeBoss
{
[Key]
public int Id { get; set; }
public int BossId { get; set; }
public int EmployeeId { get; set; }
[ForeignKey(nameof(EmployeeId))]
[InverseProperty("EmployeeBossEmployees")]
public virtual Employee Employee { get; set; }
[ForeignKey(nameof(BossId))]
[InverseProperty("EmployeeBossBosses")]
public virtual Employee Boss { get; set; }
}
and include in your dbcontext:
public virtual DbSet<Employee> Employees { get; set; }
public virtual DbSet<EmpoyeeBoss> EmployeeBosses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<EmpoyeeBoss>(entity =>
{
entity.HasOne(d => d.Employee)
.WithMany(p => p.EmployeeBossEmployees)
.HasForeignKey(d => d.EmployeeId)
.OnDelete(DeleteBehavior.ClientSetNull);
entity.HasOne(d => d.Boss)
.WithMany(p => p.EmployeeBossBosses)
.HasForeignKey(d => d.BossId);
});

Entity Framework Core many-to-many relation - getting error "Invalid column name 'id'"

I'm doing a triple many-to-many relationship in the same relationship table in Entity Framework Core.
The certificate, language and candidate entities are related to each other from many-to-many in the CandidateLanguageCertificationLanguage relationship table.
The database tables are:
CREATE TABLE candidato.Candidato
(
ID_CANDIDATO int ,
NOMBRE VARCHAR(50) ,
PRIMARY KEY(ID_CANDIDATO)
);
CREATE TABLE candidato.Idiomaa
(
ID_IDIOMA int ,
NOMBRE VARCHAR(50) ,
PRIMARY KEY(ID_IDIOMA)
);
CREATE TABLE candidato.CertificadoIdiomaa
(
ID_CERTIFICADO_IDIOMA int,
NOMBRE VARCHAR(50),
PRIMARY KEY(ID_CERTIFICADO_IDIOMA)
);
CREATE TABLE candidato.CandidatoIdiomaCertificacionIdiomaa
(
ID_CANDIDATO int,
ID_IDIOMA int,
ID_CERTIFICADO_IDIOMA int,
PRIMARY KEY(ID_CANDIDATO, ID_IDIOMA, ID_CERTIFICADO_IDIOMA),
FOREIGN KEY (ID_CANDIDATO) REFERENCES candidato.Candidato (id_candidato),
FOREIGN KEY (ID_IDIOMA) REFERENCES candidato.Idiomaa (ID_IDIOMA),
FOREIGN KEY (ID_CERTIFICADO_IDIOMA) REFERENCES candidato.CertificadoIdiomaa (ID_CERTIFICADO_IDIOMA)
);
The relationships established in the context through Fluent API are:
builder.Entity<CandidatoIdiomaCertificacionIdiomaa>().ToTable("CandidatoIdiomaCertificacionIdiomaa", "candidato");
builder.Entity<CandidatoIdiomaCertificacionIdiomaa>().HasKey(cici => new { cici.IdCandidato, cici.IdIdioma, cici.IdCertificadoIdioma });
builder.Entity<CandidatoIdiomaCertificacionIdiomaa>().Property(cici => cici.IdCandidato).HasColumnName("Id_Candidato");
builder.Entity<CandidatoIdiomaCertificacionIdiomaa>().Property(cici => cici.IdIdioma).HasColumnName("Id_Idioma");
builder.Entity<CandidatoIdiomaCertificacionIdiomaa>().Property(cici => cici.IdCertificadoIdioma).HasColumnName("Id_Certificado_Idioma");
builder.Entity<CertificadoIdiomaa>().ToTable("CertificadoIdiomaa", "candidato");
builder.Entity<CertificadoIdiomaa>().HasKey(ci => ci.Id);
builder.Entity<CertificadoIdiomaa>().Property(ci => ci.Id).HasColumnName("Id_Certificado_Idioma").ValueGeneratedOnAdd();
builder.Entity<CertificadoIdiomaa>().Property(ci => ci.Nombre).HasColumnName("Nombre");
builder.Entity<Idiomaa>().ToTable("Idiomaa", "candidato");
builder.Entity<Idiomaa>().HasKey(i => i.Id);
builder.Entity<Idiomaa>().Property(i => i.Id).HasColumnName("Id_Candidato").ValueGeneratedOnAdd();
builder.Entity<Idiomaa>().Property(i => i.Nombre).HasColumnName("Nombre");
builder.Entity<Candidato>().ToTable("Candidato", "candidato");
builder.Entity<Candidato>().HasKey(i => i.Id);
builder.Entity<Candidato>().Property(i => i.Id).HasColumnName("Id_Candidato").ValueGeneratedOnAdd();
builder.Entity<Candidato>().Property(i => i.Nombre).HasColumnName("Nombre");
builder.Entity<CandidatoIdiomaCertificacionIdiomaa>()
.HasOne(cici => cici.CertificadoIdioma)
.WithMany(cei => cei.CandidatosIdiomasCertificacionesIdiomaas)
.HasForeignKey(cf => cf.IdCertificadoIdioma);
builder.Entity<CandidatoIdiomaCertificacionIdiomaa>()
.HasOne(cici => cici.Candidato)
.WithMany(c => c.CandidatosIdiomasCertificacionesIdiomaas)
.HasForeignKey(cici => cici.IdCandidato);
builder.Entity<CandidatoIdiomaCertificacionIdiomaa>()
.HasOne(cici => cici.Idioma)
.WithMany(cei => cei.CandidatosIdiomasCertificacionesIdiomaas)
.HasForeignKey(cf => cf.IdIdioma);
The models are:
public class Idiomaa
{
#region propiedades
public int Id { get; internal set; }
public string Nombre { get; internal set; }
//public ICollection<CandidatoIdiomaa> CandidatosIdiomas { get; internal set; }
public ICollection<CandidatoIdiomaCertificacionIdiomaa> CandidatosIdiomasCertificacionesIdiomaas { get; internal set; }
#endregion
}
public class CertificadoIdiomaa
{
public int Id{ get; internal set; }
public string Nombre { get; internal set; }
public ICollection<CandidatoIdiomaCertificacionIdiomaa> CandidatosIdiomasCertificacionesIdiomaas { get; internal set; }
}
public class CandidatoIdiomaCertificacionIdiomaa
{
#region propiedades
public int IdCandidato { get; internal set; }
public int IdIdioma { get; internal set; }
public int IdCertificadoIdioma { get; internal set; }
public CertificadoIdiomaa CertificadoIdioma { get; internal set; }
public Candidato Candidato { get; internal set; }
public Idiomaa Idioma { get; internal set; }
#endregion
}
public class Candidato
{
#region propiedades
public int Id { get; internal set; }
public string CorreoElectronico { get; internal set; }
#endregion
}
The query I wrote is:
contexto.Where(certificadoIdioma => certificadoIdioma.Nombre.Contains(filtro))
.Include(ci => ci.CandidatosIdiomasCertificacionesIdiomaas)
.ThenInclude(cici => cici.Idioma).ToList();
The error I get:
Invalid column name 'Id_Candidato'
Invalid column name 'Id_Candidato'
The funny thing is that if I change the include of the query to candidate it works perfect:
contexto.Where(certificadoIdioma => certificadoIdioma.Nombre.Contains(filtro))
.Include(ci => ci.CandidatosIdiomasCertificacionesIdiomaas)
.ThenInclude(cici => cici.candidato).ToList();
The tables have been made by a DBA, I am a programmer and I cannot modify the database, I know that you could make the relations in several tables instead of one, your reasons will be, that's another story. I am sure that in Entity Framework Core this can be done but I do not know how, I suspect that I have made a mistake when setting up the relationships in Fluent API, but I have been reviewing it for hours and nothing ...
Could someone take a look at it?
Thank you so much guys.
regards
You are missing int keyword:
public class Idiomaa
{
#region propiedades
public Id { get; internal set; } here int keyword is missing
public string Nombre { get; internal set; }
//public ICollection<CandidatoIdiomaa> CandidatosIdiomas { get; internal set; }
public ICollection<CandidatoIdiomaCertificacionIdiomaa> CandidatosIdiomasCertificacionesIdiomaas { get; internal set; }
#endregion
}
public Id { get; internal set; } here int keyword is missing

How to define two FK in the same table?

I have a table called User which inherit the properties from IdentityUser, inside that table I added a reference to the UserFriendship table which need to store all the user friendship:
public class User : IdentityUser
{
public string FirstName { get; set; }
public DateTime BirthDate { get; set; }
public virtual ICollection<UserFriendship> UserFriendship { get; set; }
}
Essentially the UserFriendship contains two users, who are those who have a common friendship, this is the model definition:
public class UserFriendship
{
public int Id { get; set; }
[Key, ForeignKey("UserA")]
public string UserAId { get; set; }
public User UserA { get; set; }
[Key, ForeignKey("UserB")]
public string UserBId { get; set; }
public User UserB { get; set; }
[Required]
public DateTime DateTime { get; set; }
}
I defined the UserA and the UserB which are two FK of a User that are contained inside AspNetUsers table.
Now inside the FluentAPI I declared the following:
builder.Entity<UserFriendship>(entity =>
{
entity.HasKey(f => f.Id);
entity.HasOne(u => u.UserA)
.WithMany(n => n.UserFriendships)
.HasForeignKey(u => u.UserAId)
.IsRequired();
entity.HasOne(u => u.UserB)
.WithMany(n => n.UserFriendships)
.HasForeignKey(u => u.UserBId)
.IsRequired();
});
when I execute this command:
add-migration InitialMigration -context MyAppContext
I'll get:
Cannot create a relationship between 'User.UserFriendships' and 'UserFriendship.UserB', because there already is a relationship between 'User.UserFriendships' and 'UserFriendship.UserA'. Navigation properties can only participate in a single relationship.
I'm not an expert of EnityFramework, but based on that error I think that I cannot define two FK in the same table?
Sorry for any mistake, thanks.
You can define more than one FK in table.
The problem here is you are pointing two times to one navigation property - UserFriendships. The solution would be to create two navigation properties.
Navigation properties are used to browse the related data for specified foreign-key (you have one-to-many relationship) of entity.
Try this:
public class User
{
public string FirstName { get; set; }
public DateTime BirthDate { get; set; }
public ICollection<UserFriendship> UserAFriendships { get; set; }
public ICollection<UserFriendship> UserBFriendships { get; set; }
}
public class UserFriendship
{
public int Id { get; set; }
public string UserAId { get; set; }
public User UserA { get; set; }
public string UserBId { get; set; }
public User UserB { get; set; }
public DateTime DateTime { get; set; }
}
And define the relationship through fluent api as following:
modelBuilder.Entity<UserFriendship>(entity =>
{
entity.HasKey(f => f.Id);
entity.HasOne(u => u.UserA)
.WithMany(n => n.UserAFriendships)
.HasForeignKey(u => u.UserAId)
.IsRequired();
entity.HasOne(u => u.UserB)
.WithMany(n => n.UserBFriendships)
.HasForeignKey(u => u.UserBId)
.IsRequired();
});
What is more - you don't need to specify attributes Key, ForeignKey if you use Fluent API.

How to refer two tables, using a column in another table - Entity Framework

As per the image above, I have a requirement where, Table C needs to refer both Table A and Table B.
Note: RefId in Table_C is a reference key for both Tables A and B.
Please, refer the code snippets,
Table_A Class
public partial class Table_A
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Table_C> Table_C { get; set; }
}
Table_B Class
public partial class Table_B
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Table_C> Table_C { get; set; }
}
Table_C Class
public partial class Table_C
{
public int Id { get; set; }
public int RefId { get; set; }
public Type Type {get; set; }
public virtual Table_A Table_A { get; set; }
public virtual Table_B Table_B { get; set; }
}
Fluent API
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Table_1>()
.Property(e => e.Name)
.IsFixedLength();
modelBuilder.Entity<Table_1>()
.HasMany(e => e.Table_3)
.WithRequired(e => e.Table_1)
.HasForeignKey(e => e.RefId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Table_2>()
.Property(e => e.Name)
.IsFixedLength();
modelBuilder.Entity<Table_2>()
.HasMany(e => e.Table_3)
.WithRequired(e => e.Table_2)
.HasForeignKey(e => e.RefId)
.WillCascadeOnDelete(false);
}
However, the below error occurs when trying to achieve this requirement using the code shown above.
The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_dbo.TableC.TableA_Id". The conflict occurred in database "TestDB",
table "dbo.TableA", column 'Id'. The statement has been terminated
How can this be implemented using Entity Framework 6 (SQL Server 2014, .NET framework 4.6.1)?
Seems to work fine for me:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Data.Entity;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ef6Test
{
public partial class Table_A
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Table_C> Table_C { get; } = new HashSet<Table_C>();
}
//Table_B Class
public partial class Table_B
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Table_C> Table_C { get; } = new HashSet<Table_C>();
}
//Table_C Class
public partial class Table_C
{
public int Id { get; set; }
public int RefId { get; set; }
public string Type { get; set; }
public virtual Table_A Table_A { get; set; }
public virtual Table_B Table_B { get; set; }
}
class Db: DbContext
{
public DbSet<Table_A> Table_A { get; set; }
public DbSet<Table_B> Table_B { get; set; }
public DbSet<Table_C> Table_C { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Table_A>()
.HasMany(e => e.Table_C)
.WithRequired(e => e.Table_A)
.HasForeignKey(e => e.RefId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Table_B>()
.HasMany(e => e.Table_C)
.WithRequired(e => e.Table_B)
.HasForeignKey(e => e.RefId)
.WillCascadeOnDelete(false);
}
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<Db>());
using (var db = new Db())
{
db.Database.Log = m => Console.WriteLine(m);
db.Database.Initialize(true);
var a = new Table_A();
var b = new Table_B();
var c = new Table_C();
a.Table_C.Add(c);
b.Table_C.Add(c);
db.Table_A.Add(a);
db.Table_B.Add(b);
db.Table_C.Add(c);
db.SaveChanges();
}
Console.ReadKey();
}
}
}
I managed to fulfill this requirement using the comments made by #David Browne - Microsoft. Therefore, usage of multiple columns to refer multiple related tables is the approach for this sort of a scenario. Hope this will help anyone looking for an answer to a question of this nature. Thanks #David Browne - Microsoft for the valuable input.

Resources