AutoMapper not working as expected? - c#

I have a class at my Domain layer something as below:
public class Employee : AuditableEntity
{
public string FirstName { get; set; }
public string Surname { get; set; }
public double Salary{ get; set; }
public int PhoneNo { get; set; }
public double Bonus
{
get { return Salary + EmployeeAdditionals.Sum(e => e.Value); }
}
// virtual allow lazy loading
public virtual ReadOnlyCollection<EmployeeAdditional> EmployeeAdditionals { get; private set; }
// Paramterless constructor for EF
public Employee()
{
EmployeeAdditionals = new List<EmployeeAdditional>();
}
public void AddAdditionalInfo(EmployeeAdditional additionalInfo)
{
additionalInfo.Employee = this;
additionalInfo.EmployeeId = Id;
((Collection<EmployeeAdditional>) EmployeeAdditionals).Add(additionalInfo);
}
I then have the following viewModel class in my MVC 5 Application:
public class EmployeeDetailsViewModel : BaseViewModel
{
public EmployeeDetailsViewModel()
{
Employee = new Employee();
}
public Employee Employee { get; set; }
//Other fields
}
public class Employee
{
[DisplayName("Employee First Name")]
public string FirstName { get; set; }
[DisplayName("Employee Surname")]
public string Surname{ get; set; }
public double Salary{ get; set; }
[RegEx for Phone Number and Error Mesage]
public int PhoneNo{ get; set; }
public double Bonus { get; set; }
}
I have mapping then in my controllers and viewModel builder to map my viewmodel to my domain model and domain model back to view model and I am attempting to use AutoMapper for this.
Using manual mapping something like below:
// set domain from viewModel
employee.FirstName= model.Employee.FirstName;
//etc for rest of fields
// set view model from domain
viewModel.Employee.FirstName = employee.FirstName;
//etc for rest of fields
Now in my AutoMapperBootstrapper class in Setup method I have the following:
Mapper.CreateMap<Domain.Models.Employee, Employee>();
Mapper.CreateMap<Employee, Domain.Models.Employee>();
Then I am using this is following in my controller and viewmodel builder:
//set domain from viewModel
Mapper.Map(employee, model.Employee);
// set viewmodel from domain
Mapper.Map(viewModel.Employee, employee);
When I hit a breakpoint on mapping from domain to viewModel I see the model.Employee with the values from screen but when I step over this line the values do not get mapped to domain employee and then the model.Emplyee values are reset to null for the strings, 0.0 for double, etc.
Is there something incorrect with my configuration or use of automapper?

You pass wrong parameter. If you want to use existing instance, make sure first parameter is source and second parameter is destination.
Try to switch the parameter.
//set domain from viewModel (viewModel is the source)
Mapper.Map(model.Employee, employee);
^^^^^ ^^^^^
source destination
// set viewmodel from domain (domain is the source)
Mapper.Map(employee, viewModel.Employee);
^^^^^ ^^^^^
source destination

Shouldn't your map code actually be like this? You have to use instances for Map(), it looks like you are using Types
//set domain from viewModel
Mapper.Map(viewEmployee, new model.Employee());
// set viewmodel from domain
Mapper.Map( employee, new viewmodel.Employee());
This is how I would expect you to use it:
var viewModel = Mapper.Map<ViewModel.Employee>(domainModel); <-- this creates a ViewModel for a Domain Model
and the reverse would be:
var domainModel = Mapper.Map<Domain.Employee>(viewModel);

Related

Mapster - How to do ignore mapping for null properties

I'm using Mapster to map Dto instances to Model objects.
The Dtos are sent by a Javascript client, sending only the properties updated.
I would like to ignore null values, and have Mapster leave the model instance unchanged for this properties.
A simplified example to better explain the scenario:
// My .Net Dto class, used for client/server communication.
public class PersonDto
{
public string Id { get; set; }
public string Name { get; set; }
public string Family { get; set; }
}
// My Model class. Let's assume is the same data as per the Dto.
public class Person
{
public string Id { get; set; }
public string Name { get; set; }
public string Family { get; set; }
}
public void Update()
{
var existingPerson = new Person
{
Id = "A",
Name = "Ned",
Family = "Stark"
};
var patchDataSentFromClient = new PersonDto
{
Id = "A",
Name = "Rob"
};
patchDataSentFromClient.Adapt(existingPerson);
// Here existingPerson.Family should be "Stark", but it gets emptied out.
// the mapping should be equivalent to:
// if (patchDataSentFromClient.Family != null) existingPerson.Family = patchDataSentFromClient.Family;
}
Edit: the point is I don't want to write down the mapping condition for each of the thousands of properties in my Dtos. I want Mapster to Automap all string properties by name, but keep the "patch-like" logic of ignoring null values.
You can use IgnoreNullValues.

How can I use Automapper to assign values to fields in destination model but not in source model?

I am getting the hang of Automapper in an ASP.NET MVC 5 application for the purpose of mapping a domain model to a ViewModel. There is a case that I still don't know how to resolve: when the ViewModel (destination) has a property not in the domain model (source).
The two additional properties in the ViewModel are IEnumerables that I need to populate in the Controller.
As I explain in the comments in the Controller block (shown below), the domain model is the source and will be fed into the View table. The additional two IEnumerables in the ViewModel will fill the DropDownLists in the HTML.BeginForm() block.
The examples I have seen using .CreateMap<>().ForMember() deal with calculations or transformations of properties in the source model, and not this case, where I am defining something in the controller based on the Action parameters.
My question is how to map the remaining IEnumerables, as defined in the controller?
Mapping Config in App_Start
public static class MappingConfig
{
public static void RegisterMaps()
{
AutoMapper.Mapper.Initialize(config =>
{
config.CreateMap<StudentRoster, StudentRosterViewModel>();
});
}
}
Model and ViewModel:
[Table("StudentRoster")]
public partial class StudentRoster
{
public int ID { get; set; }
[Required]
[StringLength(3)]
public string Campus { get; set; }
[Required]
[StringLength(4)]
public string FiscalYear { get; set; }
[Required]
[StringLength(50)]
public string StudentName { get; set; }
public int StudentID { get; set; }
}
// ViewModel
public partial class StudentRosterViewModel
{
// Automapper successfully mappped the first five fields
// to the parent class
public int ID { get; set; }
public string Campus { get; set; }
public string FiscalYear { get; set; }
public string StudentName { get; set; }
public int StudentID { get; set; }
// These two fields are not in the parent class
public IEnumerable<SelectListItem> CampusListSelect { get; set; }
public IEnumerable<SelectListItem> FiscalYearSelect { get; set; }
}
Index Action in Controller:
// GET: StudentRosterViewModels
public ActionResult Index(string campus = "MRA", string fy="FY16")
{
IEnumerable<StudentRoster> query = db.StudentRosters.Where(m=>m.Campus==campus).ToList();
// This successfully maps the domain model to the viewmodel
// This IEnumerable will display in the "Table"
IEnumerable<StudentRosterViewModel> mappedQuery =
AutoMapper.Mapper.Map<IEnumerable<StudentRoster>, IEnumerable<StudentRosterViewModel>>(query);
// The two remaining IEnumerables need to be mapped to 'mappedQuery'
// CampusListSelect and FiscalYearSelect
// These two IEnumerables will populate the dropdownlists in Html.BeginForm()
IEnumerable<SelectListItem> CampusList = new SelectList(new List<string> { "CRA", "DRA", "MRA", "PRA" }, campus);
IEnumerable<SelectListItem> FiscalYearList = new SelectList(new List<string> { "FY12", "FY13", "FY14", "FY15", "FY16" }, fy);
return View(mappedQuery.ToList());
}
You can try to use DynamicMap to populate your items without creating additional classes for mapping.
In case if you're using the old version of AutoMapper (4.1 or below) the you can try something the following:
// GET: StudentRosterViewModels
public ActionResult Index(string campus = "MRA", string fy="FY16")
{
IEnumerable<StudentRoster> query = db.StudentRosters.Where(m=>m.Campus==campus).ToList();
// This successfully maps the domain model to the viewmodel
// This IEnumerable will display in the "Table"
IEnumerable<StudentRosterViewModel> mappedQuery =
AutoMapper.Mapper.Map<IEnumerable<StudentRoster>, IEnumerable<StudentRosterViewModel>>(query);
// The two remaining IEnumerables need to be mapped to 'mappedQuery'
// CampusListSelect and FiscalYearSelect
// These two IEnumerables will populate the dropdownlists in Html.BeginForm()
IEnumerable<SelectListItem> CampusList = new SelectList(new List<string> { "CRA", "DRA", "MRA", "PRA" }, campus);
IEnumerable<SelectListItem> FiscalYearList = new SelectList(new List<string> { "FY12", "FY13", "FY14", "FY15", "FY16" }, fy);
var objForDynamicMapping = new
{
CampusListSelect = CampusList,
FiscalYearListSelect = FiscalYearList
};
foreach(var mappedItem in mappedQuery)
{
// will create the mapping configuration dynamically
AutoMapper.Mapper.DynamicMap(objForDynamicMapping, mappedItem);
}
return View(mappedQuery.ToList());
}
In case if you're using the AutoMapper 4.2 or high.
Then you just need to put this row:
var config = new MapperConfiguration(cfg => cfg.CreateMissingTypeMaps = true);
in place where you create the mapper configuration and then just use method Map like:
mapper.Map(objForDynamicMapping, mappedItem);
instead of DynamicMap.
Hope it will help.

ViewModel Object Convert to Entity Framework Object

Goal: to save ViewModel object by Entity Framework. I have UserViewModel object which has list of UnitViewModel. Then, I have a UserAdapter class which converts UserViewModel into Entity Framework User object (see Convert()below how).
Now, my question is how do I convert this list of UnitViewModel to its corresponding Entity Framework Unit list? - Do I have to get each object from DB Context by calling something like context.Units.Where(u=>myListofUnitIDs.Contains(u.UnitID))?
public class UserViewModel
{
public Guid? UserID { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public DateTime? CreateTime { get; set; }
public List<UnitViewModel> UserUnits { get; set; }
}
public class UnitViewModel
{
public Guid UnitID { get; set; }
public string Name { get; set; }
public int? SortIndex { get; set; }
public DateTime CreateTime { get; set; }
public bool Assigned { get; set; }
}
public class UserAdapter
{
public static User Convert(UserViewModel userView)
{
User user;
if (userView.UserID.HasValue)
{
using (var provider = new CoinsDB.UsersProvider())
{
user = provider.GetUser(userView.UserID.Value);
}
}
else
{
user = new User();
}
user.FirstName = userView.FirstName;
user.LastName = user.LastName;
user.Password = StringHelper.GetSHA1(userView.Password);
user.UserName = user.UserName;
user.CreateTime = DateTime.Now;
// Problem here :)
// user.Units = userView.UserUnits;
return user;
}
}
UPDATE: The main concern here is that I have to retrieve each Unit from database to match (or map) it with ViewModel.Unit objects, right? Can I avoid it?
For your information, this operation is called as Mapping mainly. So, you want to map your view model object to the entity object.
For this, you can either use already existed 3rd party library as AutoMapper. It will map properties by reflection which have same name. Also you can add your custom logic with After method. But, this approach has some advantages and disadvantages. Being aware of these disadvantages could help you to decide whether you must use this API or not. So, I suggest you to read some articles about advantages and disadvantages of AutoMapper especially for converting entities to other models. One of such disadvantages is that it can be problem to change the name of one property in the view model in the future, and AutoMapper will not handle this anymore and you won't get any warning about this.
foreach(var item in userView.UserUnits)
{
// get the mapped instance of UnitViewModel as Unit
var userUnit = Mapper.Map<UnitViewModel, UserUnit>(item);
user.Units.Add(userUnit);
}
So, I recommend to write your custom mappers.
For example, I have created a custom library for this and it maps objects lik this:
user.Units = userView.UserUnits
.Select(userUnitViewModel => userUnitViewModel.MapTo<UserUnit>())
.ToList();
And I am implementing these mapping functions as:
public class UserUnitMapper:
IMapToNew<UnitViewModel, UserUnit>
{
public UnitViewModel Map(UserUnit source)
{
return new UnitViewModel
{
Name = source.Name,
...
};
}
}
And then in runtime, I am detecting the types of the objects which will be used during mapping, and then call the Map method. In this way, your mappers will be seperated from your action methods. But, if you want it urgently, of course you can use this:
foreach(var item in userView.UserUnits)
{
// get the mapped instance of UnitViewModel as Unit
var userUnit= new UserUnit()
{
Name = item.Name,
...
};
user.Units.Add(userUnit);
}

Model or ViewModel when representing a subset of data in MVVM?

If I have a complex model representing a large amount of data, and I only wish to display a cut-down version of that model (e.g. Name, Description), what is the best approach in MVVM?
Most solutions I can find seem to assume that the data is already present in memory and recommend using a new ViewModel that exposes only the fields required.
However rather than select out all of the data from the database, it would be preferable to select just what is necessary. Do I then create a new model to hold that data? Selecting directly into the ViewModel is possible but feels like the wrong thing to do. Likewise using a new model to represent a different version of the same data also feels off somehow.
What is the accepted method of doing this?
As a simple example (Simple enough class that I wouldn't ordinarily do this):
public class User {
public int UserID {get;set;}
public string FirstName
public string LastName
public int AccessLevelID
public List<Groups> UserGroups
}
but I only really need:
public class PreviewUser {
int UserID
string FirstName
}
You can create another type with is a subset of the business type.
usually this is known as a
DTO - Data transfer Object which encapsulates only what you need. so the database needs to query only the subset of the entity.
public class UserDto
{
public int ID { get;set;}
public string Name{ get;set;}
}
Secondly if you need to add some ui logic to the display it is common to wrap the specific DTO in a more specific UI model.
public class UserUI
{
UserDTO _userDto;
UserUI(UserDTO userDto)
{
_userDto = userDto;
}
public string Name
{
get{return IsAfter_21_hours ? "The user as gone home" : _userDto.Name;}
}
}
the UserViewModel will reference an instance of UserUI.
You can either remove properties you don't need from the model (to slightly improve performance) or you can create a viewmodel that will provide only properties that you want to show.
Here is an example:
public class UserViewModel
{
private readonly User _user;
public UserViewModel(User user)
{
_user = user;
}
public int UserID
{
get { return _user.UserID; }
}
public string FirstName
{
get { return _user.FirstName; }
}
}
...
var viewModels = userRepository.GetUsers().Select(user => new UserViewModel(user));
UPDATED:
If performance is really important for you, you can use inheritance. Base class will be smaller version of the data and derived class will contain complete data. You can use the base class when you need to get only some fields from DB and save bandwidth.
public class BaseUser
{
public int UserID { get; set; }
public string FirstName { get; set; }
}
public class User : BaseUser
{
public string LastName { get; set; }
public int AccessLevelID { get; set; }
public List<Groups> UserGroups { get; set; }
}
There are number of approaches you may use:
use "full version" of source model. Since you're building UI, the user will see only what you want to display;
use view model, and wrap source model into this view model. The implementation is trivial, and amount of data is limited before UI;
use view model, and copy data from source model into this view model. The implementation is more complex (either mapping from existing model, or loading only required data from database), but view model and model are totally decoupled.
Actually, it depends on what is more suitable for you.
Note, that often the difference between "view model" and "model" is blurred. If the model looks like this:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
...and view model - like this:
public class PersonViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
then throw this view model away. While there's no difference, you don't need to create extra classes.
I think you don't have to create new model class to hold user data for view . Instead create a view model class and map the model properties to VM. See the example below
public class UserViewModel
{
Public UserViewModel(User user)
{
//initialize required viewmodel properties here
}
int UserID {get;set;}
string FirstName{get;set;}
}

ASP.NET MVC model binding with dynamic viewmodel

What is the recommended way to handle binding a complex viewmodel? For example, using the objects below:
class PersonViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
//I have also tried a generic object instead of dynamic with the same results
public dynamic PreferredSportsTeam { get; set; }
}
class SportsTeamViewModel
{
public string Name { get; set; }
public string City { get; set; }
}
class BaseballTeamViewModel : SportsTeamViewModel
{
public double TeamERA { get; set; }
}
class HockeyTeamViewModel : SportsTeamViewModel
{
public int TotalSaves { get; set; }
}
I am trying to put either an instance of a BaseballTeamViewModel OR a HockeyTeamViewModel into the "PreferredSportsTeam" property of the PersonViewModel and then pass that PersonViewModel to the Edit View to allow the user to edit all of the fields.
I have successfully used EditorTemplates to display the corresponding View of the object stored in the "PreferredSportsTeam" property. However, when submitting the Edit Form the MVC model binder cannot reconstruct whatever object was stored in the PreferredSportsTeam. It does correctly fill the FirstName and LastName properties but the PreferredSportsTeam property always returns as a generic object that I cannot cast into anything else.
Is there something that I am missing or is it simply not possible with the default MVC model binder? I am aware of custom model binders and think they may be a solution but I am not knowledgeable enough about them to understand how they could be used to fix the problem.
Below code sample shows how to use dynamic object :
var team = new Team();
team.FirstName = "FirstName";
team.LastName = "LastName";
var baseballTeam = new BaseBallTeam();
baseballTeam.TotalSaves = 100;
team.PreferredSportsTeam = new ExpandoObject();
team.PreferredSportsTeam.BaseBallTeam = baseballTeam;
See This for more detailed explanation.

Categories