Enumerating value across a foreign key relationship in MVC? - c#

My database has a variety of different tables that reference each other through the use of standard fk relationships. I'm trying to figure out how to setup my controller and views to display the contents of these other tables/models from within the main view.
I apologize if it's a bit confusing, but here's an example. I have a table employees and I'm attempting to list all the emails for that employee in my detail view, the emails are stored in email_manager and there is a foreign key that references the employee_id column from the employees table. My employee model looks something like this:
[Table("employee.employees")]
public partial class employees1
{
public employees1()
{
employee_email_manager = new HashSet<email_manager>();
employee_employment_history = new HashSet<employment_history>();
employee_job_manager = new HashSet<job_manager>();
employee_phone_manager = new HashSet<phone_manager>();
this.salaries = new HashSet<salary>();
}
[Key]
public int employee_id { get; set; }
[Display(Name="Employee ID")]
public int? assigned_id { get; set; }
[Display(Name="Web User ID")]
public int? all_id { get; set; }
[Required]
[StringLength(50)]
[Display(Name="First Name")]
public string first_name { get; set; }
[StringLength(50)]
[Display(Name="Last Name")]
public string last_name { get; set; }
[Column(TypeName = "date")]
[Display(Name="Birthday")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime birth_day { get; set; }
[Required]
[StringLength(1)]
[Display(Name="Gender")]
public string gender { get; set; }
[Required]
[StringLength(128)]
[Display(Name="Social")]
public string social { get; set; }
[Required]
[StringLength(128)]
[Display(Name="Address")]
public string address_line_1 { get; set; }
[StringLength(50)]
[Display(Name="Suite/Apt#")]
public string address_line_2 { get; set; }
[Required]
[StringLength(40)]
[Display(Name="City")]
public string city { get; set; }
[Required]
[StringLength(20)]
[Display(Name="State")]
public string state { get; set; }
[Required]
[StringLength(11)]
[Display(Name="Zip")]
public string zip { get; set; }
[Column(TypeName = "date")]
[Display(Name="Hire Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime hire_date { get; set; }
[Column(TypeName = "date")]
[Display(Name="Separation Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime? termination_date { get; set; }
[StringLength(70)]
[Display(Name="Emergency Contact Name")]
public string emergency_contact_name { get; set; }
[StringLength(15)]
[Display(Name = "Emergency Contact Number")]
public string emergency_contact_phone { get; set; }
[Display(Name = "Notes")]
public string notes { get; set; }
public virtual all_employees all_employees { get; set; }
public virtual ICollection<email_manager> employee_email_manager { get; set; }
public virtual ICollection<employment_history> employee_employment_history { get; set; }
public virtual ICollection<job_manager> employee_job_manager { get; set; }
public virtual ICollection<phone_manager> employee_phone_manager { get; set; }
public virtual ICollection<salary> salaries { get; set; }
And my email_manager model is:
[Table("employee.email_manager")]
public partial class email_manager
{
[Key]
public int email_id { get; set; }
public int employee_id { get; set; }
[Required]
[StringLength(255)]
public string email { get; set; }
public int email_type { get; set; }
[Column(TypeName = "date")]
public DateTime date_added { get; set; }
public bool deleted { get; set; }
public virtual email_types email_types { get; set; }
public virtual employees1 employees1 { get; set; }
}
[Table("employee.email_types")]
public partial class email_types
{
public email_types()
{
email_manager = new HashSet<email_manager>();
}
[Key]
public int email_type_id { get; set; }
[Required]
[StringLength(50)]
public string email_type_name { get; set; }
public virtual ICollection<email_manager> email_manager { get; set; }
}

Solving this one was actually an all day task for me, but I managed to finally crack the answer. It turns out that even though my foreign keys were mapped inside the the ApplicationDbContext, they weren't being mapped properly. This was causing me some serious headache, and it turns out it was heavily related to another question I asked Here.
To remedy this, I had to explicitly define my foreign key columns within each of my respective models, using [ForeignKey("FOREIGN_KEY_COLUMN")] directly above my collection housing the model with the respective primary key. The corrected version is similar to:
[Table("employee.email_manager")]
public partial class email_manager
{
[Key]
public int email_id { get; set; }
public int employee_id { get; set; }
[Required]
[StringLength(255)]
public string email { get; set; }
//[ForeignKey("email_type")]
public int email_type { get; set; }
[Column(TypeName = "date")]
public DateTime date_added { get; set; }
public bool deleted { get; set; }
[ForeignKey("email_type")]
public virtual email_types email_types { get; set; }
public virtual employees1 employees1 { get; set; }
}
[Table("employee.email_types")]
public partial class email_types
{
public email_types()
{
email_manager = new HashSet<email_manager>();
}
[Key]
public int email_type_id { get; set; }
[Required]
[StringLength(50)]
public string email_type_name { get; set; }
public virtual ICollection<email_manager> email_manager { get; set; }
}
Within the view, to reference the appropriate emails, you can then do something similar to:
<dt>
#Html.DisplayNameFor(model => model.employee_email_manager)
</dt>
<dd>
#foreach (var item in Model.employee_email_manager)
{
<text>#Html.DisplayFor(modelItem => item.email_types.email_type_name): </text>
#Html.DisplayFor(modelItem => item.email)
<br />
}
</dd>
This change didn't require changing my employees1 model in any way, only the referenced models such as email_manager, phone_manager, etc.

Related

How to avoid cycles when introducing FOREIGN KEY constraint in a table?

I'm using code-first approach with EF Core 6 to construct the database and its tables. The problem is that when I update the database, I get an error regarding cycles or multiple cascade paths. I searched the same questions but none of them could solve my problem.
My entity classes are:
public class User
{
[Key]
public int UserId { get; set; }
[Required]
[DataType(DataType.Text)]
public string Username { get; set; }
[Required]
[DataType(DataType.Text)]
public string Password { get; set; }
[Required]
[DataType(DataType.Text)]
public string UserRole { get; set; }
[Required]
public bool IsActive { get; set; }
[Required]
public bool CanAccessNotifications { get; set; }
[Required]
public bool CanAccessMessages { get; set; }
[Required]
public bool CanAccessFiles { get; set; }
[Required]
public bool CanAccessPmDatabase { get; set; }
[Required]
public bool CanMakeChangesToPmDatabase { get; set; }
[Required]
public bool IsLocked { get; set; }
[Required]
public bool CanLocked { get; set; }
//Navigation properties
public virtual CostCenter CostCenter { get; set; }
public virtual List<PmSchedule> PmSchedules { get; set; }
}
public class ServiceType
{
[Key]
public int ServiceTypeId { get; set; }
[Required]
[DataType(DataType.Text)]
public string Title { get; set; }
[Required]
public bool IsActive { get; set; } = true;
//Navigation properties
public virtual List<PmDataSet> PmDataSets { get; set; }
public virtual List<PmSchedule> PmSchedules { get; set; }
}
public class PmSchedule
{
[Key]
public long PmScheduleId { get; set; }
[Required]
public long PmNumber { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime ScheduledStartDate { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime ScheduledEndDate { get; set; }
[Required]
[DataType(DataType.Text)]
public string MainFileName { get; set; }
[DataType(DataType.Text)]
public string? UploadedFileName { get; set; }
[Required]
public int MainUploader { get; set; }
public int? Uploader { get; set; }
[DataType(DataType.DateTime)]
public DateTime? CompletionDate { get; set; }
[Required]
[DataType(DataType.Text)]
public string? Status { get; set; }
//Navigation properties
public virtual CostCenter CostCenter { get; set; }
public virtual ServiceType ServiceType { get; set; }
}
public class PmDataSet
{
[Key]
public long PmDataSetId { get; set; }
[Required]
public long PmNumber { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime ScheduledStartDate { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime ScheduledEndDate { get; set; }
[Required]
[DataType(DataType.Text)]
public string WorkOrderNumber { get; set; }
[Required]
[DataType(DataType.Text)]
public string Priority { get; set; }
[Required]
[DataType(DataType.Text)]
public string Activity { get; set; }
[DataType(DataType.DateTime)]
public DateTime? StartTime { get; set; }
[DataType(DataType.DateTime)]
public DateTime? EndTime { get; set; }
[Required]
public int ActualDuration { get; set; }
[Required]
public int StandardDuration { get; set; }
[Required]
[DataType(DataType.Text)]
public string Executor { get; set; }
[DataType(DataType.Text)]
public string? Expaination { get; set; }
//Navigation properties
public virtual ServiceType ServiceType { get; set; }
public virtual CostCenter CostCenter { get; set; }
public virtual Equipment Equipment { get; set; }
}
public class Equipment
{
[Key]
public long EquipmentId { get; set; }
[Required]
[DataType(DataType.Text)]
public string EquipmentCode { get; set; }
[Required]
[DataType(DataType.Text)]
public string EquipmentTitle { get; set; }
//Navigation properties
public virtual CostCenter CostCenter { get; set; }
public virtual List<PmDataSet> PmDataSets { get; set; }
}
public class CostCenter
{
[Key]
public int CostCenterId { get; set; }
[Required]
[DataType(DataType.Text)]
public string Title { get; set; }
[Required]
public bool IsActive { get; set; } = true;
//Navigation properties
public virtual List<PmDataSet>? PmDataSets { get; set; }
public virtual List<PmSchedule>? PmSchedules { get; set; }
public virtual List<Equipment>? Equipments { get; set; }
public virtual List<User>? Users { get; set; }
}
My context class:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
I get the following error when update the database:
Introducing FOREIGN KEY constraint
'FK_PmDataSets_Equipments_EquipmentId' on table 'PmDataSets' 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 or index. See previous errors.
you need to add some extra config like this :
protected override void OnModelCreating(ModelBuilder mb)
{
var pmConfig = mb.Entity<PmDataSet>();
pmConfig.HasOne(n => n.Equipment).WithMany(n => n.PmDataSets).OnDelete(DeleteBehavior.NoAction);
}
the problem is your Equipment model or/and some others already has at least one cascade delete relation,
so if you add another cascade delete relation there will be a chain deletion if one of these entities got deleted.
so you need to tell ef that some/none of these relations are not cascading while deleting.
(A) <-- cascade --> (B)
(B) <-- cascade --> (C)
so there will be a logical cascade between A and C, that is not arbitrary.
with a little playing with that config you will solve your problem ;)
-------- update
to answer the commented question u can go by ef convictions:
public User MainUploader { get; set; }
[Required]
public int MainUploaderId { get; set; }
public User Uploader { get; set; }
public int? UploaderId { get; set; }
you also can use [ForeignKey("fkName")] annotation if you want to use another way of naming your properties,
or use fluent API ForeignKey(a=> a.something) method or its overlods.

Creating a one-to-many relationship EF Core - update-database fail

I wants to create a one-to-many relationship.
The plan is as follows: One Customer can have multiple Bikes. Below I present the data models.
Bike Model
public class Bike
{
[Key]
public Guid Id { get; set; }
[Required]
[MaxLength(50)]
public string Brand { get; set; }
[Required]
public int Size { get; set; }
[Required]
[MaxLength(200)]
public string Description { get; set; }
[Required]
[MaxLength(50)]
public string Model { get; set; }
[ForeignKey("CustomerID")]
public Guid CustomerID { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime AddedBike { get; set; }
}
Customer Model
public class Customer
{
[Key]
public Guid Id { get; set; }
[StringLength(50, MinimumLength = 3)]
[Required]
public string Name { get; set; }
[Required]
[StringLength(50, MinimumLength = 3)]
public string Surname { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required]
[DisplayName("Telephone")]
public string TelephoneNumber { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime DateTimeAdd { get; set; }
[DataType(DataType.DateTime)]
[DisplayName("Last Update Customer")]
public DateTime EditDate { get; set; }
public virtual ICollection<Bike> Bike { get; set; }
}
DBContext
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Bike> Bikes { get; set; }
public DbSet<Customer> Customers { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
Migration is going well. However, update-database causes this error:
PM> update-database
Build started...
Build succeeded.
System.InvalidOperationException: There is no entity type mapped to the table 'Bikes' used in a data operation. Either add the corresponding entity type to the model or specify the column types in the data operation.
...
There is no entity type mapped to the table 'Bikes' used in a data operation. Either add the corresponding entity type to the model or specify the column types in the data operation.
Where is the mistake I am making?
Finally, it worked. I finally deleted all migrations, created a new migration and updated the database.
I then added the relationships in the models, did the migration again and updated the database and it worked.
My models look like this
Bike.cs
public class Bike
{
[Key]
public Guid Id { get; set; }
[Required]
[MaxLength(50)]
public string Brand { get; set; }
[Required]
public int Size { get; set; }
[Required]
[MaxLength(200)]
public string Description { get; set; }
[Required]
[MaxLength(50)]
public string Model { get; set; }
public Guid CustomerID { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime AddedBike { get; set; }
[ForeignKey("CustomerID")]
public Customer Customer { get; set; }
}
Customer.cs
public class Customer
{
[Key]
public Guid Id { get; set; }
[StringLength(50, MinimumLength = 3)]
[Required]
public string Name { get; set; }
[Required]
[StringLength(50, MinimumLength = 3)]
public string Surname { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required]
[DisplayName("Telephone")]
public string TelephoneNumber { get; set; }
[Required]
[DataType(DataType.DateTime)]
public DateTime DateTimeAdd { get; set; }
[DataType(DataType.DateTime)]
[DisplayName("Last Update Customer")]
public DateTime Edit { get; set; }
public virtual ICollection<Bike> Bike { 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.

Unable to determine the principal end of an association - EF Code First

I have a class "User" on which I have added the following fields:
[Required]
public int CreatedBy_Id { get; set; }
[ForeignKey("CreatedBy_Id")]
public User CreatedBy { get; set; }
public int? UpdatedBy_Id { get; set; }
[ForeignKey("UpdatedBy_Id")]
public User UpdatedBy { get; set; }
When I try to push these fields into database with this command :
Add-Migration -Configuration DataModel.DevMigrations.Configuration TestAlterUser
I get this error:
Unable to determine the principal end of an association between the
types 'DataModel.Entities.User' and
'DataModel.Entities.User'.
The principal end of this association must be explicitly configured
using either the relationship fluent API or data annotations.
If I remove the UpdatedBy property (or CreatedBy), it works perfectly.
The complete user's class :
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Login { get; set; }
[Required]
[MaxLength(50)]
public string LastName { get; set; }
[MaxLength(50)]
public string FirstName { get; set; }
[Required]
public int CreatedBy_Id { get; set; }
[ForeignKey("CreatedBy_Id")]
public User CreatedBy { get; set; }
public int? UpdatedBy_Id { get; set; }
[ForeignKey("UpdatedBy_Id")]
public User UpdatedBy { get; set; }
}
Try to use InverseProperty attribute:
public class User
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Login { get; set; }
[Required]
[MaxLength(50)]
public string LastName { get; set; }
[MaxLength(50)]
public string FirstName { get; set; }
[Required]
public int CreatedBy_Id { get; set; }
[ForeignKey("CreatedBy_Id")]
public virtual User CreatedBy { get; set; }
public int? UpdatedBy_Id { get; set; }
[ForeignKey("UpdatedBy_Id")]
public virtual User UpdatedBy { get; set; }
//add these properties
[InverseProperty("CreatedBy")]
public virtual ICollection<User> WereCreated {get; set;}
[InverseProperty("UpdatedBy")]
public virtual ICollection<User> WereUpdated {get; set;}
}

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