I have a UserViewModel for my users. I want to use that for registration.
I do not want to use my datamodel for registration or login.
My UserViewModel is as below:
public class UserViewModel
{
public int user_id { get; set; } //Primary Key in user table
[Required]
[DisplayName("Email:")]
public string email { get; set; }
[Required]
[DisplayName("First Name:")]
public string f_name { get; set; }
[Required]
[DisplayName("Last Name:")]
public string l_name { get; set; }
[Required]
[DisplayName("Contact Number:")]
public string contact { get; set; }
[Required]
[DisplayName("Gender:")]
public string gender { get; set; }
[Required]
[DisplayName("Blood Type:")]
public string blood_type { get; set; }
[Required]
[DisplayName("Password:")]
[DataType(DataType.Password)]
public string password { get; set; }
[Required]
[DisplayName("Confirm Password:")]
[DataType(DataType.Password)]
[Compare("password")]
public string confirm_password { get; set; }
}
My Registration ActionMethod is as below:
[HttpPost]
public ActionResult Registration(UserViewModel uvmr)
{
db.users.Add(uvmr);
db.SaveChanges();
return View();
}
My dataModel for user(user.cs) is as below:
public partial class user
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public user()
{
this.appointments = new HashSet<appointment>();
}
public int user_id { get; set; }
public string f_name { get; set; }
public string l_name { get; set; }
public string email { get; set; }
public string contact { get; set; }
public string gender { get; set; }
public string blood_type { get; set; }
public string password { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<appointment> appointments { get; set; }
}
N.B: I have no Confirm Password column in my user table.
Now the error says cannot convert from das.Models.ViewModel.UserViewModel to das.Models.DataModel.user
What is work-around for this one?
You can't provide just any object instance to db.users.Add(uvmr); - the DbSet<T>.Add() method is typed to each of your data models.
Since your models are fairly similar, converting one to the other is relatively simple:
var newUser = new user
{
user_id = uvmr.user_id
f_name = uvmr.f_name
l_name = uvmr.l_name
email = uvmr.email
contact = uvmr.contact
gender = uvmr.gender
blood_type = uvmr.blood_type
password = uvmr.password
};
Then you can add the new user instance:
db.users.Add(newUser);
Assuming that you don't want to duplicate this code everytime you need to map one to the other, you can create a mapping utility - I tend to use a static class for that:
public static class Mapper
{
public static user MapUser(UserViewModel uvmr)
{
return uvmr == null ? null : new user
{
user_id = uvmr.user_id
f_name = uvmr.f_name
l_name = uvmr.l_name
email = uvmr.email
contact = uvmr.contact
gender = uvmr.gender
blood_type = uvmr.blood_type
password = uvmr.password
};
}
}
Then you could do something like:
var user = Mapper.MapUser(uvmr);
Of course, utilities like AutoMapper can do the same, and in your case might be simpler - AutoMapper, even without any setup code, will try to map properties with the same name.
As an aside, your class names and properties are currently violating Microsoft's naming conventions - I'd suggest reading the Naming Guidelines if this is code meant to shared outside of your organization.
Related
I have a customer object that has a list of sales orders. I am trying to create a partial view on my webpage that lets me use radio buttons to click through and see the lists per customer. The issue is that in the customer controller, I need to pass the customer IEntity (via an id) to the constructor.
I tried looking this up but I am new to all this and I don't think I am using the right words or maybe I just don't understand well enough yet to find the answer in other peoples code.
Customer Object (and constructor obviously)
public class Customer : IEntity
{
[Key]
public int CustomerId { get; set; }
public int Id => CustomerId;
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Display(Name = "Customer")]
public virtual string Name => $"{FirstName} {LastName}";
[Display(Name = "Address")]
public string Address1 { get; set; }
public string Address2 { get; set; }
[Display(Name = "City")]
public string City { get; set; }
[Display(Name = "State")]
public string State { get; set; }
[Display(Name = "Zipcode")]
public string Zipcode { get; set; }
[Display(Name = "Email")]
public string Email { get; set; }
[Display(Name = "Phone Number (USA)")]
public string UsaPhone { get; set; }
public decimal TotalSales => SalesOrders.Sum(so => so.OrderTotal);
public List<SalesOrder> SalesOrders { get; private set; }
public Customer()
{
SalesOrders = new List<SalesOrder>();
}
}
Code from the Customer Controller where I am getting the error
public ActionResult _customersSalesOrders(int id)
{
var customer = db.Customers.Find(id);
var salesOrderList = new List<SalesOrder>(customer);
return PartialView(salesOrderList);
}
The result is error CS1503 "Argument 1: cannot convert from 'ITP245.Models.Customer' to 'int'
You are creating a new List<SalesOrder>, which means you can only place into it objects that are of type SalesOrder. Your Customer class does not inherit from SalesOrder & cannot be placed into the list.
Maybe you mean to place the customer's sales orders into the list?
If so, your code might look something like this:
public ActionResult _customersSalesOrders(int id)
{
var customer = db.Customers.Find(id);
var salesOrderList = customer.SalesOrders;
return PartialView(salesOrderList);
}
Is there any way to somehow combine the data from two models and THEN map them both to the same viewModel in the context of an edit action?
I have never had to update several tables at once in an edit action in ASP.NET MVC with Entity Framework 6.1.3. This is the layout:
I have a DB table called "Address" which has fields for StreetNumber, StreetName, City, State, ZipCode. It has a one-to-one relationship with another table called Bars. As in, a bar can only have one address and one address can only have one bar.
Because I am storing this data in two separate tables, I am having a very difficult time trying to successfully implement an Edit action which takes data from one form (BarForm) and should update both the Bar and Address database tables. See my code:
BarController
public ActionResult Edit(int id)
{
var bar = _context.Bars.SingleOrDefault(m => m.Id == id);
var address = _context.Addresses.SingleOrDefault(a => a.BarId == id);
//Make sure that the id actually exists:
if (bar == null)
{
return HttpNotFound();
}
var viewModel = Mapper.Map<Bar, BarFormViewModel>(bar, new BarFormViewModel());
if (address == null)
{
address = new Address();
}
Mapper.Map<Address, BarFormViewModel>(address, viewModel);
viewModel.IsNew = false;
return View("BarForm", viewModel);
}
[ValidateAntiForgeryToken]
public ActionResult Save(BarFormViewModel bar)
{
if (!ModelState.IsValid)
{
var viewModel = Mapper.Map<BarFormViewModel, BarFormViewModel>(bar, new BarFormViewModel());
viewModel.IsNew = false;
return View("BarForm", viewModel);
}
if (bar.Id == 0)
{
var newbar = Mapper.Map<BarFormViewModel, Bar>(bar);
newbar.LastUpdated = DateTime.UtcNow;
_context.Bars.Add(newbar);
var addressToAdd = Mapper.Map<BarFormViewModel, Address>(bar);
_context.Addresses.Add(addressToAdd);
}
else
{
var barInDb = _context.Bars.Single(b => b.Id == bar.Id);
var addressInDb = _context.Addresses.Single(a => a.BarId == bar.Id);
Mapper.Map<BarFormViewModel, Bar>(bar, barInDb);
Mapper.Map<BarFormViewModel, Address>(bar, addressInDb);
}
_context.SaveChanges();
return RedirectToAction("Index", "Bar");
}
Domain Models:
public class Bar
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public string GooglePlaceId { get; set; }
public string SundayDiscounts { get; set; }
public string MondayDiscounts { get; set; }
public string TuesdayDiscounts { get; set; }
public string WednesdayDiscounts { get; set; }
public string ThursdayDiscounts { get; set; }
public string FridayDiscounts { get; set; }
public string SaturdayDiscounts { get; set; }
[Display(Name = "Last Updated")]
public DateTime LastUpdated { get; set; }
}
public class Address
{
public int Id { get; set; }
public int? Number { get; set; }
public string StreetName { get; set; }
public string City { get; set; }
public string State { get; set; }
[Required]
public int ZipCode { get; set; }
public Bar Bar { get; set; }
public int BarId { get; set; }
}
View Model which includes both Address and Bar properties:
{
public class BarFormViewModel
{
public int? Id { get; set; }
public string Name { get; set; }
[Required]
[Display(Name = "Google Place ID")]
public string GooglePlaceId { get; set; }
[Display(Name = "Sunday Happy Hour Info:")]
public string SundayDiscounts { get; set; }
[Display(Name = "Monday Happy Hour Info:")]
public string MondayDiscounts { get; set; }
[Display(Name = "Tuesday Happy Hour Info:")]
public string TuesdayDiscounts { get; set; }
[Display(Name = "Wednesday Happy Hour Info:")]
public string WednesdayDiscounts { get; set; }
[Display(Name = "Thursday Happy Hour Info:")]
public string ThursdayDiscounts { get; set; }
[Display(Name = "Friday Happy Hour Info:")]
public string FridayDiscounts { get; set; }
[Display(Name = "Saturday Happy Hour Info:")]
public string SaturdayDiscounts { get; set; }
[Display(Name = "Last Updated")]
public DateTime? LastUpdated { get; set; }
//Address Model Info
public Address Address { get; set; }
public int? AddressId { get; set; }
[RegularExpression("([1-9][0-9]*)", ErrorMessage = "Must be a number")]
public int? Number { get; set; }
public string StreetName { get; set; }
public string City { get; set; }
public string State { get; set; }
[Required]
public int? ZipCode { get; set; }
public bool IsNew { get; set; }
}
The problem here is that I am getting an empty AddressId with this setup, which is causing an exception when the Save action gets run. This is because the BarForm view is getting passed a ViewModel which has been mapped from a Bar object and the Bar domain model actually has no Address information in it, since it is not the Address model/table.
Is there any way to somehow combine the data from both the Address and Bar models and THEN map them both to the same viewModel?
I keep getting a Sequence Contains no Elements error for this line in the Save action:
var addressInDb = _context.Addresses.Single(a => a.Id == bar.AddressId);
I also tried:
var addressInDb = _context.Addresses.Single(a => a.BarId == bar.Id);
Neither work. I understand what the error is saying and have also checked the actual HTML for my hidden Addressid field and it is blank... See code in my BarForm View:
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.AddressId)
#Html.AntiForgeryToken()
Remove the new BarFormViewModel() as the second parameter in your mapping calls as it is not necessary.
In your post action, inside your if statement that checks if the ModelState is valid and if bar.Id == 0, bar is already a view model, so no need to mapping.
And when you create your AutoMapper mapping, you must create a custom property mapping because the Address.Id property will not map automatically to the AddressId property as the name is not the same.
AutoMapper.Mapper.CreateMap<Address, BarFormViewModel>()
.ForMember(dest => dest.AddressId, o => o.MapFrom(source => source.Id));
And then do the same for the inverse mapping.
I am trying to add this information into my database. My SQL Server will generate the Id for each row in the table. However, for some reason my code is adding a "0" for Id and I cannot figure out why or where it is coming from and I need to remove it so that the database can just generate it.
Here is my code:
public class Contact
{
public Contact()
{
}
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[StringLength(50)]
public string Email { get; set; }
[Column(TypeName = "ntext")]
[Required]
public string Message { get; set; }
[Column(TypeName = "date")]
[Required]
public DateTime Date { get; set; }
[Column(TypeName = "time")]
[Required]
public TimeSpan Time { get; set; }
}
public class Context : DbContext
{
public Context()
: base("ContactConnectionString")
{
}
public DbSet<Contact> ContactForm { get; set; }
}
public class ImportToDataBase
{
public static void SubmitToDatabase(string theMessage, string fullName, string emailAddress)
{
using (var db = new Context())
{
var contact = new Contact()
{
Email = emailAddress,
Message = theMessage,
Name = fullName,
Date = DateTime.Now.Date,
Time = DateTime.Now.TimeOfDay,
// ID is not set in here
};
db.ContactForm.Add(contact);
db.SaveChanges();
}
}
}
}
Try decorating your Id property with the following:
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
The Key attribute tells Entity Framework that the Id property is the primary key and should not be included in the insert statement.
As a best practice, you need to mark your id with Key and Identity Option.
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
For fluent API you can use as:
HasKey(i => i.ID);
Property(i => i.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
I have an Attendee Class and An AttendeeViewModel
The datetime field on the Attendee Model gets set to the default .NET Datetime when i map it from AttendeeViewModel instead of the value that is already existing in the Attendee Model
Here's my AttendeeViewModel
public class AttendeeViewModel
{
public int Id { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
public int FEventId { get; set; }
public string DisplayName
{
get { return string.Format("{0}, {1}", FirstName, LastName); }
}
}
Here's my Base AttendeeModel
public class Attendee
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public DateTime CreatedAt { get; set; }
public bool IsActive { get; set; }
public int FEventId { get; set; }
public virtual ApplicationUser CreatedBy { get; set; }
public virtual FEvent FEvent { get; set; }
public ICollection<ProjectPledge> ProjectPledges { get; set; }
}
Here's My mapping configuration
public static void Configure()
{
Mapper.CreateMap<AttendeeViewModel, Attendee>().ForMember(dest=>dest.CreatedAt , opt=>opt.Ignore());
}
And heres's the Controller Action
[HttpPost]
[ValidateAntiForgeryToken]
public virtual ActionResult Edit(AttendeeViewModel attendee)
{
if (!_attendeeService.CanAddAttendee(attendee.Email, attendee.FilanthropyEventId))
{
AddEmailModelError();
}
if (ModelState.IsValid)
{
var mappedAttendee = _attendeeService.GetById(attendee.Id);
mappedAttendee = Mapper.Map<AttendeeViewModel, Attendee>(attendee);
_attendeeService.AddOrUpdate(mappedAttendee);
return RedirectToAction(MVC.Attendee.Index(mappedAttendee.FilanthropyEventId));
}
return View(attendee);
}
if I set the configuration to be this insetad of opt.Ignore()
Mapper.CreateMap<AttendeeViewModel, Attendee>().ForMember(dest=>dest.CreatedAt , opt=>opt.UseDestinationValue());
The Mapping fails giving this exception
Missing type map configuration or unsupported mapping.
Mapping types:
AttendeeViewModel -> DateTime
MyProject.Web.ViewModels.AttendeeViewModel -> System.DateTime
Destination path:
Attendee.CreatedAt.CreatedAt
Source value:
MyProject.Web.ViewModels.AttendeeViewModel
Any ideas on how i can resolve this?
If you want to map onto an existing object you need to use the overload that takes the existing destination:
Mapper.Map<Source, Destination>(source, destination);
that should do the trick.
Have you tried removing the ".ForMember" section and just let AutoMapper ignore it? In order to help you any more it would be helpful to see the two models for comparison.
Update: after lookind at your models I would suggest the following should solve the issue you are having...
Mapper.CreateMap <attendeeviewmodel, attendee>.ForMember (x => x.CreatedAt, opt => opt.MapFrom (src => datetime.utcnow));
I have two models, a code model and a tag model which are linked by a many to many relationship. I am trying to add a code entry that includes a possible selection of many tags using a view model (using check boxes for the tags in my view). I am getting the error:
The model item passed into the dictionary is of type 'System.Collections.Generic.List'1[StoRed.Models.Code]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[StoRed.Models.CodeTagViewModel]'.
It feels like I need to somehow convert my data to the acceptable format before trying to save it into the table but I'm new to MVC and I am having trouble finding any useful information on the internet about my specific problem. Any help would be greatly appreciated.
The code model
public class Code
{
[Key]
public int CodeID { get; set; }
[Required]
[StringLength(30)]
public string Title { get; set; }
[Required]
[StringLength(150)]
public string Description { get; set; }
public DateTime DateAdded { get; set; }
public DateTime LastUpdated { get; set; }
[Required]
[StringLength(30)]
public string Project { get; set; }
[Required]
[StringLength(30)]
public string CMS { get; set; }
public int DotNetVersion { get; set; }
[Required]
[StringLength(150)]
public string Dependencies { get; set; }
[StringLength(30)]
public string Author { get; set; }
public string CodeFile { get; set; }
[Required]
[StringLength(100)]
public string TFSLocation { get; set; }
////Creates a relationship in the DB with Tag
//[ForeignKey("TagID")]
public virtual ICollection<Tag> Tags { get; set; }
////Purely for API
//[Required]
public int TagID { get; set; }
}
The Tag model
public class Tag
{
[Key]
public int TagID { get; set; }
[Required]
[StringLength(30)]
public string TagName { get; set; }
public virtual ICollection<Code> Code { get; set; }
}
The context
public class Context : DbContext
{
public DbSet<Code> Code { get; set; }
public DbSet<Tag> Tags { get; set; }
}
The view model
public class CodeTagViewModel
{
public Tag Tag { get; set; }
public Tag TagID { get; set; }
public List<Tag> Tags { get; set; }
public int CodeID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime DateAdded { get; set; }
public DateTime LastUpdated { get; set; }
public string Project { get; set; }
public string CMS { get; set; }
public int DotNetVersion { get; set; }
public string Dependencies { get; set; }
public string Author { get; set; }
public string CodeFile { get; set; }
public string TFSLocation { get; set; }
}
Relevant part of the code controller
[HttpPost]
public ActionResult Create(CodeTagViewModel codeTagViewModel)
{
if (ModelState.IsValid)
{
Code code = new Code();
Tag tag = new Tag();
var codeTag = new CodeTagViewModel();
code.Title = codeTagViewModel.Title;
code.Description = codeTagViewModel.Description;
code.DateAdded = codeTagViewModel.DateAdded;
code.LastUpdated = codeTagViewModel.LastUpdated;
code.Project = codeTagViewModel.Project;
code.CMS = codeTagViewModel.CMS;
code.DotNetVersion = codeTagViewModel.DotNetVersion;
code.Dependencies = codeTagViewModel.Dependencies;
code.Author = codeTagViewModel.Author;
code.CodeFile = codeTagViewModel.CodeFile;
code.TFSLocation = codeTagViewModel.TFSLocation;
code.Tags = codeTagViewModel.Tags;
db.Code.Add(code);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(codeTagViewModel);
}
Your best bet is to create some kind of provider/manager/service/factory/handler - choose a name that makes most sense in terms of the job it is doing within the flow of data through your system - that is responsible for taking the ViewModel and mapping the properties of the ViewModel into an instance of the domain model before persisting the domain model to the data store, either itself or by passing the hydrated domain model to a repository layer. You can either do this manually or by using something like AutoMapper. Here's a quick manual example:
Create a CommandHandlers folder in your web project with the interface and dependant handler:
public interface ICodeCommandHandler
{
int Save(CodeTagViewModel input);
}
public class CodeCommandHandler : ICodeCommandHandler
{
private IRepository<Code> repository;
public CodeCommandHandler(IRepository<Code> repository)
{
this.repository = repository;
}
public int Save(CodeTagViewModel input)
{
Code code = new Code();
Tag tag = new Tag();
code.Title = input.Title;
code.Description = input.Description;
code.DateAdded = input.DateAdded;
code.LastUpdated = input.LastUpdated;
code.Project = input.Project;
code.CMS = input.CMS;
code.DotNetVersion = input.DotNetVersion;
code.Dependencies = input.Dependencies;
code.Author = input.Author;
code.CodeFile = input.CodeFile;
code.TFSLocation = input.TFSLocation;
code.Tags.Add(tag);
return repository.Save(code);
}
}
Then in your controller, inject the ICodeCommandHandler in via constructor injection, the same as you do with the repository in the CodeCommandHandler:
private readonly ICodeCommandHandler commandHandler;
public CodeController(ICodeCommandHandler commandHandler)
{
this.commandHandler = commandHandler;
}
[HttpPost]
public ActionResult Create(CodeTagViewModel codeTagViewModel)
{
if (!ModelState.IsValid)
{
return View(codeTagViewModel);
}
var id = codeCommandHandler.Save(codeTagViewModel);
// maybe do something useful with the document id after save
return RedirectToAction("Index");
}
To keep the Repository nice and simple, here's how that could look:
public interface IRepository<T>
{
int Save(T entity);
}
public class CodeRepository : IRepository<Code>
{
public int Save(Code entity)
{
using (var context = new Context())
{
context.Code.Add(entity);
context.SaveChanges();
}
}
}
I've not gone into detail about the dependency injection side of things as that wasn't part of the question but this should give you an idea of where to start