Specifying Relationships in Models in ASP.NET MVC4 - asp.net-mvc

I'm an ASP.NET MVC4 beginner and I'm trying to create a blog kind of. I have a problem with creating relationships in my models. Background of my issue is that, I have a model (Users.cs) with user information, a model (Posts.cs) containing posts information, and a third model (Comments.cs) containing comments information.
So a user can have many posts but a post can belong to only one user,
a user can have many comments but a comment can belong to only a user,
a post can have many comments but a comment can only belong to a post,
My question is, how do I write the three models? So far I have this:
public class Users
{
public int UserID { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public DateTime DOB { get; set; }
public string Sex { get; set; }
public string Email { get; set; }
public DateTime RegDate { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string PasswordSalt { get; set; }
public virtual List<Posts> Post { get; set; }
public virtual List<Comments> Comment { get; set; }
}
class Posts
{
public int PostID { get; set; }
public string PostTitle { get; set; }
public string PostContent { get; set; }
public DateTime PostDate { get; set; }
public int AuthorID { get; set; }
public int CommentID { get; set; }
public virtual List<Comments> Comment { get; set; }
public virtual Users user { get; set; }
}
public class Comments
{
public int CommentID { get; set; }
public string Comment { get; set; }
public DateTime CommentDate { get; set; }
public int AuthorID { get; set; }
public int PostID { get; set; }
public virtual Posts post { get; set; }
public virtual Users user { get; set; }
}
Please how do I write the three models correctly? Help!!!

You have strongly typed model classes, and you're already using them correctly. You just need to remove the redundant properties pointing to ID's - Comments doesn't need an int AuthorID pointing to the author when it already has Users user.
Remove these properties:
class Posts
{
public int AuthorID { get; set; }
public int CommentID { get; set; }
}
public class Comments
{
public int AuthorID { get; set; }
public int PostID { get; set; }
}

Related

Entity Framework Core 5 - Introducing FOREIGN KEY constraint on table may cause cycles or multiple cascade paths

I am trying to implement Entity Framework core 5
and am also new to it. below are three models that I am trying to implement.
before posting a new question, I checked the following answers but couldn't understand the card example. Maybe my problem listed below will help anyone else like me today to understand it better.
Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths - why?
Foreign key constraint may cause cycles or multiple cascade paths?
My models are given below :
[Table("Clinics")]
public class Clinic
{
public int ClinicID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Address { get; set; }
public List<Doctor> DoctorsAvailable { get; set; } = new List<Doctor>();
}
[Table("Doctors")]
public class Doctor
{
public int ID { get; set; }
public string Name { get; set; }
public DoctorsSpecilization Specilization { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public List<Clinic> ClinicsAvailableAt { get; set; } = new List<Clinic>();
}
[Table("Patients")]
public class Patient
{
public int PatientID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public int DoctorID { get; set; }
public Doctor Doctor { get; set; }
}
[Table("Consultations")]
public class Consultation
{
public int ID { get; set; }
public int ClinicID { get; set; }
public int DoctorID { get; set; }
public int PatientID { get; set; }
public Clinic Clinic { get; set; }
[ForeignKey("DoctorID")]
public Doctor Doctor { get; set; }
[ForeignKey("PatientID")]
public Patient Patient { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
The problem is the navigation properties of the Doctor and Patient in the Consultation model. When I try to "update-database" it fails with
Introducing FOREIGN KEY constraint 'FK_Consultations_Patients_PatientID' on table 'Consultations' 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.
However, if the navigation properties are removed it works fine. You may ask why I need to have those navigation properties. It's for the sake of displaying relevant information on the view.
Any help in explaining or commenting about the concept would be very much appreciated.
Thank you.
Here are the cascade delete paths in the shown model
Clinic -> Consultation
Doctor -> Consultation
Patient -> Consultation
Doctor -> Patient -> Consultation
The problem (multiple cascade paths) are the last two. As you can see, when deleting a Doctor, the linked Consultation records can be deleted either directly or view linked Patient records. Because of that possibility, some databases (mainly SqlServer) reject cascade delete options and require you to turn of it for at least one of the relationships forming the cycle and handle the deletion manually or via trigger.
So normally that's what you should do when such cycle exists.
But here looks like something is wrong with the model. Either Patient should not be linked to a single Doctor, but to many via linking table and removing Patient.Doctor navigation property (thus the associated FK relationship), thus naturally breaking the multiple cascade paths, i.e. deleting Doctor deletes just links to clinics and patients, but not clinics and patients themselves.
Or, if you want to keep Patient to single Doctor relationship, then Consultation.Doctor (and associated Consultation.DoctorId FK and relationship) is redundant - the doctor of the consultation can be obtained via consultation.Patient.Doctor). So remove it and that will also solve the multiple cascade paths issue since there will be no more Doctor -> Consultation cascade delete link.
For clarity, the first suggested option requires the following model changes:
[Table("Clinics")]
public class Clinic // Unchanged
{
public int ClinicID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Address { get; set; }
public List<Doctor> DoctorsAvailable { get; set; } = new List<Doctor>();
}
[Table("Doctors")]
public class Doctor
{
public int ID { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public List<Clinic> ClinicsAvailableAt { get; set; } = new List<Clinic>();
public ICollection<Patient> Patients { get; set; } // <-- added
}
[Table("Patients")]
public class Patient
{
public int PatientID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
//public int DoctorID { get; set; } <-- removed
//public Doctor Doctor { get; set; } <-- removed
public ICollection<Doctor> Doctors { get; set; } // <-- added
}
[Table("Consultations")]
public class Consultation // Unchanged
{
public int ID { get; set; }
public int ClinicID { get; set; }
public int DoctorID { get; set; }
public int PatientID { get; set; }
public Clinic Clinic { get; set; }
[ForeignKey("DoctorID")]
public Doctor Doctor { get; set; }
[ForeignKey("PatientID")]
public Patient Patient { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}
and option 2:
[Table("Clinics")]
public class Clinic // Unchanged
{
public int ClinicID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Address { get; set; }
public List<Doctor> DoctorsAvailable { get; set; } = new List<Doctor>();
}
[Table("Doctors")]
public class Doctor // Unchanged
{
public int ID { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public List<Clinic> ClinicsAvailableAt { get; set; } = new List<Clinic>();
}
[Table("Patients")]
public class Patient // Unchanged
{
public int PatientID { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public int DoctorID { get; set; }
public Doctor Doctor { get; set; }
}
[Table("Consultations")]
public class Consultation
{
public int ID { get; set; }
public int ClinicID { get; set; }
//public int DoctorID { get; set; } <-- removed
public int PatientID { get; set; }
public Clinic Clinic { get; set; }
//[ForeignKey("DoctorID")]
//public Doctor Doctor { get; set; } <-- removed
[ForeignKey("PatientID")]
public Patient Patient { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
}

MVC View display items of a list contained within a list

I wonder if someone could help me please. I have a shopping basket which can contain products and each product can contain customers so for instance:
A basket contains the details of the 'purchaser' (FirstName, LastName, etc.) and
Product details (product name, date, etc.) and each product will have
Delegates (people actually attending the event....FirstName, LastName, etc.
What I'm trying to create is a form which shows all of the products with a list of fields underneath allowing the purchaser to enter/amend the names of the people that are attending the event:
What I'm struggling with though is to create the input fields for the attendees. I can use:
foreach(var dele in product.delegates)
{
<p>#dele.FirstName</p>
}
Which does display a list of the names of the attendees as expected but I would expect to be able to use something like:
for(var i=0, i < product.delegates.Count; i++)
{
#Html.TextBoxFor(x => x[i].FirstName)
}
But this displays the name (repeated) of the purchaser and not the attendees.
Could anyone assist please? Like I say I want to display a list of inputs that can amended and posted to an ActionResult to update the DB.
As a note I can see the model is populated with the information that I'm expecting.
public class ShoppingCart
{
public int ShoppingCartId { get; set; }
public DateTime Created { get; set; }
public string TransStatus { get; set; }
public bool Completed { get; set; }
public string ContactNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Phone { get; set; }
public string Mobile { get; set; }
public string CompanyName { get; set; }
public string PostCode { get; set; }
public string Email { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string City { get; set; }
public string County { get; set; }
public string StoreKey { get; set; }
public DateTime? SentToStore { get; set; }
public virtual ICollection<ShoppingCartProduct> Products { get; set; }
}
public class ShoppingCartProduct
{
public int ShoppingCartProductId { get; set; }
public int ShoppingCartId { get; set; }
public string ProductId { get; set; }
public string CourseId { get; set; }
public string ProductName { get; set; }
public string ProductType { get; set; }
public decimal ItemPrice { get; set; }
public string Location { get; set; }
public DateTime? EventDate { get; set; }
public int Quantity { get; set; }
public decimal TotalPrice => ItemPrice * Quantity;
public virtual ShoppingCart ShoppingCart { get; set; }
public virtual ICollection<ShoppingCartDelegate> delegates { get; set; }
}
public class ShoppingCartDelegate
{
public int ShoppingCartDelegateID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string email { get; set; }
public int ShoppingCartProductId { get; set; }
public virtual ShoppingCartProduct ShoppingCartProduct { get; set; }
}
Sorry buddy, it was me.....the objects were collections and not lists so I was never going to be able to iterate through them with an index. Sorted now. Thanks for you time :)

ASP.NET MVC Check if User has already posted in this table

I'm gonna cut to the chase.
I'm creating a Survey platform, it has 3 models.
Model Survey, it has Many SurveyQuestion which has many SurveyAnswer.
(I can insert all of the values of these models but I dont think it is needed)
public class SurveyAnswer
{
[Key]
public int Id { get; set; }
public string Value { get; set; }
public string SubmittedBy { get; set; }
public int SurveyId { get; set; }
public int QuestionId { get; set; }
public virtual Survey Survey { get; set; }
public virtual SurveyQuestion Question { get; set; }
public string Comment { get; set; }
}
Now a problem I'm having is once someone created a survey and another person is starting it, he answers and that's it. How do I show that the next time he comes to an index page? How do I show that "you already submitted this survey"? Do I do that in Controller or in View? I would prefer to that in this action currently (it's a menu for all ongoing surveys).
[HttpGet]
public ActionResult Menu()
{
var survey = Mapper.Map<IEnumerable<Survey>, IEnumerable<SurveyViewModel>>(_unitOfWork.SurveyRepository.Get());
return View(survey.ToList());
}
Put all your validation rules to your AbstractValidator class.
[Validator(typeof(SurveyAnswerValidator))]
public class SurveyAnswer{
[Key]
public int Id { get; set; }
public string Value { get; set; }
public string SubmittedBy { get; set; }
public int SurveyId { get; set; }
public int QuestionId { get; set; }
public virtual Survey Survey { get; set; }
public virtual SurveyQuestion Question { get; set; }
public string Comment { get; set; }
}
public class SurveyAnswerValidator : AbstractValidator<SurveyAnswer>
{
public SurveyAnswerValidator()
{
//list your rules
RuleFor(x => x.SubmittedBy).Must(BeUnique).WithMessage("Already
submitted this survey");
}
private bool BeUnique(string submittedBy)
{
if(_context.SurveyAnswers.
FirstOrDefault(x => x.SubmittedBy == submittedBy) == null){
return true;
}
else{
return false;
}
}
}
If you want to check uniqueness in ViewModel you can use Remote.
public class SurveyAnswerVM{
[Key]
public int Id { get; set; }
public string Value { get; set; }
[Remote("HasSubmitted", "ControllerName")]
public string SubmittedBy { get; set; }
public int SurveyId { get; set; }
public int QuestionId { get; set; }
public virtual Survey Survey { get; set; }
public virtual SurveyQuestion Question { get; set; }
public string Comment { get; set; }
}
Where HasSubmitted is a method you may create in controller to return true if the user has submitted.
RemoteAttribute
https://msdn.microsoft.com/en-us/library/gg508808(VS.98).aspx
The best solution by vahdet (suggested in comments)
[Index("IX_AnswerQuestion", 2, IsUnique = true)]
[StringLength(36)]
public string SubmittedBy { get; set; }
public int SurveyId { get; set; }
[Index("IX_AnswerQuestion", 1, IsUnique = true)]
public int QuestionId { get; set; }

asp.net using mvc 3 linq and join

i have a problem with my join in my asp.net .. i have two database tables.. named APPLICANT and Profile...they have the same fields.. meaning if the Last name in applicant is null the last name is already in profile table. I already connect my program to the applicant table.. but it have so many null fields that have to fetch from the profile data table.... sorry i'm new in asp.net ...
Here's my code in controller:
public View Result Index()
{
var applicants = (from a in db.APPLICANTs
select a).ToList();
return View(applicants);
}
heres my context:
public partial class APPLICANT
{
public int APPLICANT_ID { get; set; }
public Nullable<int> Profile_id { get; set; }
public string APPLICANT_LastName { get; set; }
public string APPLICANT_FirstName { get; set; }
public string APPLICANT_MiddleName { get; set; }
public string APPLICANT_Address { get; set; }
public string APPLICANT_City { get; set; }
public string APPLICANT_ZipCode { get; set; }
public string APPLICANT_Phone { get; set; }
public string APPLICANT_Email { get; set; }
}
public partial class Profile
{
public int PROFILE_ID { get; set; }
public string Applicant_LASTNAME { get; set; }
public string Applicant_FIRSTNAME { get; set; }
public string Applicant_MIDDLENAME { get; set; }
public string Applicant_EMAIL { get; set; }
public string Applicant_PHONE { get; set; }
public string Applicant_ADDRESS { get; set; }
public string Applicant_ZIPCODE { get; set; }
public string Applicant_CITY { get; set; }
}
thanks for those who can help my problem..

MVC - Entity framework - Metadata relation

Today I've been working with MVC for the first time. Also normally I use the EF with model first, but I wanted to try POCO.
So I've made my 3 entities and when I try to make a controller I get an error:
Unable to retrieve metadata for "BookExchange.Models.Exchange". Unable to determine the principal end of an association between the types "BookExchange.Models.Exchange" and "BookExchange.Models.Book". The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
My 3 classes:
public class Book
{
public int BookID { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public string ISBN10 { get; set; }
public int PersonID { get; set; }
public virtual Person Person { get; set; }
public virtual Exchange Exchange { get; set; }
}
public class Person
{
public int PersonID { get; set; }
public string Name { get; set; }
public virtual ICollection<Book> Books { get; set; }
public virtual ICollection<Exchange> Exchanges { get; set; }
}
public class Exchange
{
[Key]
public int BookID { get; set; }
public int PersonID { get; set; }
public DateTime ReturnDate { get; set; }
public virtual Person Person { get; set; }
public virtual Book Book { get; set; }
}
Does anyone know what I'm doing wrong? I don't want to lose the association properties.
Thanks in advance!
Try adding foreign key properties for your references. E.g.
public class Book
{
public int BookID { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public string ISBN10 { get; set; }
public virtual Person Person { get; set; }
public int PersonID { get; set; }
public virtual Exchange Exchange { get; set; }
public int ExchangeID { get; set; }
}
public class Person
{
public int PersonID { get; set; }
public string Name { get; set; }
public virtual ICollection<Book> Books { get; set; }
public virtual ICollection<Exchange> Exchanges { get; set; }
}
public class Exchange
{
public int ExchangeID { get; set; }
public int BookID { get; set; }
public virtual Book Book { get; set; }
public int PersonID { get; set; }
public virtual Person Person { get; set; }
public DateTime ReturnDate { get; set; }
}
Also, take a look at ScottGu's post on code first and this EF post on conventions.
Try this: (Remove database, so EF will create new)
public class Book
{
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public string ISBN { get; set; }
public virtual Person Person { get; set; }
public virtual Exchange Exchange { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Book> Books { get; set; }
public virtual ICollection<Exchange> Exchanges { get; set; }
}
public class Exchange
{
public int Id { get; set; }
public DateTime ReturnDate { get; set; }
public virtual Person Person { get; set; }
public virtual Book Book { get; set; }
}
It's your one on one associations.
Remove one reference between exchange or book, so Code-first can decide which one is more important in your one on one relation (Book <--> Exchange)
If you want to know why, you should read this:

Resources