EntityFramework - Updating many-to-many relationships - c#

I'm trying to update a many-to-many relationship. I have two models, diary and tags. The model diary contains a list of tags and tags contains a list of diaries. But whenever I try to add another tag to an existing list or change an existing one I get thrown an exception:
The entity type List`1 is not part of the model for the current context.
Does my way of updating even work on collections? Or is there another approach I should be looking at?
Diary Model
public class Diary
{
[Key]
public int IdDiary { get; set; }
[Required(ErrorMessage = "This field is required")]
public string NameDiary { get; set; }
[Required(ErrorMessage = "This field is required")]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime CreationDate { get; set; }
public bool Locked { get; set; }
public ICollection<Entry> Entries { get; set; }
[DataType(DataType.MultilineText)]
[Required(ErrorMessage = "This field is required")]
public string Summary { get; set; }
public string ImageUrl { get; set; }
public ICollection<Tag> Tags { get; set; }
}
Entry Model
public class Entry
{
[Key]
public int IdEntry { get; set; }
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime CreationDate { get; set; }
[DataType(DataType.MultilineText)]
public string EntryText { get; set; }
[ForeignKey("Diary_IdDiary")]
public Diary Diary { get; set; }
public int Diary_IdDiary { get; set; }
public string EntryName { get; set; }
}
Tag Model
public class Tag
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Diary> Diaries { get; set; }
}
Update Method
public void UpdateDiary(Diary updatedDiary)
{
var searchResult = SearchDiary(updatedDiary);
if (searchResult != null)
{
updatedDiary.IdDiary = searchResult.IdDiary;
_context.Entry(searchResult).CurrentValues.SetValues(updatedDiary);
_context.Entry(searchResult.Tags).CurrentValues.SetValues(updatedDiary.Tags);
_context.SaveChanges();
}
}
SearchDiary Method
public Diary SearchDiary(Diary searchDiary)
{
var queryResult =
_context.Diaries.Include(d => d.Entries).Include(d => d.Tags)
.Where(d => (d.NameDiary == searchDiary.NameDiary && d.CreationDate == searchDiary.CreationDate) || d.IdDiary == searchDiary.IdDiary);
return queryResult.FirstOrDefault();
}
Thank you for reading

Here is one way:
DiaryModel
public class Diary
{
[Key]
public int IdDiary { get; set; }
[Required(ErrorMessage = "This field is required")]
public string NameDiary { get; set; }
[Required(ErrorMessage = "This field is required")]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime CreationDate { get; set; }
public bool Locked { get; set; }
public virtual ICollection<Entry> Entries { get; set; }
[DataType(DataType.MultilineText)]
[Required(ErrorMessage = "This field is required")]
public string Summary { get; set; }
public string ImageUrl { get; set; }
public ICollection<Tag> Tags { get; set; }
}
EntryModel
public class Entry
{
[Key]
public int IdEntry { get; set; }
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime CreationDate { get; set; }
[DataType(DataType.MultilineText)]
public string EntryText { get; set; }
[ForeignKey("Diary_IdDiary")]
public virtual Diary Diary { get; set; }
public int Diary_IdDiary { get; set; }
public string EntryName { get; set; }
}
For Search
public Diary SearchDiary(int DiaryId)
{
return _context.Diaries
.Include(p => p.Entries)
.Include(p => p.Tags)
.FirstOrDefault(p => p.IdDiary == DiaryId);
}
For Update
public void UpdateDiary(Diary Diary)
{
_context.Update(Diary);
_context.SaveChanges();
}
You should pass the modified Diary object to the update functions.

Related

C# Unable to track entity of type because primary key property is null

I have a User table and a UserProfileImage table that is linked together.
This is the User table structure
public class User : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string MobileNumber { get; set; }
public override bool LockoutEnabled { get; set; } = true;
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime? DateOfBirth { get; set; }
public DateTime CreatedOn { get; set; } = DateTime.Now;
public DateTime UpdatedOn { get; set; } = DateTime.Now;
public UserProfileImage UserProfileImage { get; } = new UserProfileImage();
}
This is the UserProfileImage structure
public class UserProfileImage
{
[Key]
public string Id { get; set; }
public string Name { get; set; }
public string UserId { get; set; }
public virtual User User { get; set; }
public string Link { get; set; }
public string Location { get; set; }
public DateTime CreatedOn { get; set; } = DateTime.Now;
}
I have linked them in the dbcontext like this:
modelBuilder.Entity<User>()
.HasOne(a => a.UserProfileImage)
.WithOne(b => b.User)
.HasForeignKey<UserProfileImage>(b => b.UserId)
.OnDelete(DeleteBehavior.Cascade);
However, when I try to add a new user, I get this exception:
Unable to track an entity of type 'UserProfileImage' because primary
key property 'Id' is null.
May I know what does this mean? I am not adding a new entry to UserProfileImage at the moment.
try to fix your classes:
ublic class User : IdentityUser
{
......
public string UserProfileImageId { get; set;}
[ForeignKey(nameof(UserProfileImageId ))]
[InverseProperty("User")]
public virtual UserProfileImage UserProfileImage { get; set;}
}
public class UserProfileImage
{
[Key]
public string Id { get; set; }
......
[InverseProperty("UserProfileImage")]
public virtual User User { get; set;}
}

LINQ with Include and Where not showing desired results

I have a linq statement that combines Residents and Requests table like so
var resident = await _context.Resident
.Include(s => s.Requests)
.FirstOrDefaultAsync(m => m.ID == id);
The problem is that all requests show up even those that are not related to the Resident.
I have tried adding a Where statement but still getting all requests.
var resident = await _context.Resident
.Include(s => s.Requests)
.Where(s =>s.UserID == "f7c6ceef-663f-48af-9a84-b0a3d2a97601")
.FirstOrDefaultAsync(m => m.ID == id);
For reference this is the Resident Model Class
public class Resident
{
public Resident()
{
this.CreatedAt = DateTime.Now;
}
public int ID { get; set; }
public string UserID { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Street Address")]
public string StreetAddress { get; set; }
[Required]
[Display(Name = "Postal Code")]
public string PostalCode { get; set; }
[Required]
[Display(Name = "Phone Number")]
public string PhoneNumber { get; set; }
[Required]
[Display(Name = "Number of Cameras")]
public int CameraQty { get; set; }
public DateTime CreatedAt { get; set; }
public string Latlng { get; set; }
public ICollection<Camera> Cameras { get; set; }
public ICollection<Request> Requests { get; set; }
}
This is the Request Model Class
using System.ComponentModel.DataAnnotations;
namespace MVC_NeighbourhoodCamera.Models
{
public class Request
{
public Request()
{
this.CreatedAt = DateTime.Now;
}
public int ID { get; set; }
public int ResidentID { get; set; }
public string UserID { get; set; }
public DateTime StartDateTime { get; set; }
public DateTime EndDateTime { get; set; }
[DataType(DataType.MultilineText)]
public string Details { get; set; }
public Boolean Completed { get; set; }
public Boolean Active { get; set; }
public DateTime CreatedAt { get; set; }
public Resident Resident { get; set; }
}
}
You do not have foreign key properly set in Resident->Requests one-to-many relationship.
Fix your Request class as following by adding ForeignKeyAttribute (+make migration and run it in to the db):
public class Request
{
//Other properties
..
public int ResidentID { get; set;}
ForeignKey["ResidentID"]
public Resident Resident { get; set;}
}
More info can be found i.e here.

.NET EF MVC not loading property

I've been trying to get something done in .NET EF for some time now.
My model looks like this:
public class Route
{
[Key]
public int RouteId { get; set; }
[Display(Name = "Rope")]
public int ropeId { get; set; }
public virtual Rope rope { get; set; }
[Display(Name = "Routesetter")]
public int routeSetterId { get; set; }
public virtual RouteSetter routeSetter { get; set; }
[Display(Name = "Secundary routesetter")]
public int? secundaryRouteSetterId { get; set; }
public virtual RouteSetter secundaryRouteSetter { get; set; }
[Display(Name = "Grade")]
public int gradeId { get; set; }
public virtual ClimbingGrade grade { get; set; }
[Display (Name = "Routename")]
public string name { get; set; }
[Display(Name = "Color")]
public int colorId { get; set; }
public virtual GripColor color { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd-MM-yyyy}", ApplyFormatInEditMode = true)]
[Display (Name ="Date set")]
public DateTime dateSet { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd-MM-yyyy}", ApplyFormatInEditMode = true)]
[Display (Name ="Date removed")]
public DateTime? dateRemoved { get; set; }
[Display (Name ="Marked for removal")]
public bool markedForRemoval { get; set; }
}
The controller:
public ActionResult Index()
{
var routes = db.Routes.Include(r => r.color).Include(r => r.grade).Include(r => r.rope).Include(r => r.routeSetter).Include(r => r.secundaryRouteSetter);
ViewBag.Page = "Routes";
return View(routes.ToList());
}
Now the color property is always null. while all other properties are loaded.
I can't seem to figure this one out. Any help would be welcome
EDIT
The GripColor clas:
public class GripColor
{
[Key]
public int GripColorId { get; set; }
[Display(Name = "Primary color")]
public string primaryColor { get; set; }
[Display(Name = "Secundary color")]
public string secundaryColor { get; set; }
public string displayName { get { return primaryColor + ' ' + secundaryColor; } }
public virtual List<Route> Routes { get; set; }
}
I assume the gripColors are OK in the database;
ViewBag.ColorID = new SelectList(db.Colors, "GripColorId", "displayName");
The above is used to populate a dropdown (wich works) And the colorId is stored correctly in the database
There is no foreign key relating the two tables, then how are you expecting to make inner joins between them?You should setup the relationship between these two tables and define the foreign key.You can use data annotations for this purpose or i highly recommend using fluent api.Check out this documentation for more info:
http://docs.efproject.net/en/latest/modeling/index.html

How to gain access to table data through foreign key reference?

I have a statement in one of my entities which uses a foreign key to return an IEnumerable<CustomField>.
I have used LINQ in my repository to test the below method to see if it works and it does. But when I use the foreign key reference in the entity it returns null. Am I missing something here? How can I use a foreign key to gain access to the data in another entity.
Invoice entity:
[Table("vwinvoice")]
public class Invoice
{
[Key]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int Sys_InvoiceID { get; set; }
[DisplayName("Inc.In Turnover")]
public bool Turnover { get; set; }
public int FK_StatusID { get; set; }
[DisplayName("Invoice No.")]
public string InvoiceNumber { get; set; }
[DisplayName("Invoice Date")]
public DateTime InvoiceDate { get; set; }
[DisplayName("Document Type")]
public string DocType { get; set; }
[DisplayName("Supplier Invoice No.")]
[Column("SupplierInvoiceNumber")]
public string SuppInvNumber { get; set; }
public int FK_SupplierID { get; set; }
[DisplayName("Account Number")]
public string AccountNumber { get; set; }
[DisplayName("Order Number")]
public string OrderNumber { get; set; }
[DisplayName("Order Date")]
public DateTime? OrderDate { get; set; }
[DisplayName("Currency Code_Doc")]
public string CurrencyCode_Doc { get; set; }
[DisplayName("Net Amount_Doc")]
public decimal? NetAmount_Doc { get; set; }
[DisplayName("VAT Amount_Doc")]
public decimal? VATAmount_Doc { get; set; }
[DisplayName("Gross Amount_Doc")]
[Required]
public decimal? GrossAmount_Doc { get; set; }
[DisplayName("Currency Code_Home")]
public string CurrencyCode_Home { get; set; }
[DisplayName("Net Amount_Home")]
public decimal? NetAmount_Home { get; set; }
[DisplayName("VAT Amount_Home")]
public decimal? VATAmount_Home { get; set; }
[DisplayName("Gross Amount_Home")]
public decimal? GrossAmount_Home { get; set; }
[DisplayName("Payment Reference")]
public string PaymentReference { get; set; }
[DisplayName("Supplier")]
public string AccountName { get; set; }
[DisplayName("Status")]
public string StatusName { get; set; }
[DisplayName("Auditor Comments")]
public string AuditorComments { get; set; }
[DisplayName("Reviewer Comments")]
public string ReviewerComments { get; set; }
[DisplayName("Data Source")]
[Required]
public string DataOrigin { get; set; }
public int DetailLineCount { get; set; }
public IEnumerable<CustomField> ClientData {
get {
//Use the CustomFields foreign key to gain access to the data returns null.
return GetCustomFieldData(this.CustomFields.Select(r => r));
}
}
private IEnumerable<CustomField> GetCustomFieldData(IEnumerable<Entities.CustomFields> enumerable) {
return (from f in enumerable
select new CustomField {
Name = f.FK_CustomHeader,
Value = f.Value
});
}
//Custom Field Additions
public virtual ICollection<CustomFields> CustomFields { get; set; }
}
CustomFields entity:
[Table("tblCustomFields")]
public class CustomFields
{
[Key]
public int ID { get; set; }
public int? FK_SysInvoiceID { get; set; }
[StringLength(255)]
public string FK_CustomHeader { get; set; }
[StringLength(255)]
public string Value { get; set; }
public virtual Invoice Invoices { get; set; }
public virtual CustomFieldHeaders CustomFieldHeaders { get; set; }
}
I also cannot place a breakpoint in the get statement to see what happens, why is this? It just skips over the breakpoint whenever I try to return a list of Invoices, which can be seen here:
public IQueryable<Invoice> Invoices
{
get
{
var x = _ctx.Invoices.ToList();
return _ctx.Invoices;
}
}
You are using the virtual keyword when declaring your CustomFields property. As such it will be lazy loaded. If you want the property to be populated once returned from the repository you will need to explicitly Include the table in your method:
var x = _ctx.Invoices.Include(i => i.CustomFields).ToList();
return _ctx.Invoices;
Or you can remove the virtual keyword and the property will always be populated, with the consequent performance hit of the database join and the extra data being returned whenever you access Invoices.

Complex relationship mappings in entity framework

I am building a reservation system. I have users in roles('admin', 'client', 'employee', 'student').
Each reservation must be associated with a user of role client, it might be assigned to user of role employee and might also be assigned to user of role student.
So in my reservation class I have properties of type User and I have marked them with [ForeignKey("AnytypeId")] attribute to hint EF for relations.
I have seen code like this at http://blog.stevensanderson.com/2011/01/28/mvcscaffolding-one-to-many-relationships/
public class Reservation
{
public int ReservationID
{
get;
set;
}
[Required(ErrorMessage="Please provide a valid date")]
public DateTime ReservationDate
{
get;
set;
}
public DateTime ReservationEnd { get; set; }
public DateTime EntryDate
{
get;
set;
}
public DateTime UpdatedOn
{
get;
set;
}
public decimal Ammount
{
get;
set;
}
public decimal? Discount { get; set; }
[DataType(DataType.MultilineText)]
public string ServiceDetails { get; set; }
[DataType(DataType.MultilineText)]
public string Remarks { get; set; }
public string VoucherNumber { get; set; }
public int ServiceID
{
get;
set;
}
public Service Service
{
get;
set;
}
public string EmployeeId { get; set; }
[ForeignKey("EmployeeId")]
public User Employee { get; set; }
public string ClientId { get; set; }
[ForeignKey("ClientId")]
public User Client { get; set; }
public string StudentId { get; set; }
[ForeignKey("StudentId")]
public User Student { get; set; }
}
public class User
{
//[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
//public Guid UserId { get; set; }
[Key]
[Required(ErrorMessage = "User Name is required")]
[Display(Name = "User Name")]
[MaxLength(100)]
public string UserName { get; set; }
[Required]
[MaxLength(64)]
public byte[] PasswordHash { get; set; }
[Required]
[MaxLength(128)]
public byte[] PasswordSalt { get; set; }
[Required(ErrorMessage = "Email is required")]
[DataType(DataType.EmailAddress)]
[MaxLength(200)]
public string Email { get; set; }
[MaxLength(200)]
public string Comment { get; set; }
[Display(Name = "Approved?")]
public bool IsApproved { get; set; }
[Display(Name = "Crate Date")]
public DateTime DateCreated { get; set; }
[Display(Name = "Last Login Date")]
public DateTime? DateLastLogin { get; set; }
[Display(Name = "Last Activity Date")]
public DateTime? DateLastActivity { get; set; }
[Display(Name = "Last Password Change Date")]
public DateTime DateLastPasswordChange { get; set; }
public string address { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string Phone { get; set; }
public bool? IsActive { get; set; }
public int? ClientTypeID { get; set; }
public virtual ClientType ClientType { get; set; }
public virtual ICollection<Role> Roles { get; set; }
public DateTime? PackageValidity { get; set; }
public virtual ICollection<Reservation> Reservations { get; set; }
}
public class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
this.HasMany(u => u.Roles)
.WithMany(r => r.Users)
.Map(m =>
{
m.ToTable("RoleMemberships");
m.MapLeftKey("Username");
m.MapRightKey("RoleName");
});
}
}
Now as I run my mvc3 EF code first app database created for me on the fly with following ERD and edmx model.
Now few problems that I am having:
1. When I am listing all the users of role clients their reservation property is showing always 0 even if their are reservations available in database.
2. If I am trying to delete a user of role client who have reservation in database I get following error.
The DELETE statement conflicted with the REFERENCE constraint "Reservation_Client". The conflict occurred in database "CRSDB", table "dbo.Reservations", column 'ClientId'.
The statement has been terminated.
I checked the realtions in ERD and edmx model their is no cascade delete applied to them. How can I instruct EF to delete all the reservations when deleting user of role client but not for users of role employee or student.
This code does the trick
public class Reservation
{
public int ReservationID
{
get;
set;
}
[Required(ErrorMessage="Please provide a valid date")]
public DateTime ReservationDate
{
get;
set;
}
public DateTime ReservationEnd { get; set; }
public DateTime EntryDate
{
get;
set;
}
public DateTime UpdatedOn
{
get;
set;
}
public decimal Ammount
{
get;
set;
}
public decimal? Discount { get; set; }
[DataType(DataType.MultilineText)]
public string ServiceDetails { get; set; }
[DataType(DataType.MultilineText)]
public string Remarks { get; set; }
public String PaymentMethod { get; set; }
public string VoucherNumber { get; set; }
public int ServiceID
{
get;
set;
}
public virtual Service Service
{
get;
set;
}
public string EmployeeID { get; set; }
[ForeignKey("EmployeeID")]
public virtual User Employee { get; set; }
public string ClientID { get; set; }
[ForeignKey("ClientID")]
public virtual User Client { get; set; }
public string StudentID { get; set; }
[ForeignKey("StudentID")]
public virtual User Student { get; set; }
}
public class ReservationMap : EntityTypeConfiguration<Reservation>
{
public ReservationMap()
{
this.HasOptional(r => r.Client).WithMany().WillCascadeOnDelete(true);
this.HasOptional(r => r.Employee).WithMany().WillCascadeOnDelete(false);
this.HasOptional(r=>r.Student).WithMany().WillCascadeOnDelete(false);
}
}

Categories

Resources