Automapper map Viewmodel to Model with same name - c#

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.

Related

What is the correct way to get an Entity with it's navigation property in Entity Framework Core?

How I get an Entity with it's navigation property in Entity Framework Core? I have seen in docs.microsoft that I have to use .Include() and it's working in a project but this time it's not working.
Model.cs
public class UniversityModel
{
public int ID { get; set; }
public string Name { get; set; }
public string LongDescription { get; set; }
public string Address { get; set; }
public List<UniSession> Sessions { get; set; }
public DateTime Date { get; set; }
public List<UniEmail> Emails { get; set; }
public List<UniPhone> Phones { get; set; }
}
And I'm accessing the UniversityModel with it's navigation property like.
UniversityModel university = await _context.Universities
.Include(u => u.Phones)
.Include(u => u.Emails)
.Include(u => u.Sessions)
.SingleOrDefaultAsync(u => u.ID == id);
It's getting the university correctly but Navigation properties are not including.
For make it clear look at the Model below all the navigation property models are same with foreign key of university.
public class UniEmail
{
public int ID { get; set; }
public int UniversityId { get; set; }
[StringLength(50)]
public string Email { get; set; }
}
What is the wrong how correctly I can Include all the navigation properties and If my Including code is wrong then how it's worked in another project?
The problem is that your entity model does not follow EF Core conventions and you don't use fluent configuration. Although EF discovers the relationship through collection navigation property, since there is no inverse reference navigation property and UniversityId field is not identified as FK, EF maps FK to a shadow property called UniversityModelId.
EF Core FK naming conventions are explained in the Fully Defined Relationships section of the documentation:
If the dependent entity contains a property named <primary key property name>, <navigation property name><primary key property name>, or <principal entity name><primary key property name> then it will be configured as the foreign key.
In other words, UniversityId will be considered conventionally as FK if:
(1) UniversityModel class is called University
(2) ID property of UniversityModel class is called UniversityID (and tagged with [Key] attribute because it would not match the PK convention)
(3) You add inverse navigation property called University:
public UniversityModel University { get; set; }
public int UniversityId { get; set; }
And of course it can be mapped explicitly with fluent API:
modelBuilder.Entity<UniversityModel>()
.HasMany(e => e.Emails) // with collection navigation property
.WithOne() // and no reference navigation property
.HasForeignKey(e => e.UniversityId); // and foreign key

Is it possible to tell automapper to ignore mapping at runtime?

I'm using Entity Framework 6 and Automapper to map entities to dtos.
I have this models
public class PersonDto
{
public int Id { get; set; }
public string Name { get; set; }
public AddressDto Address { get; set; }
}
public class AddressDto
{
public int Id { get; set; }
public string Street { get; set; }
public string City { get; set; }
}
I use automapper Queryable Extension to map dto from entity.
var personDto = dbContext.People.Project().To<PersonDto>();
The problem with above method is that it will make EF to always load the address entity. I want address to be loaded only if i explicitly tell them to with include(x => x.Address). If i specify ignore() in automapper map, the address will not be loaded. Is it possible to tell automapper to ignore the address property at runtime? Automapper queryable extensions i'm using doesn't support all functionalities like "Condition or after map". Is there any workaround for this?
You need to enable explicit expansion for your DTOs. First in your configuration:
Mapper.CreateMap<Person, PersonDto>()
.ForMember(d => d.Address, opt => opt.ExplicitExpansion());
Then at runtime:
dbContext.People.Project.To<PersonDto>(membersToExpand: d => d.Address);
The "membersToExpand" can be a list of expressions to destination members, or a dictionary of string values representing the property names to expand.

EF mapping one to one optional

I have som mapping problem with EF.
This is my classes
public class User{
public Guid Id { get; set; }
// Fullname of the user account owner
public string Name { get; set; }
public string Email { get; set; }
public string Username { get; set; }
public Player Player { get; set; }
}
public class Player
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual User User { get; set; }
}
It works fine, but now I want to create the navigation property Player and User in this classes. I have this Fluent code:
modelBuilder.Entity<User>()
.HasOptional(x => x.Player)
.WithOptionalDependent(x => x.User)
.Map(x => x.MapKey("Username"));
But I only get this error message, and I have no ide what's wrong.
Each property name in a type must be unique. Property name 'Username'
was already defined.
My DB setup looks like the classes, in the player table the Name is unique. It's not unique in the User table. A user can exist without a player and vice versa. (Actully I don't want any User property inside the Player class but I think it's a requierment?!)
I think it's complaining about the fact that UserName is already a property in the object model. See the docs for the Map() method:
From http://msdn.microsoft.com/en-us/library/system.data.entity.modelconfiguration.configuration.foreignkeynavigationpropertyconfiguration.map%28v=vs.103%29:
Configures the relationship to use foreign key property(s) that are
not exposed in the object model. The column(s) and table can be
customized by specifying a configuration action. If an empty
configuration action is specified then column name(s) will be
generated by convention. If foreign key properties are exposed in the
object model then use the HasForeignKey method. Not all relationships
support exposing foreign key properties in the object model.
Remove the modelBuilder code and mark the PrimaryKey as a ForeignKey on the dependent table. For example if Players don't exist without a User:
public class User
{
public Guid Id { get; set; }
// Fullname of the user account owner
public string Name { get; set; }
public string Email { get; set; }
public string Username { get; set; }
public Player Player { get; set; }
}
public class Player
{
[ForeignKey("User")]
public Guid Id { get; set; }
public string Name { get; set; }
public virtual User User { get; set; }
}
The ForeignKey attribute tells EF which side of the one-to-one is dependent, allowing it to map it properly.
If your columns in the database has the same name as the properties of your model you don't need to map the property ".Map(x => x.MapKey("Username"));" EF already mapped the property "Username" using the convention and is because of that the EF is complaining
With your entities
...I just like to do it the other way around:
modelBuilder.Entity<Player>()
.HasRequired(i => i.User)
.WithRequiredDependent(i => i.Player);
or this (optional):
modelBuilder.Entity<Player>()
.HasRequired(i => i.User)
.WithOptional(x => x.Player);

EF 4.1 Bidirectional one-to-one problem

Hi I am having a problem with a simple EF 4.1 code first model.
I have a class person and a class survey that are bidirectionally linked. The database model is correct but I always get this error:
Unable to determine the principal end of an association between the types 'DAL.Models.Survey' and 'DAL.Models.Person'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
Class Person
[Key]
public Guid Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public string Email { get; set; }
public virtual Survey Survey { get; set; }
Class Survey
[Key]
public Guid Id { get; set; }
public bool IsFinished { get; set; }
public virtual Person Person { get; set; }
Datacontext:
modelBuilder.Entity<Survey>().HasRequired(s => s.Person).WithOptional().WillCascadeOnDelete(true);
Can anyone help please
You should define the other navigation property in your mapping since you have it in the model. Otherwise EF will create a second (one-to-many) association:
modelBuilder.Entity<Survey>()
.HasRequired(s => s.Person)
.WithOptional(p => p.Survey)
.WillCascadeOnDelete(true);
I think you have to specify either a foreign key property through HasForeignKey or foreign key column name using Map. Something like this:
modelBuilder.Entity<Survey>()
.HasRequired(s => s.Person)
.WithOptional()
.WillCascadeOnDelete(true)
.Map(m => m.MapKey("fk_column"));

MVC2 ViewModel Binding

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.

Categories