I am having trouble with SQL Server creating Foreign Key constraints when using Entity Framework Code First.
This is my scenario. I am building an application which allows us to log tickets against any of our systems and automatically assign the ticket to the relevant person.
We have Services, which can have many categories. The categories can have many subcategories. A help desk person can be assigned to Service, and Category or Subcategory.
Here are my classes:
Service.cs
public class Service
{
[Key]
public int ServiceID { get; set; }
public string Title { get; set; }
public DateTime CreatedDate { get; set; }
public HelpDeskMember CreatedBy { get; set; }
public DateTime? DeletedDate { get; set; }
public HelpDeskMember DeletedBy { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<Subcategory> Subcategories { get; set; }
public virtual ICollection<HelpDeskMember> LinesOfSupport { get; set; }
}
Category.cs
public class Category
{
[Key]
public int CategoryID { get; set; }
[ForeignKey("Service")]
public int ServiceID { get; set; }
public string Title { get; set; }
public DateTime CreatedDate { get; set; }
public HelpDeskMember CreatedBy { get; set; }
public DateTime? DeletedDate { get; set; }
public HelpDeskMember DeletedBy { get; set; }
public virtual Service Service { get; set; }
public virtual ICollection<Subcategory> Subcategories { get; set; }
public virtual ICollection<HelpDeskMember> LinesOfSupport { get; set; }
}
Subcategory.cs
public class Subcategory
{
[Key]
public int SubcategoryID { get; set; }
[ForeignKey("Service")]
public int ServiceID { get; set; }
[ForeignKey("Category")]
public int CategoryID { get; set; }
public string Title { get; set; }
public DateTime CreatedDate { get; set; }
public HelpDeskMember CreatedBy { get; set; }
public DateTime? DeletedDate { get; set; }
public HelpDeskMember DeletedBy { get; set; }
public virtual Service Service { get; set; }
public virtual Category Category { get; set; }
public virtual ICollection<HelpDeskMember> LinesOfSupport { get; set; }
}
and finally HelpDeskMember.cs
public class HelpDeskMember
{
public int HelpDeskMemberID { get; set; }
public string Name { get; set; }
public bool Admin { get; set; }
public bool Available { get; set; }
public DateTime? CreatedDate { get; set; }
public DateTime? LastLogin { get; set; }
public DateTime? DeletedDate { get; set; }
public DateTime? DeletedBy { get; set; }
public virtual ICollection<Service> Services { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<Subcategory> Subcategories { get; set; }
}
When the Database is being initialised, I am getting the following error message:
Introducing FOREIGN KEY constraint 'Subcategory_Service' on table 'Subcategories' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint. See previous errors.
I'm guessing the problem is with how I have defined the Key's and ForeignKey's. Any help appreciated. Thanks.
Depending on your needs. You can in configuration for your entity use WillCascadeOnDelete(false)1 or globally removing OneToManyCascadeDeleteConvention2.
Both can be set in OnModelCreating using ModelBuilder input parameter.
Related
I'm still studying Entity Framework and tried to create a model including the foreign keys.
But when I tried to migrate the code, I got this error
Introducing FOREIGN KEY constraint 'FK_dbo.QuestionResults_dbo.QuestionsTables_QuetionsTableId' on table 'QuestionResults' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints
These are my model classes:
public class MainDetails
{
[Key]
public int Id { get; set; }
public string Language { get; set; }
[Required]
public string CustomerName { get; set; }
[Required]
public string ContactNumber { get; set; }
public string EmailAddress { get; set; }
[DisplayName("Service Type")]
[ForeignKey("QuestionsTable")]
public int ServiceTypeId { get; set; }
public virtual QuestionsTable QuestionsTable { get; set; }
[Required]
public string VehicleNumber { get; set; }
[Required]
public string ServiceLocation { get; set; }
public string Suggestion { get; set; }
public bool Status { get; set; } = true;
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Created Date")]
public DateTime CreatedDate { get; set; } = DateTime.Now;
public virtual QuestionResults QuestionResults { get; set; }
public virtual IList<QuestionResults> QuestionResultsMainlist { get; set; }
public virtual IList<QuestionsTable> QuestionsTables { get; set; }
}
public class QuestionsTable
{
[Key]
public int Id { get; set; }
public string ServiceType { get; set; }
public string Question { get; set; }
public virtual IList<MainDetails> MainDetailsServiceType { get; set; }
public QuestionsTable()
{
MainDetailsServiceType = new List<MainDetails>();
}
}
public class QuestionResults
{
[Key]
public int Id { get; set; }
[DisplayName("MainDetail ID")]
[ForeignKey("MainDetails")]
public int MainDetailsId { get; set; }
public virtual MainDetails MainDetails { get; set; }
[DisplayName("MainDetail ID")]
[ForeignKey("QuestionsTable")]
public int QuetionsTableId { get; set; }
public virtual QuestionsTable QuestionsTable { get; set; }
[Required]
public string CustoAnswer { get; set; }
}
This is the table structure I wanted to create:
To resolve this you can use the EF Model Builder
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<QuestionsTable>()
.HasRequired(a => a.MainDetails)
.WithOptionalDependent()
.WillCascadeOnDelete(false); //This is the important row
}
You'll have to play around with the type of relationship you would like along with whether you specify explicitly the foreign key. If you haven't seen this model builder before have a read here: https://learn.microsoft.com/en-us/ef/core/modeling/
Upon breaking your scenario down I noticed some "oddities". I reduced the noise in the domain model to from your example to this
public class MainDetails
{
[Key]
public int Id { get; set; }
[ForeignKey("QuestionsTable")]
public int ServiceTypeId { get; set; }
public virtual QuestionsTable QuestionsTable { get; set; }
public virtual QuestionResults QuestionResults { get; set; }
public virtual IList<QuestionResults> QuestionResultsMainlist { get; set; }
public virtual IList<QuestionsTable> QuestionsTables { get; set; }
}
public class QuestionsTable
{
[Key]
public int Id { get; set; }
public string ServiceType { get; set; }
public string Question { get; set; }
public virtual IList<MainDetails> MainDetailsServiceType { get; set; }
}
public class QuestionResults
{
[Key]
public int Id { get; set; }
[ForeignKey("MainDetails")]
public int MainDetailsId { get; set; }
public virtual MainDetails MainDetails { get; set; }
[ForeignKey("QuestionsTable")]
public int QuetionsTableId { get; set; }
public virtual QuestionsTable QuestionsTable { get; set; }
}
I few things I noted.
MainDetails contains both One-To-Many (QuestionTable) and Many-To-Many (IList) relationships? I'm unsure on your intention
QuestionsResults contains singular relationships to both entities which aren't replicated in the QuestionTable class? that's fine if it's intentional
ServiceType is a string in QuestionsTable but you are expecting an int as the foreign key in MainDetails?
I'm using EF Core 2.2
the Code with the error
var ClientCase= _context.Client_Cases.Include(a=>a.Case_Sessions). FirstOrDefault(x => x.Id == id);
The Error
System.Data.SqlClient.SqlException: 'Invalid column name
'Client_CaseId'. Invalid column name 'Case_LevelId'. Invalid column
name 'Client_CaseId'. Invalid column name 'Court_CircleId'. Invalid
column name 'Court_HallId'.'
Entities
1- Parent Client_Case
public class Client_Cases
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
public string Opponent { get; set; }
public DateTime? StartDate { get; set; }
public DateTime Recieve_Date { get; set; }
[ForeignKey("Clients")]
public long? ClientID { get;set;}
public Clients Client { get; set; }
[ForeignKey("Case_Levels")]
public long? LevelID { get; set; }
public virtual Case_Levels Case_Levels { get; set; }
[ForeignKey("Case_Types")]
public long? TypeID { get; set; }
public virtual Case_Types Case_Types { get; set; }
[ForeignKey("Court_Circles")]
public long? CircleID { get; set; }
public virtual Court_Circles Court_Circles { get; set; }
[ForeignKey("Court_Halls")]
public long? HallID { get; set; }
public virtual Court_Halls Court_Halls { get; set; }
[ForeignKey("Courts")]
public long? CourtID { get; set; }
public virtual Courts Court { get; set; }
[ForeignKey("Case_Status")]
public long? StatusID { get; set; }
public Case_Status Case_Status { get; set; }
[ForeignKey("Lawyers")]
public long? LawyerID { get; set; }
public virtual LawyersData Lawyers { get; set; }
public string Description { get; set; }
public string Code { get; set; }
public string CaseNo { get; set; }
public List<Case_Sessions> Case_Sessions { get; set; }
}
Detail Entity Case_Session
public class Case_Sessions
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Id { get; set; }
[ForeignKey("Client_Cases")]
public long? CaseID { get;set;}
public Client_Cases Client_Case { get; set; }
[ForeignKey("Case_Levels")]
public long? LevelID { get; set; }
public Case_Levels Case_Level { get; set; }
[ForeignKey("Court_Circles")]
public long? CircleID { get; set; }
public Court_Circles Court_Circle { get; set; }
[ForeignKey("Court_Halls")]
public long? HallID { get; set; }
public Court_Halls Court_Hall { get; set; }
[ForeignKey("Case_Status")]
public long? StatusID { get; set; }
public Case_Status Case_Status { get; set; }
public DateTime Session_Date { get; set; }
public string Judge_Name { get; set; }
public string Session_Result { get; set; }
public string Notes { get; set; }
}
If I get the parent without including the child it works.
If I get the details, it works.
I know the error that EF Core Create its own naming convention for the Foreign keys
but I think the tag Foreign Key override that naming convention
Now where I am wrong?
[ForeignKey("")] Mean? name the property you have added in class to become a foreign key. e.g:
public long? CaseID { get;set;}
[ForeignKey("CaseID")]
public Client_Cases Client_Case { get; set; }
public long? CircleID { get; set; }
[ForeignKey("CircleID")]
public Court_Circles Court_Circle { get; set; }
You can use annotations like above, In your case, below correction needed:
[ForeignKey("Client")] // it should be [ForeignKey("Client")] not an extra s if you using entities name in annotation.
public long? ClientID { get;set;}
public Clients Client { get; set; }
this should be your relationship for lawyer:
[ForeignKey("Lawyers")]
public long? LawyersID { get; set; }
public virtual LawyersData Lawyers { get; set; }
I am assuming that the type of primary key in LawyersData table is long?.
There are no primary or candidate keys in the referenced table 'dbo.Client_Master' that match the referencing column list in the foreign key 'FK_dbo.Client_Question_Master_dbo.Client_Master_client_id'.
Could not create constraint or index. See previous errors.
My Client_Master Model
public class Client_Master
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long Client_Id { get; set; }
public string Client_Name { get; set; }
public string Client_Address { get; set; }
public string Client_Email { get; set; }
public string Client_Phone { get; set; }
[DefaultValue(" ")]
public string Client_Country { get; set; }
[DefaultValue(" ")]
public string Client_State { get; set; }
[DefaultValue(" ")]
public string Client_Postcode { get; set; }
public bool Is_Active { get; set; }
public long? Created_By { get; set; }
public DateTime? Created_Date { get; set; }
[ForeignKey("Business_Master")]
public long? Business_Id { get; set; }
[ForeignKey("Categories")]
public long? Category_Id { get; set; }
public virtual Categories Categories { get; set; }
public virtual Business_Master Business_Master { get; set; }
[JsonIgnore]
public virtual ICollection<Client_Question_Master> Client_Question_Master { get; set; }
}
And My Client_Question_Master Modal
public class Client_Question_Master
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long client_question_master_id { get; set; }
[ForeignKey("Client_Master")]
public long? client_id { get; set; }
public virtual Client_Master Client_Master { get; set; }
[ForeignKey("Question_Types")]
public long? question_type_id { get; set; }
public virtual Question_Types Question_Types { get; set; }
public string question { get; set; }
public long order_no { get; set; }
public bool isContribute { get; set; } = true;
[ForeignKey("Section_Master")]
public long? section_id { get; set; }
public virtual Section_Master Section_Master { get; set; }
public double amount { get; set; }
public bool isActive { get; set; } = true;
public bool isRequired { get; set; } = true;
public bool isComment { get; set; } = true;
public string values { get; set; }
public bool isRevenue { get; set; }
public bool isStaff { get; set; }
public bool isMarketing { get; set; }
public DateTime created_date { get; set; } = DateTime.Now;
}
After add-migration during updating database it is giving me error.
Your usage of the ForeignKey attribute is the wrong way round, when using a nullable foreign key.
For example you use:
[ForeignKey("Client_Master")]
public long? client_id { get; set; }
public virtual Client_Master Client_Master { get; set; }
However it should be:
public long? client_id { get; set; }
[ForeignKey("client_id")]
public virtual Client_Master Client_Master { get; set; }
You tell EntityFramework what property is the foreign key. This prevent it from creating a field that has the same datatype as the primary key.
Check your edmx file.Edmx may not have that column as Primary key in another table which you are using as foreign key.
This question already has answers here:
Create code first, many to many, with additional fields in association table
(7 answers)
Closed 6 years ago.
I have a many to many relationship defined in my DB between User and Clinic tables. The database wasn't created using EF but I generated the DB model classes using EF. M-M relationship is usually created with EF by having collection object property of the of the linked classes defined within those classes but here I have an indirect relationship. Both User and Clinic models have UserClinic collection object instead of pointing directly to the other class.
Here's how the data objects are defined:
User
public User()
{
this.UserClinics = new HashSet<UserClinic>();
}
public long UsersID { get; set; }
public string AspNetUsersID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Organization { get; set; }
public string Title { get; set; }
public Nullable<System.DateTime> CreateDT { get; set; }
public Nullable<long> CreateBy { get; set; }
public Nullable<System.DateTime> UpdateDT { get; set; }
public Nullable<long> UpdateBy { get; set; }
public string Type { get; set; }
public string LoginID { get; set; }
public virtual AspNetUser AspNetUser { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<UserClinic> UserClinics { get; set; }
}
Clinic
public Clinic()
{
this.UserClinics = new HashSet<UserClinic>();
this.Patients = new HashSet<Patient>();
}
public long ClinicID { get; set; }
public Nullable<long> NetworkID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public Nullable<System.DateTime> CreateDT { get; set; }
public Nullable<long> CreateBy { get; set; }
public Nullable<System.DateTime> UpdateDT { get; set; }
public Nullable<long> UpdateBy { get; set; }
public virtual Network Network { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<UserClinic> UserClinics { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Patient> Patients { get; set; }
}
UserClinic
public partial class UserClinic
{
public long UsersID { get; set; }
public long ClinicID { get; set; }
public Nullable<System.DateTime> CreateDT { get; set; }
public Nullable<long> CreateBy { get; set; }
public Nullable<System.DateTime> UpdateDT { get; set; }
public Nullable<long> UpdateBy { get; set; }
public virtual Clinic Clinic { get; set; }
public virtual User User { get; set; }
}
What's the best way to query objects so I get something to the effect of:
var cliniclist = user.clinics.toList();
or
var user.clinics.Add(clinicList);
To select you just need to select the UserClinics collection and then select the Clinic property from it:
var clinics = user.UserClinics.Select(x => x.Clinic);
To add:
user.UserClinics.Add(new UserClinic { ClinicID = 1, Clinic = new Clinic { ... } });
I want to map from
LDTTicketUploadDTO[] to IEnumerable<LDTTicket>
The mappings are created in this method and at the end I map the data.
public void UploadLDTTickets(LDTTicketUploadDTO[] ticketDTOs)
{
Mapper.CreateMap<LDTTicketUploadDTO, LDTTicket>();
Mapper.CreateMap<LDTTicketDTO, LDTTicket>();
Mapper.CreateMap<LDTCustomerDTO, LDTCustomer>();
Mapper.CreateMap<LDTDeviceDTO, LDTDevice>();
Mapper.CreateMap<LDTUnitDTO, LDTUnit>();
Mapper.CreateMap<LDTCommandDTO, LDTCommand>();
Mapper.CreateMap<LDTCommandParameterDTO, LDTCommandParameter>();
Mapper.CreateMap<LDTObjectDTO, LDTObject>();
Mapper.CreateMap<LDTControlFileDTO, LDTControlFile>();
Mapper.CreateMap<LDTDeviceDTO, LDTDevice>();
Mapper.CreateMap<LDTLanguageDTO, LDTLanguage>();
Mapper.CreateMap<LDTObjectBitDTO, LDTObjectBit>();
var tickets = Mapper.Map<IEnumerable<LDTTicketUploadDTO>, IEnumerable<LDTTicket>>(ticketDTOs);
// do something with tickets
}
This is how the DTO´s are structured:
public class LDTTicketUploadDTO
{
public LDTTicketDTO Ticket { get; set; }
public LDTDeviceDTO Device { get; set; }
public LDTCustomerDTO Customer { get; set; }
}
public enum TicketStatus
{
New,
InProgress,
Done
}
public class LDTTicketDTO
{
public bool UploadNeeded { get; set; }
public string TicketNumber { get; set; }
public TicketStatus Status { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedOn { get; set; }
public string AssignedTo { get; set; }
public IEnumerable<LDTUnitDTO> Units { get; set; }
}
public class LDTUnitDTO
{
public int Id { get; set; }
public string FunctionUnit { get; set; }
public int FunctionUnitAddress { get; set; }
public string Zone { get; set; }
public int ZoneUnitAddress { get; set; }
public string Object { get; set; }
public int ObjectAddress { get; set; }
public IEnumerable<LDTCommandDTO> Commands { get; set; }
}
and more...
What works is that these properties are correctly mapped to their counterpart entities:
public LDTDeviceDTO Device { get; set; }
public LDTCustomerDTO Customer { get; set; }
What works NOT is that this property is not mapped:
public LDTTicketDTO Ticket { get; set; }
This is how the Entities are structured:
public class LDTTicket
{
[Key, Column(Order = 0)]
[Required]
public string SerialNumber { get; set; }
[Key, Column(Order = 1)]
[Required]
public string TicketNumber { get; set; }
[Required]
public DateTime CreatedOn { get; set; }
[Required]
public string AssignedTo { get; set; }
public TicketStatus Status { get; set; }
public string CreatedBy { get; set; }
public bool UploadNeeded { get; set; }
public virtual LDTCustomer Customer { get; set; }
public virtual LDTDevice Device { get; set; }
public virtual ICollection<LDTUnit> Units { get; set; }
}
ONLY the Customer and Device property are mapped in the LDTTicket
What is wrong with my configuration?
It's expecting to populate a LDTTicket sub-property on the ticket, not the matching properties of the ticket itself. Create direct mappings onto the ticket from the Ticket subproperty of the source directly onto the matching properties of the destination. NOTE: You only need to define your mappings once, not per method execution. Mappings should be defined at app start up and thereafter used.
public void UploadLDTTickets(LDTTicketUploadDTO[] ticketDTOs)
{
Mapper.CreateMap<LDTTicketUploadDTO, LDTTicket>();
.ForMember(d => d.SerialNumber, m => m.MapFrom(s => s.Ticket.SerialNumber))
...
//Mapper.CreateMap<LDTTicketDTO, LDTTicket>(); You don't need this
Mapper.CreateMap<LDTCustomerDTO, LDTCustomer>();
Mapper.CreateMap<LDTDeviceDTO, LDTDevice>();
...
}