Avoid cycles or multiple cascade paths - c#

I am getting the following exception:
I have gone through many posts here, here and here. But no post suggests proper solution to the problem. I want to know how can this situation be tackled practically.
My Models and Contexts are as follows:
public class Context : DbContext
{
public Context() : base("DefaultConnection")
{
}
public DbSet<Student> Students { get; set; }
public DbSet<Course> Courses { get; set; }
public DbSet<Staff> Staffs { get; set; }
}
public class Student
{
public int StudentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Required]
public virtual Course Course { get; set; }
[Required]
public virtual Staff Staff { get; set; }
}
public class Staff
{
public int StaffId { get; set; }
public string Name { get; set; }
public string Contact { get; set; }
}
public class Course
{
public int CourseId { get; set; }
public string CourseName { get; set; }
[Required]
public virtual Staff Staff { get; set; }
}
I am getting this exception on the line :
context.Students.Add(student);
of the following code:
public void AddStudent()
{
Student student = new Student();
student.FirstName = "Bruce";
student.LastName = "Wayne";
student.Course = new Course();
student.Course.CourseName = "CSE";
student.Course.Staff = new Staff();
student.Course.Staff.Name = "Albert";
student.Course.Staff.Contact = "1234567890";
context.Students.Add(student);
context.Courses.Add(student.Course);
context.SaveChanges();
Console.WriteLine("Student , Course, Staff Added");
}

I had asked this question some time back. This should help you out.
EF Code First giving problems in foreign keys
Reference reading here
http://weblogs.asp.net/manavi/archive/2011/05/01/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations.aspx
The main part to look for in the article is "What's a Multiple Cascade Path Anyway?"
To solve the problem practically you need to identify which path do you want the cascade delete to be turned on. For e.g. If the a staff gets deleted does the course also get deleted or does it remain ?

Disabling cascading deletes for that entity should solve your issue. If you want a cascading delete for this set of entities, do it manually. It can't be done automatically because when there's a cycle there's no way to determine when to stop.

Related

EF Optional Relationships / Nullable / Navigation Table

