I am building an MVC app in ASP.Net and I want to combine my user model with my address model for registration purposes. Now I need to know how to implement the ViewModel for this. I am using code first and I am a beginner.
Below is the code for my two models and the db context.
public class SiteDataModel : DbContext
{
// Your context has been configured to use a 'SiteDataModel' connection string from your application's
// configuration file (App.config or Web.config). By default, this connection string targets the
// 'WMVC.SiteDataModel' database on your LocalDb instance.
//
// If you wish to target a different database and/or database provider, modify the 'SiteDataModel'
// connection string in the application configuration file.
public SiteDataModel()
: base("name=SiteDataModel")
{
}
// Add a DbSet for each entity type that you want to include in your model. For more information
// on configuring and using a Code First model, see http://go.microsoft.com/fwlink/?LinkId=390109.
public virtual DbSet<Web_User> WebUsers { get; set; }
public virtual DbSet<Web_User_Address> WebUserAddresses { get; set; }
}
public class Web_User
{
[Key]
public int User_Id { get; set; }
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Email")]
[DataType(DataType.EmailAddress, ErrorMessage = "Please enter a valid e-mail address")]
public string Email { get; set; }
[Display(Name = "Password")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Cellphone Number")]
[DataType(DataType.PhoneNumber)]
public string CellPhoneNumber { get; set; }
[Display(Name = "Date of Birth")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime DateOfBirth { get; set; }
public int Address_Id { get; set; }
public virtual Web_User_Address UserAddresses { get; set; }
}
public class Web_User_Address
{
[Key]
public int Address_Id { get; set; }
[Required]
[Display(Name="Postal Address")]
[DataType(DataType.PostalCode)]
public int PostalCode { get; set; }
[Required]
public string Street { get; set; }
public string Province { get; set; }
public ICollection<Web_User> WebUsers { get; set; }
}
A few pointers, you might need to update your Web_User_Address, to exclude "WebUsers" and have 1 userID, and your web_user Dbset should not have any addresslink( or adress ID)
look at it this way: one user can have many addresses. even if that is not the case in your program.
Accessing the two DbSets in the viewmodel, you can do something like this:
class RegistrationViewModel
{
SiteDataModel db = new SiteDataModel();
public AddUserAndAddress(Web_User_Address address, Web_User user){
//Note: address has a userId
var user = db.Web_User.First(a => a.Name== user.Name && /*etc*/ );
if(user != null){
// User already exists. Do something about that here
}
db.Web_User_Address.Add(address);
db.Web_User.Add(user);
db.SaveChanges();
}
}
I hope this helps. best of luck!
After re-reading your question. I finally understood what you asked.
The simple way to do this is to create a model with both Dbsets :)
Related
I have a system that produce some online classes. In this class we have students and a teacher.
I have used Microsoft default Identity System and Authorization for this system, but something is bothering me in my design.
For more explanation I want to define Roles(Teacher, Students, Admins and etc) , but it is so confusing how to handle relation between Course and Teacher (it is one to many relation) and Course to Students (it has many to many relation).
So I have question is that true way to have two relation between two entities or not? if it is not, How should I handle this?
Here is my Course entity
[Key]
[Display(Name = "شناسه")]
public Guid CourseId { get; set; }
[Required]
[Display(Name = "لوگوی دوره")]
public string LogoPath { get; set; }
[Required]
[Display(Name = "نام دوره")]
public string Name { get; set; }
[Required]
[Display(Name = "شرح دوره")]
public string Description { get; set; }
[Required]
[Display(Name = "شهریه")]
public int Price { get; set; }
[Display(Name = "دارای تخفیف")]
public bool HasDiscount { get; set; }
[Display(Name = "درصد تخفیف")]
public float DiscountPercentage { get; set; }
[Required]
[Display(Name = "آخرین تاریخ به روزرسانی")]
public DateTime LastUpdateUpdate { get; set; }
public string UserId { get; set; }
public AppUser CourseTeacher { get; set; }
public Guid CaptionId { get; set; }
public MainCaption CourseCaption{ get; set; }
public ICollection<Chapter> Chapters { get; set; }
public ICollection<AppUser> Students{ get; set; }
and here is my AppUser entity
[Required]
[Display(Name = "نام")]
public string Firstname { get; set; }
[Required]
[Display(Name = "نام خانوادگی")]
public string LastName { get; set; }
[Required]
[Display(Name = "جنسیت")]
public Gender Gender { get; set; }
[Display(Name = "عنوان")]
public string Title { get; set; }
[Display(Name = "اعتبار")]
public int Credit { get; set; }
[Display(Name = "تاریخ تولد")]
public string BirthDate { get; set; }
[Display(Name = "مدرک تحصیلی")]
public EducationalDegree? Degree { get; set; }
[Display(Name = "آدرس تصویر")]
public string ImagePath { get; set; }
[Display(Name = "تصویر تایید شده")]
public bool? IsImageConfirmed { get; set; }
[Display(Name = "آدرس فیس بوک")]
public string Facebook { get; set; }
[Display(Name = "آدرس اینستاگرام")]
public string Instagram { get; set; }
[Display(Name = "آدرس لینکداین")]
public string Linkedin { get; set; }
[Display(Name = "آدرس توئیتر")]
public string Twitter { get; set; }
[Display(Name = "آدرس وبسایت")]
public string Website { get; set; }
[Display(Name = "تاریخ ثبت نام")]
public DateTime RegisterDate { get; set; }
public ICollection<Course> StudentCourses { get; set; }
public ICollection<Course> TeacherCourses { get; set; }
public ICollection<News> WrittenNews { get; set; }
Tnx to All
Edit
I forgot to say this contains an error Sequence contains more than one matching element and it seems logical
One important this is that if I use same class for inheritance how should I add two relations for this two tables AppUser and Course
I want to define Roles(Teacher, Students, Admins and etc)
You can do it in a couple different ways:
Have User and Role tables and enforce roles on the application level, e.g. Only "teacher" user can do teacher things, only student can enrol into courses etc.
With EF you can use inheritance. Abstract User would have all the common fields and Student, Teacher and Admin would have fields specific only to their role.
Please see the code:
abstract class User
{
public int UserId { get; set; }
public string Name { get; set; }
}
class Teacher : User
{
public string Specialty { get; set; }
}
class Student : User
{
public int Grade { get; set; }
}
See more info here - the example given in this official documentation is very close to what you're trying to achieve.
Course to Students (it has many to many relation)
For this type of a relationship I'd create a new table/entity StudentCourse with composite (StudentId, CourseId) key. And the reason for it is, usually you don't just want a link between 2 entities but also to keep some additional info like Mark, Performance or EnrolmentDate:
class StudentCourse
{
public int StudentId { get; set; }
public int CourseId { get; set; }
public Student Student { get; set; }
public Course Course { get; set; }
// Any additional fields related to the relationship
public int Mark { get; set; }
}
I can't build Identity on a user table already exist and I could not modify the user table within Identity
I created the models and then I generated Controls and Views.
https://imgur.com/Q6yhKgZ.png
Then I try to create a new identity through add new scaffolded item, but I could not use the already existing user table
https://imgur.com/V5YCuyz.png
And I also tried to create a new project and create the Identity first and then modify the Identity user table by adding some columns and then create my tables on the same context, but I will not find the user model to modify it and create the relationships with the other entities.
[Table("User")]
public class User
{
[Key]
public int Id { get; set; }
[Required]
[Display(Name = "Name")]
[DataType(DataType.Text)]
public string Name { get; set; }
[Required]
[Display(Name = "Phone Number")]
[DataType(DataType.PhoneNumber)]
public string Phone { get; set; }
[Required]
[Display(Name = "Email Address")]
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
[Required]
[Display(Name = "Password")]
[DataType(DataType.Password)]
[StringLength(20, MinimumLength = 6)]
public string Password { get; set; }
[Required]
[Display(Name = "Confirm Password")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "Confirm password doesn't match, Type again !")]
[NotMapped]
public string ConfirmPassword { get; set; }
[Required]
[Display(Name = "Gender")]
[DataType(DataType.Text)]
public string Gender { get; set; }
[Required]
[Display(Name = "Birth Date")]
[DataType(DataType.Date)]
[CustomDate(ErrorMessage = "Use Correct Birthdate")]
public DateTime BirthDate { get; set; }
public bool Eanble { get; set; }
public string Permissions { get; set; }
[DataType(DataType.DateTime)]
public DateTime CreateAt { get; set; }
[DataType(DataType.DateTime)]
public DateTime UpdateAt { get; set; }
[InverseProperty("Owner")]
public List<Article> Articles { get; set; }
[InverseProperty("Owner")]
public List<Consultation> Consultations { get; set; }
[InverseProperty("Owner")]
public List<ConsultationComment> ConsultationComments { get; set; }
}
You are taking the wrong approach.
In order to add your own custom properties to the user you should create a class which derives from Microsoft.AspNetCore.Identity.IdentityUser. There you can add your own custom properties which would be added to the table. Here is how you can do it:
// using Microsoft.AspNetCore.Identity; is needed
public class ApplicationUser : IdentityUser
{
public int Age { get; set; }
}
Now you should create a class which derives from IdentityDbContext:
public class AppIdentityDbContext : IdentityDbContext<ApplicationUser>
{
public AppIdentityDbContext(DbContextOptions<AppIdentityDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
This is basically a DbContext with 2 DbSets that Identity will use.
Then go to your Startup.cs and add the following line inside ConfigureServices:
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<AppIdentityDbContext>();
Now you can build your project and see the changes applied to your database. You can not remove Identity properties, but you can add your own.
Let's say I have an User model which has Email and Password properties for authentication purposes, as follows:
public class User
{
public long Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
}
My goal is to make these properties required only on POST requests, but not on PUT. That is: Email and Password fields are required to create a new user. But when editing I can omit these properties. I know that this goal can be archived removing the [Required] from Email and Password and checking for these properties when POSTing, but this does not seem to be a good practice.
So, there is a more elegant solution for this use case?
You shouldn't use an Entity as input/output parameters. Instead, create two separate view models that represent the actions being called:
public class User
{
public long Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
}
// When creating, the client cannot know the Id because it doesn't exist
public class CreateUserViewModel
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
}
// and when updating, the Id is required but not the Email nor the Password
public class UpdateUserViewModel
{
[Required]
public long Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
}
Of course, since you are creating an API, "View Model" might not make a lot of sense. You may use the Data Transfer Object (DTO) term instead.
One suggestion would be to use View Models. In your case, you could have a RegisterViewModel that takes all of the values from the model and then you could have an EditViewModel that doesnt include email and password. In your controller, your edit function can either replace the old values for the other field or just ignore them.
https://www.codeproject.com/Articles/1174826/Using-MVVM-in-MVC-Applications-Part
My database is created using code first. Here is the model for my client.
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Display(Name = "Phone number")]
public int PhoneNumber { get; set; }
[Display(Name = "Email adress")]
[EmailAddress]
public string EmailAdress { get; set; }
[Display(Name = "Date of the wedding")]
public DateTime DateOfTheWedding { get; set; }
[Display(Name = "Type of client")]
public TypeOfClient TypeOfClient { get; set; }
[NotMapped]
public int DaySinceLastVisit { get; set; }
I also have a model Visit :
public int Id { get; set; }
public Client Client { get; set; }
[Required]
[ForeignKey("Client")]
public int ClientId { get; set; }
[Display(Name = "Date of the visit")]
public DateTime DateOfTheVisit { get; set; }
public String Comment { get; set; }
Each client have a multiple visit.
How can I get the last visit and add it to my model ?
Here is my resquest linq that I want to improve.
var clientsQueryable = _context.Clients
.Include(c => c.TypeOfClient);
if (!String.IsNullOrWhiteSpace(query))
clientsQueryable.Where(client => client.FirstName.Contains(query));
var listClients = clientsQueryable
.ToList()
.Select(Mapper.Map<Client, ClientDto>);
return Ok(listClients);
I want to have in Client the info DaySinceLastVisit.
I presume that we need to get the last visit and make a calculation to get the days that have passed since this visit ?
First of all:
If a Client has multiple Visits, it would be good to add a
public virtual ICollection<Visit> Visits { get; set; }
To your Client entity, so you can easily access all the Visits linked to a Client.
Then it's easy to calculate the "DaySinceLastVisit" using similar code like this:
public int DaySinceLastVisit => (DateTime.Now - Visits.OrderByDescending(v => v.DateOfTheVisit).FirstOrDefault().DateOfTheVisit).Days;
I am trying to update the database using entity framework, I map my entities to viewmodels using automapper, and map it back the same way:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([FromJson] MyCVViewModel model)
{
var userId = User.Identity.GetUserId();
//find the cv
CV cv = repository.FindCV(model.CVId);
//auto mapper mapping
Mapper.CreateMap<MyCVViewModel, CV>();
Mapper.CreateMap<MyCompanyViewModel, Company>();
cv = Mapper.Map<MyCVViewModel, CV>(model, cv);
//edit
repository.EditCV(cv);
}
When I map it back, the foreign key CVid inside company entity becomes 0, i think something was lost during the mapping process, how do you map the foreign key?
Here is my view model and entity:
View Model:
public class MyCVViewModel
{
public int CVId { get; set; }
[Required]
[StringLength(100, ErrorMessage = "Title cannot exceed 100 characters.")]
[Display(Name = "Title")]
public string Title { get; set; }
[Required]
[StringLength(1000, ErrorMessage = "Statment cannot exceed 1000 characters.")]
[Display(Name = "Statement")]
public string Statement { get; set; }
public bool Reference { get; set; }
public List<MyCompanyViewModel> Companies { get; set; }
}
public class MyCompanyViewModel
{
[Required]
[StringLength(100, ErrorMessage = "Company Name cannot exceed 100 characters.")]
[Display(Name = "Company Name")]
public string CompanyName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "Job Title cannot exceed 100 characters.")]
[Display(Name = "Job Title")]
public string JobTitle { get; set; }
[Required]
[DataType(DataType.Date)]
[Display(Name = "Start Date")]
public DateTime StartDate { get; set; }
[Required]
[DataType(DataType.Date)]
[Display(Name = "End Date")]
public DateTime EndDate { get; set; }
[Required]
[StringLength(1000, ErrorMessage = "Job Description cannot exceed 1000 characters.")]
[Display(Name = "Job Description")]
public string Description { get; set; }
}
Entity:
public class CV
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CVId { get; set; }
public string Title { get; set; }
public string Statement { get; set; }
public bool Reference { get; set; }
public virtual ICollection<Company> Companies { get; set; }
}
public class Company
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public string JobTitle { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string Description { get; set; }
public virtual CV CV { get; set; }
public int CVId { get; set; }
}
and this is the error message when I try to update:
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value.
I see where the problem is, but don't know how to tell automapper to retain foreign key value
MyCompanyViewModel class does not contain a definition of CVId property, so by default Automapper does not know where he should take a value for injecting into Company's CVId property. Just define it:
public class MyCompanyViewModel
{
public int CVId { get; set; }
// Other properties
}
Then for each CompanyViewModel add corresponding hidden input field into the view:
#for (int i = 0; i < Model.Companies.Count; i++)
{
// ...
#Html.HiddenFor(m => Model.Companies[i].CVId)
// ...
}
and you are good to go!