MVC2 ViewModel Binding - c#

How do I get properties in my BLL passed down to a ModeView. For example, I have this class in a separate Class Library:
[MetadataType(typeof(PersonMetaData))]
public partial class Person
{
[Bind(Include = "PersonId,DepartmentId,FirstName,LastName,Active,DateAdded,DateDeleted")]
public class PersonMetaData
{
public object PersonId { get; set; }
public object DepartmentId { get; set; }
public object FirstName { get; set; }
public object LastName { get; set; }
public Department PersonDepartment { get; set; }
public string FullName()
{
return string.Format("{0} {1}", FirstName, LastName);
}
}
}
My ViewModel looks like this:
public class PersonViewModel
{
public int PersonId { get; set; }
public string FullName{ get; set; }
public string PersonDepartment { get; set; }
}
When I generate a new "View" strongly-typed to the PersonViewModel and set as "List" View Content....the page is generated, but FullName is not coming through.
I created the PersonDepartment property because I want to display the Department Name the person is in. I have a Department Class set up similarly. For example, I want to be able to do something like "PersonDepartment.DepartmentName" that displays the department name on the page.
I am using a DBML (Linq To SQL), so the partial classes are extending from the auto-generated classes.
I am not sure how to get FullName property filled and passed to ViewModel and get Department properties connected to the Person information being passed. Any help would be greatly appreciated.

You have mentioned that you are using AutoMapper. In your model FullName is a method and not a property. AutoMapper won't map automatically to methods. According to the conventions you could prefix your method name with Get to make this to work:
public string GetFullName()
{
return string.Format("{0} {1}", FirstName, LastName);
}
This will be mapped to the FullName property in the view model. Another option is to explicitly declare how the mapping is done:
Mapper.CreateMap<Person, PersonViewModel>()
.ForMember(
dest => dest.FullName,
opt => opt.MapFrom(src => src.FullName())
);
As far as the department name property is concerned I would recommend you modify your model so that instead of a DepartmentId property it has directly a property called Department containing the id and the name which will allow you to map them easily in your view model. If you cannot modify your model this way you could have the Department property in the view model populated directly by the repository and not by AutoMapper.

Related

Should I implement my foreign key as an integer property or object reference in my model? (MVC)

Consider the following code snippet from my Student model:
public class Student
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public int Teacher { get; set; }
}
The 'Teacher' property is a foreign key in my DB. I want to know if the above implementation of that attribute is correct (as I use repositories to link the relevant student to the teacher) or should I be using the teacher object
public Teacher Teacher{get;set;}
to do this? In what circumstances would you want to use either of the two implementations?
It depends, you can use only class or you can use both(class and primitive). It also depends which ORM you are using. If you are using EntityFramework then
If you use only object then when you insert new Student and you want
assign teacher you must have an object.
If you use both then when you create new Student you can set
teacherId so you will "save one select"

Automapper map Viewmodel to Model with same name

Viewmodel
public string Personal_Data_Surname { get; set; }
public string FamilyMember_SurName { get; set; }
Entity class Applicant
public string SurName { get; set; }
Entity class FamilyMember
public string SurName { get; set; }
Automapper Config
Mapper.Configuration.RecognizePrefixes("Personal_Data_");
Mapper.CreateMap<ApplicationViewModel, Applicant>();
Mapper.Configuration.RecognizePrefixes("FamilyMember_");
Mapper.CreateMap<ApplicationViewModel, FamilyMember>();
Controller code mapping entities
Applicant applicant = Mapper.Map<ApplicationViewModel, Applicant>(vaModel);
FamilyMember familyMember = Mapper.Map<ApplicationViewModel, FamilyMember>(vaModel);
The problem is that it maps Personal_Data_Surname from the viewmodel to the Surname in entity class Applicant and FamilyMember. Is it possible to specify a prefix for a specific type
You have to customize your mapping using ForMemeber method, with the MapFrom option method, for sample:
Mapper.CreateMap<ApplicationViewModel, Applicant>()
.ForMember(viewModel => viewModel.Personal_Data_Surname,
opt => opt.MapFrom(entity => entity.SurName));
Then, AutoMapper will map the Personal_Data_Surname viewModel's property to SurName entity's property. Do it to another entities.

Exclude Fields From Model Validation