I solved this, answer below.
I am new to EF and having a lot of difficulty trying to get an optional relationship. I am looking to have a relationship where I have ApiLogItem Model with an UserId property which can be null / anonymous user or a logged in user to track all Api calls. The goal is to have Existing Users who do some create a new object to be linked to that object. I do not want to create new Users every time a new ApiLogItem is created.
I have tried a dozen variations with virtual / foreign key attributes and I am stumped. It works great for null / anonymous user but once I attach an actual user to the ApiLogItem it will not insert. I get this error:
{"Violation of PRIMARY KEY constraint 'PK_AspNetUsers'. Cannot insert
duplicate key in object 'dbo.AspNetUsers'. The duplicate key value is
(09c0d2e2-b003-4be8-a62a-08d7268af58e).\r\nThe statement has been
terminated."}
I have tried following this tutorial but alas no luck.
https://www.learnentityframeworkcore.com/conventions/one-to-many-relationship#targetText=EF%20Core%20will%20create%20a,public%20class%20Author
public class ApiLogItem
{
[Key]
public long Id { get; set; }
[Required]
public int StatusCode { get; set; }
[Required]
public string Method { get; set; }
[MaxLength(45)]
public string IPAddress { get; set; }
public Guid? UserId { get; set; }
public ApplicationUser User { get; set; }
}
public class ApplicationUser : IdentityUser<Guid>
{
[MaxLength(64)]
public string FirstName { get; set; }
[MaxLength(64)]
public string LastName { get; set; }
public List<ApiLogItem> ApiLogItems { get; set; }
}
Error happens when I want to create a new ApiLogItem:
using (ApplicationDbContext _dbContext = new ApplicationDbContext(_optionsBuilder.Options))
{
_dbContext.ApiLogs.Add(apiLogItem);
await _dbContext.SaveChangesAsync();
}
I have reviewed several other stackoverflow issues and none seem to fix. You can find the repository here:
https://github.com/enkodellc/blazorboilerplate
You are calling applicationDbContextSeed.SeedDb(); in your Startup class each time you run, and in your SeedDb method, you are adding a user with a static id 09C0D2E2-B003-4BE8-A62A-08D7268AF58E.
The first time you run, it will create that user; the second time, it will fail because that user (with that id) already exists.
I figured it out. It needs a virtual in the parent and just the id in the child. I need to learn more about EF as it is not intuitive to me. Will post a better answer later today after testing.
public class ApplicationUser : IdentityUser<Guid>
{
[MaxLength(64)]
public string FirstName { get; set; }
[MaxLength(64)]
public string LastName { get; set; }
public ICollection<ApiLogItem> ApiLogItems { get; set; }
}
public class ApiLogItem
{
[Key]
public long Id { get; set; }
[Required]
public int StatusCode { get; set; }
[Required]
public string Method { get; set; }
[MaxLength(45)]
public string IPAddress { get; set; }
public Guid? UserId { get; set; }
}

Entity Framework Database Model

Dear Entity experts and others,
I have the following entities,
Base class Individual:
public abstract class Individual
{
[Key]
public int IndividualID { get; set; }
... other properties
}
Yogi:
[Table("Yogis")]
public class Yogi : Individual
{
public string Firstname { get; set; }
public string Lastname { get; set; }
public DateTime Birthdate { get; set; }
}
Customer:
[Table("Customers")]
public class Customer : Individual
{
public string Name { get; set; }
[ForeignKey("VATID")] // tried with and without
public virtual VAT VAT { get; set; }
}
VAT:
public class VAT
{
[Key]
public int VATID { get; set; }
[Required]
public virtual Customer VATHolder { get; set; }
... other properties
}
DbContext:
public DbSet<Individual> Individuals { get; set; }
public DbSet<VAT> VATS { get; set; }
So the above creates a table VAT where there VATID is 2 to start with which should be 1 instead, I don't understand why this happens, this should be 1, isn't it? Also, there is no reference anywhere in any table that references Customer & VAT, how can I create a separate table VAT_Customer where you have a VATID & CustomerID ?
This is how i create and insert the entity Customer:
VAT vat = new VAT("123456789");
DataBaseHandler.InsertIndividual(new Customer("Customer name", vat,...));
Insert Function:
public static void InsertIndividual(Individual individual)
{
using (MyDbContext ctx = new MyDbContext())
{
ctx.Individuals.Add(individual);
ctx.SaveChanges();
}
}
I have tried several ways (EXAMPLES HERE) but I prefer the TPT way, having a separate view of any entity or relations between entities. What am I doing wrong and how would I achieve the desired result? Let me know if I can clarify anything. Thank you in advance for any help or suggestions!
Kind regards!
Your ForeignKey placement should look something like this if you wanna have 1:1 Relationship :
enter code here
[ForeignKey("VAT")]
public virtual int VATId { get; set; }
public virtual VAT VAT { get; set; }
Name in the ForeignKey is same as your class.
hope this can help

InverseProperty Entity Framework 7

I am facing a problem with EF7 inverse property. There are two entities that are connected like this.
public class Employee
{
public int Id { get; set; }
}
public class Review
{
public int Id { get; set; }
[Required]
public virtual Employee Employee { get; set; }
[Required]
public Employee Manager { get; set; }
}
I want to access a list of the reviews when I start to query my employees, so I tried to do this:
public class Employee
{
public Employee()
{
Reviews = new List<Review>();
}
public int Id { get; set; }
[InverseProperty("Employee")]
public virtual ICollection<Review> Reviews { get; set; }
}
With this, the query is not well made and return this error:
Invalid column name 'EmployeeId1'.
This is the part of the query where is the error:
SELECT [ua].[Id], [r].[EmployeeId], [r].[EmployeeId1], [r1].[EmployeeId], [r1].[EmployeeId1]
FROM [UserAssessment] AS [ua]
LEFT JOIN [Review] AS [r] ON [ua].[ReviewId] = [r].[Id]
LEFT JOIN [Review] AS [r1] ON [ua].[ReviewId] = [r1].[Id]
Anyone know what can I do?
UPDATE
This statement is generating the query:
return this.DbSet
.Include(ua => ua.Employee).ThenInclude(t => t.Role)
.Include(ua => ua.Review).ThenInclude(rt => rt.ReviewType)
.Include(ua => ua.Review).ThenInclude(rt => rt.Manager).ThenInclude(r => r.Role)
I have to access with those same includes because lazy loading is not available on EF7 yet.
You need the InverseProperty on both the Employee and Review
public class Review
{
public int Id { get; set; }
[Required]
[InverseProperty("Reviews")]
public Employee Employee { get; set; }
[Required]
public Employee Manager { get; set; }
}
public class Employee
{
public int Id { get; set; }
[InverseProperty("Employee")]
public ICollection<Review> Reviews { get; set; }
}
Should work. I have a similar setup where it creates the navigation without creating any new fields. If this doesn't work let me know and I'll spin up a test project.
Also note, that EF7 currently ignores virtual and this does not have meaning like it did in EF6.

updating data in many-to-many relationship in entity framework in code first existing database

I am working on app and struggling to really understand how to update data in tables where we have intermediate join table (i.e. table to break many-to-many relationship). for example, from following diagram. if I say I want to add record for new student with list of three courses i.e. math, English and computing. how I do that where I have
public virtual ICollection<StudentCourse> StudentCourses { get; set; }
another questions; another scenario if I have courses already and of-course I don't want duplication of math, English and computing course title, how I add new instance of student record there??
Student
public partial class Student
{
public Student()
{
this.StudentCourses = new HashSet<StudentCourse>();
}
public int StudentID { get; set; }
public string Name { get; set; }
public virtual ICollection<StudentCourse> StudentCourses { get; set; }
}
Course
public partial class Course
{
public Course()
{
this.StudentCourses = new HashSet<StudentCourse>();
}
public int CourseID { get; set; }
public string Title { get; set; }
public virtual ICollection<StudentCourse> StudentCourses { get; set; }
}
Intermediate model
public partial class StudentCourse
{
public int StudentCourseID { get; set; }
public int StudentID { get; set; }
public int CourseID { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
My third question is, do I have to use Virtual key word in above code.
First, if you do not have an additional field in StudentCourse (such semester of the registration), then you do not need to have StudentCourse class.
If you want to keep this mode, you can do this:
StudentCourse registration = new StudentCourse();
registration.StudentID = 4;
registration.CourseID = 6;
context.StudentCourses.Add(registration);
context.SaveChanges();
These resources may give you further explanation:
https://practiceaspnet.wordpress.com/2015/10/22/code-first-many-to-many-mm-relationships-using-conventions-and-data-annotations/
https://practiceaspnet.wordpress.com/2015/10/30/managing-data-in-many-to-many-relationships-with-entity-framework-code-first/
If you want to check if there is no duplicate, you can simple do these:
if(context.Courses.Where(c => c.Title == 'Math').FirstOrDefault() == null)
{
//add the course
}else {
//already existing
}

Entity Framework DbContext Delete child object

I am trying to delete the child objects in foreach loop but that doesn't seem to be working.. Here is what I have.
I have a meeting and meeting_category entities which has 1-to-many relationship..
foreach (meeting_category meetingCategory in currentMeeting.meeting_category)
{
dbContext.meeting_category.Remove(meetingCategory);
dbContext.Entry(meetingCategory).State = EntityState.Deleted;
dbContext.SaveChanges();
}
I am then returning to the same view with updated results.. This seems to be very inconsistent. Sometimes it deletes entities and sometimes it doesn't. Even when it deletes it, my meeting object still have meeting_category objects I deleted.
When it doesn't delete from DB, it updates the meeting object and removed meeting_category objects from the meeting..
Is there any kind of reset or refresh that I will have to do in order for meeting object to remove the meeting_category objects..
How can I make sure that it consistently deletes the meeting_category objects both from DB and from the meeting object and when I return to the view, I have an updated meeting object?
Here is my meeting entity
public partial class meeting
{
public meeting()
{
this.meeting_questions = new HashSet<meeting_questions>();
this.meeting_abstract = new HashSet<meeting_abstract>();
this.meeting_category = new HashSet<meeting_category>();
this.meeting_image = new HashSet<meeting_image>();
}
public int meeting_id { get; set; }
public int language_id { get; set; }
public string meeting_code { get; set; }
public string meeting_name { get; set; }
public string meeting_description { get; set; }
public System.DateTime meeting_start_date { get; set; }
public System.DateTime meeting_end_date { get; set; }
public System.DateTime abstract_cutoff_date { get; set; }
public string meeting_guidelines { get; set; }
public string created_by { get; set; }
public Nullable<System.DateTime> created_datetime { get; set; }
public Nullable<bool> meeting_published { get; set; }
public Nullable<bool> meeting_deleted { get; set; }
public Nullable<bool> meeting_fully_created { get; set; }
public virtual language language { get; set; }
public virtual ICollection<meeting_questions> meeting_questions { get; set; }
public virtual ICollection<meeting_abstract> meeting_abstract { get; set; }
public virtual ICollection<meeting_category> meeting_category { get; set; }
public virtual ICollection<meeting_image> meeting_image { get; set; }
}
And here is my meeting_category entity
public partial class meeting_category
{
public meeting_category()
{
this.abstract_category = new HashSet<abstract_category>();
}
public int meeting_category_id { get; set; }
public int meeting_id { get; set; }
public int category_type_id { get; set; }
public int category_id { get; set; }
public string category_name { get; set; }
public string category_name_en { get; set; }
public virtual ICollection<abstract_category> abstract_category { get; set; }
public virtual category category { get; set; }
public virtual category_type category_type { get; set; }
public virtual meeting meeting { get; set; }
}
I have a couple of questions about what it is that you are trying to achieve.
Firstly are you sure that the Meeting -> Category relationship is many-to-one? I would normally expect it to be many-to-many, i.e. each Meeting can have many Categories and each Category can be attached to many Meetings.
Secondly, are you actually trying to delete the Category or are you trying to remove a Category from a Meeting?
If you are simply trying to remove the relationship between a Meeting and a Category then you need to edit the navigation property, i.e. MyMeeting.Categories.Remove(MyCategory).
If you are trying to delete the actual Category then you will need to make sure that that Category is not currently linked to any Meeting otherwise you will get a Foreign Key violation if you try to remove the Category.
On top of all of this you may be encountering chaching or simply object lifetime management issues.
What type of application are you creating? ASP.NET? WinForms? etc.
How are you actually instantiating a concrete version of your DBContext? Are you using DI?
Cheers Mike
I managed to fix the issue by creating the DbContext per controller action and destroying it before loading the view.
The problem was that I was creating the DbContext in another class and was reusing the same context on all controller action. Since Entities are cached in dbcontext, I was always getting the old cached copy of entities and was working very inconsistently.

Categories