Automapper flatten using createmap - asp.net-mvc

I have multiple classes that I need to map into 1 class:
This is the source that I'm mapping from(view model):
public class UserBM
{
public int UserId { get; set; }
public string Address { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string State { get; set; }
public int CountryId { get; set; }
public string Country { get; set; }
}
This is how the destination class is(domain model):
public abstract class User
{
public int UserId { get; set; }
public virtual Location Location { get; set; }
public virtual int? LocationId { get; set; }
}
public class Location
{
public int LocationId { get; set; }
public string Address { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string State { get; set; }
public virtual int CountryId { get; set; }
public virtual Country Country { get; set; }
}
This is how my automapper create map currently looks:
Mapper.CreateMap<UserBM, User>();
Based on the documents on automapper codeplex site, this should be automatic but it doesn't work. Address, Address2, etc is still null. What should my createmap look like?

The reason is because AutoMapper can't map all those flat fields to a Location object by convention.
You'll need a custom resolver.
Mapper.CreateMap<UserBM, User>()
.ForMember(dest => dest.Location, opt => opt.ResolveUsing<LocationResolver>());
public class LocationResolver : ValueResolver<UserBM,Location>
{
protected override Location ResolveCore(UserBMsource)
{
// construct your object here.
}
}
However, i dont like this. IMO, a better way would be to encapsulate those properties in your ViewModel into a nested viewmodel:
public class UserBM
{
public int UserId { get; set; }
public LocationViewModel Location { get; set; }
}
Then all you have to do is define an additional map:
Mapper.CreateMap<User, UserBM>();
Mapper.CreateMap<LocationViewModel,Location>();
Then it will all work.
You should try and make use of AutoMapper conventions, where possible. And it's certainly possible to make your ViewModel more hierachical, to match the destinations hierachy.

EDIT strange this question has been asked by you.. seems to be a same question - i guess i am missing something...
Check this SQ Question
*
Define two mappings, both mapping from the same source to different
destinations
*

I think you need to make the property names similar to LocationAddress and LocationAddress2 on UserBM for their automatic projection to work, but I may be wrong.
Check out their page on Flattening they have property names that have both property names of the source concatenated like I indicated.

Simply follow the naming convention in your target class and prefix the address properties with Location since that's the property name in the source class:
public class UserBM
{
public int UserId { get; set; }
public string LocationAddress { get; set; }
public string LocationAddress2 { get; set; }
public string LocationAddress3 { get; set; }
public string LocationState { get; set; }
public int CountryId { get; set; }
public string Country { get; set; }
}

Related

Flatten Domain Class To ViewModel When Source Is Complex

I am using ValueInjecter to map domain classes to my view models. My domain classes are complex. To borrow an example from this question:
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
}
public class Address
{
public int Id { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
// VIEW MODEL
public class PersonViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int PersonId { get; set; }
public int AddressId { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
I have looked at FlatLoopInjection, but it expects the view model classes to be prefixed with nested domain model type like so:
public class PersonViewModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Id { get; set; }
public int AddressId { get; set; }
public string AddressCity { get; set; }
public string AddressState { get; set; }
public string AddressZip { get; set; }
}
The OP in the linked question altered his view models to match the convention expected by FlatLoopInjection. I don't want to do that.
How can I map my domain model to the original unprefixed view model? I suspect that I need to override FlatLoopInjection to remove the prefix, but I am not sure where to do this. I have looked at the source for FlatLoopInjection but I am unsure if I need to alter the Match method or the SetValue method.
you don't need flattening, add the map first:
Mapper.AddMap<Person, PersonViewModel>(src =>
{
var res = new PersonViewModel();
res.InjectFrom(src); // maps properties with same name and type
res.InjectFrom(src.Address);
return res;
});
and after that you can call:
var vm = Mapper.Map<PersonViewModel>(person);

CypherTypeException: Properties containing arrays of non-primitive types are not supported

I am getting this exception while creating nodes in NEO4J 2.0.3 using C# Client for NEO4J.
My Node Structure is like this
namespace UserGraph.BusinessObjects
{
public class UserInfo
{
public int UserID { get; set; }
public string UserName { get; set; }
public int HeadendId { get; set; }
public int Score { get; set; }
public string ThirdPartyObjID { get; set; }
public long ThirdPartyTypeId { get; set; }
public string[] ThirdPartyFriendsIds { get; set; }
public List<Programme> Programs { get; set; }
public List<Channel> Channels { get; set; }
}
public class Channel
{
public long ChannelID { get; set; }
public String ChannelName { get; set; }
}
public class Programme
{
public long ProgrammeID { get; set; }
public String ProgrammeName { get; set; }
}
}
I think Neo4j nodes don't hold Complex data. I searched and found on this link http://docs.neo4j.org/chunked/stable/graphdb-neo4j-properties.html
Can any one tell me is there any way by which i can store the list of channel and program objects in my UserInfo class.
I am running into the same issue and I am not sure I agree with the concept that all complex "properties" should be relationships. At the end of the day, the entire "node" should be something that can be serialized and stored -- this is one of the advantages of using JSON to serialize/deserialize the node. Coder the following
public class Address {
public string Line1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
public class User {
public string Name { get; set; }
public string email { get; set; }
public Address HomeAddress { get; set; }
public Address WorkAddress { get; set; }
}
While create nodes for Addresses can be done, I would ask the question -- why should the developer be forced to create nodes and relationships for something like this. If I never intend to do any direct queries on the properties of the Address class independently of the main user, then it is far simpler to treat the entire user as a complex object and store it in its entirety as the data for the node in the graph.

Automapper failing on double? property

I'm using AutoMapper to map Domain objects to ViewModel objects in my controller. Everything was working fine until I tried adding double? properties. I've started getting the following error:
Missing type map configuration or unsupported mapping.
Mapping types:
Address -> AddressModel
Domain.Address -> Web.Models.AddressModel
Destination path:
AccountAddressModel.Address.Address
Source value:
Domain.Address
My Address class and AddressModel class both have two properties called Longitude and Latitude. These properties (in both classes) are defined as double?. If I comment out these properties, everything works fine. If I make all of these properties simply double, then everything works fine. It's only double? that causes the problem.
I'm using AutoMapper 2.2.1 downloaded via NuGet.
I've read in other posts that this problem with nullables was supposed to be fixed. This leads me to believe I might be doing something different so I'm going to post my code to see if anyone can see something that might be an issue:
DOMAIN MODELS
public class AccountAddress
{
public int Id { get; set; }
public int AccountId { get; set; }
public int AddressId { get; set; }
public Address Address { get; set; }
...
}
public class Address : IUserTrackingEntity
{
public int Id { get; set; }
public string Addressee { get; set; }
public string Street1 { get; set; }
public string Street2 { get; set; }
...
public double? Longitude { get; set; }
public double? Latitutde { get; set; }
}
VIEW MODELS
public class AccountAddressEditModel
{
public string AccountName { get; set; }
public AccountAddressModel Address { get; set; }
public IList<Country> CountriesList { get; set; }
...
}
public class AccountAddressModel
{
public AccountAddressModel()
{
Address = new AddressModel();
}
public int AccountId { get; set; }
public int AddressId { get; set; }
public int Id { get; set; }
public AddressModel Address { get; set; }
}
public class AddressModel
{
public int Id { get; set; }
public string Addressee { get; set; }
[Required(ErrorMessage="A street address is required.")]
public string Street1 { get; set; }
public string Street2 { get; set; }
...
public double? Longitude { get; set; }
public double? Latitude { get; set; }
}
MAPPING CODE IN MY CONTROLLER
//Get an AccountAddress
address = _context.AccountAddresses.SingleOrDefault(ad => ad.Id == 12345);
model = new AccountAddressEditModel();
Mapper.CreateMap<AccountAddress, AccountAddressModel>();
Mapper.CreateMap<AccountAddress, AddressModel>();
Mapper.Map(address, model.AccountAddress);
Has anyone else experienced this or found a solution for this?
The line
Mapper.CreateMap<AccountAddress, AddressModel>();
should be changed to
Mapper.CreateMap<Address, AddressModel>();
This because you are mapping the Address class to the AddressModel class.

asp.net MVC 4 EntityType: EntitySet has no keys defined

I am a MVC newbie so go easy on me please.
I am getting two errors when I try to add a migration. They are as follows:
EntityType 'Icon' has no key defined. Define the key for this EntityType.
EntityType: EntitySet 'Icons' is based on type 'Icon' that has no keys defined.
I am including the Icon inside another model, like so:
public class Icon
{
public string IconName { get; set; }
public string IconColor { get; set; }
public int BackgroundXPos { get; set; }
public int BackgroundYPos { get; set; }
public string IconColorHover { get; set; }
public int BackgroundHoverXPos { get; set; }
public int BackgroundHoverYPos { get; set; }
}
public class GalleryThumbnail : CSSBoxModel
{
[DisplayName("Thumbnail Image Outline Color")]
public string ThumbnailImageOutlineColor { get; set; }
[DisplayName("Thumbnail Menu Font")]
public CSSFont ThumbnailMenuFont { get; set; }
[DisplayName("Thumbnail Icon Color")]
public Icon ThumbnailIconColor { get; set; }
}
How is this Address class below any different which is working:
public class Address
{
public String Adress1 { get; set; }
public String Adress2 { get; set; }
public String Adress3 { get; set; }
public String City { get; set; }
public String County { get; set; }
public String State { get; set; }
public String Zip { get; set; }
public String Country { get; set; }
}
[Table("UserProfile")] //Could be PP empolyee, Subscriber or Subscriber's customer
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public bool? Gender { get; set; }
public Address Address { get; set; } //billing address
public Address ShipAddress { get; set; }
}
I did not add a key in either my Icon or Address class because I have no intention of storing specific data in my DB. They are merely to be used inside other classes. So wy is one neededing an ID and the other is not?
I have not created public DbSet Icons { get; set; } in my DB Context either.
Also can you tell me what it is called when you use a class inside another ( or instance of class inside a class as in these examples ) ?
Much appreciated!
Since the address entity has no key defined it the Entity Framework assumes it's a complex property, and your UserProfile table will be rendered with columns named Addres_Address1, Address_Address2, Address_Address3, Address_City, and so on...
Even though you haven't declared an EntitySetIcons DbSet on your context class, it's still being added implicitly because one of your other classes somewhere has an ICollection or IEnumerable property defined.
More info on Code Conventions here:
http://msdn.microsoft.com/en-us/data/jj679962.aspx
So, either decorate the collections as NotMapped like #Kamyar said or simply remove the references from any class already declared as a DbSet.
you can use [NotMapped] attribute in System.ComponentModel.DataAnnotations.Schema namespace in EntityFramework.dll:
using System.ComponentModel.DataAnnotations.Schema;
...
[NotMapped]
public Address Address { get; set; } //billing address
[NotMapped]
public Address ShipAddress { get; set; }
Regarding the naming, AFAIK these are called public properties as well.

EF4 - Can a POCO be used as both an Entity and ComplexType?

I am using EF4 CTP5. Here are my POCOs:
public class Address
{
public int Id { get; set; }
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
}
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public List<Address> Addresses { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public decimal Total { get; set; }
public Address ShippingAddress { get; set; }
public Address BillingAddress { get; set; }
}
Is there a way to get Address to be a ComplexType for the Order class? After playing around with this, I'm guessing not, but maybe there's a way I haven't seen.
EDIT: In response to Shawn below, I gave it my best shot:
//modelBuilder.Entity<Order>().Ignore(o => o.BillingAddress);
//modelBuilder.Entity<Order>().Ignore(o => o.ShippingAddress);
modelBuilder.Entity<Order>()
.Property(o => o.BillingAddress.City).HasColumnName("BillingCity");
Fails at runtime with error "The configured property 'BillingAddress' is not a declared property on the entity 'Order'." Trying to use Ignore() doesn't work. Next, the Hanselman article is CTP4, but the CTP5 equivalent is:
modelBuilder.Entity<Order>().Map(mapconfig =>
{
mapconfig.Properties(o => new {
o.Id
, o.Total
, o.BillingAddress.City
});
mapconfig.ToTable("Orders");
});
Fails with error "Property 'BillingAddress.City' of type 'Order' cannot be included in its mapping."
I give up. Maybe the final release will have something like this. Or maybe I need to switch to NHibernate =)
All you need to do is to place ComplexTypeAttribute on Address class:
[ComplexType]
public class Address
{
public int Id { get; set; }
public string Name { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string PostalCode { get; set; }
}
Alternatively, you can achieve this by fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ComplexType<Address>();
}
But you cannot have Address type as to be both an Entity and a Complex Type, it's one way or another.
Take a look at this blog post where I discuss this at length:
Associations in EF Code First CTP5: Part 1 – Complex Types
If you want Address to be in the same table as Order, you're going to have to tell EF that in the DbContext OnModelCreating override.
Take a look here: http://weblogs.asp.net/scottgu/archive/2010/07/23/entity-framework-4-code-first-custom-database-schema-mapping.aspx

Resources