Let's say I have a following ViewModel :
public class PersonViewModel
{
[Required]
public String Email { get; set; }
[Required]
public String FirstName { get; set; }
[Required]
public String LastName { get; set; }
}
This is a ViewModel not a original Entity, I use this model in two places, in the first one I want to validate all fields, but in another one I want to exclude Email field from model validation. Is there anyway to specify to exclude field(s) from validation?
You can use
ModelState.Remove("Email");
to remove entries in model state, that are related to hidden fields.
The best solution is to divide view model into two:
public class PersonViewModel
{
[Required]
public String FirstName { get; set; }
[Required]
public String LastName { get; set; }
}
public class PersonWithEmailViewModel : PersonViewModel
{
[Required]
public String Email { get; set; }
}
An ugly solution:
ModelState.Remove("Email");
Recommended solution:
Create another ViewModel. A VM is supposed to represent your view, so if your view has no Email field, make a suitable VM for it.

AutoMapper and mapping one-to-many on Create

So my DB has a one-to-many association between a customer and orders.
Mapping the data to show a customer and his orders is no problem. But is there a way to map these when creating a customer?
For example:
"The very basic viewModel just to test the Mapping"
public class CVM
{
public string ContactName { get; set; } //Part of the Customer Table
public DateTime OrderDate { get; set; } //Part of the Orders Table and
//would have to be passed into the Orders List of the EF-Customer-Object
}
So the create view just has 2 inputs for Name and Date.
"The very basic controller just to test the Mapping ;)"
[HttpPost]
public ActionResult Create(CVM model)
{
Mapper.CreateMap<CVM, Customer>();
Customer customer = Mapper.Map<CVM, Customer>(model);
return View();
}
So ContactName gets mapped properly. The Problem is the OrderDate. AutoMapper would have to create an Order instance, set the value of OrderDate and pass it to the OrdersCollection of the Customer object. Is AutoMapper able to do this or am I totally wrong?
Hope you understand my explanation and someone has an Answer to me.
Thanks Folks
I think you are going about it the wrong way. What you should be doing is to instantiate a Customer instance and then map it s properties using AutoMapper.
Thus, your code would look like:
[HttpPost]
public ActionResult Create(CVM model)
{
Mapper.CreateMap<CVM, Customer>();
Customer customer = /* Construct or get a Customer instance, eg from DB. */
Mapper.Map<CVM, Customer>(model, customer);
return View();
}
BTW, you should make sure to have Mapper.CreateMap<CVM, Customer>() directives only during application startup, otherwise you are needlessly performing this (possibly costly) step on every request.
Edit
It seems I read the original question wrong. If the aim is to create a Customer with associated objects, then Automapper can help you in a few different ways (I am going forward with the Person/PhoneNumber example you gave in the comments).
Given that your Entities and View Models are:
public Person {
public string Name { get; set; }
public string List<PhoneNumber> Numbers { get; set; }
}
public PersonVM {
public string Name { get; set; }
public string IList<PhoneNumberVM> Numbers { get; set; }
}
public PhoneNumber {
public int Type { get; set; }
public string Number { get; set; }
}
public PhoneNumberVM {
public int Type { get; set; }
public string Number { get; set; }
}
then you have a few alternatives:
You can try to write a custom Mapping rule so that each PhoneNumberVM instance is mapped to a PhoneNumber instance, or
You can add a Mapper.CreateMap<PhoneNumberVM, PhoneNumber>() and just call Mapper.Map<PersonVM, Person>(model) to have your model mapped to your entity.
Of course, you would have to make sure that your model gets constructed properly, but that is not very hard as long as you use the same model to generate your HTML form.

If you create a DomainService, exposing an entity, can you access aggregate entities?

Say you create an RIA DomainService and you include a Person (shown below) entity in it, can you access aggregate entities on that object?
For instance, if I have entities like so (keep in mind that this is a naive representation, they are modeled via the EF4 designer):
public class Person
{
string FirstName { get; set; }
PhoneNumber { get; set; }
}
public class PhoneNumber
{
public string AreaCode { get; set; }
public string Trunk { get; set; }
public string Number { get; set; }
}
If I include Person when creating the PeopleDomainService, can the client access the PhoneNumber on it (and modify it)?
You can decorate the PhoneNumber attribute of the Person object with the [Include] attribute. Remember also to include an include statement in your LINQ query when you get a Person object.
Yes, you can bring in related entities.
In the PeopleDomainService.metadata.cs file, look for the PersonMetadata class. On the PhoneNumbers property, add the "Include" attribute:
[Include]:
public EntityCollection<PhoneNumber> PhoneNumbers { get; set; }
In the PeopleDomainService.cs, look for the GetPersons function and modify it to include the PhoneNumbers:
public IQueryable<Person> GetPersons()
{
return this.ObjectContext.Persons.Include("PhoneNumbers");
}
You can find more details on MSDN > Walkthrough: Taking a Tour of RIA Services > Displaying Related Data

Categories