Can I use AutoMapper to map between two fields which are immutable? - c#

I have the following two classes (many properties elided for brevity).
Service Layer POCO:
public class TicketFlag
{
public ContactKey ContactKey;
}
LINQ to SQL generated POCO:
public class TicketFlag
{
public string ContactKey;
}
When trying to use AutoMapper to map between these two on service calls -> database save, I'm getting the following exception:
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
---> System.ArgumentException: Type 'ContactKey' does not have a default constructor
ContactKey does not have a default constructor on purpose. Basically, it takes a string and a list of objects and can serialize/deserialize itself.
I have tried creating a mapping function (and it's inverse) like so:
Mapper.CreateMap<string, ContactKey>().ConvertUsing(s => ContactKeySerializer.Serialize(s));
But I'm still getting complaints because ContactKey doesn't have a default constructor.
Is there a way to get AutoMapper to not use the default constructor to do it's property mapping? Really, just mapping properties on the ContactKey isn't sufficient - I need to have it call the constructor, or get spit out from my ContactKeySerializer class.

First, you should probably be using properties for these things, not fields. However, I doubt that's part of your problem.
Instead of trying to create a map from string to ContactKey, you could try to make this part of the map from one TicketFlag to the other:
Mapper.CreateMap<LINQtoSQL.TicketFlag, Service.Layer.TicketFlag>()
.ForMember(dest => dest.ContactKey,
mem => mem.ResolveUsing(src => ContactKeySerializer.Serialize(src.ContactKey)));
I think that would prevent the error you're getting.

AutoMapper is complaining that you don't have a default constructor because AutoMapper needs to create an empty instance of the target class before it can map values to it. It can't call your ContractKey's parameterized constructor - how would it?
In this case it might seem simple, if the constructor looks like this:
public ContracktKey(string keyValue){}
But what if it had two parameters?
public ContracktKey(string keyValue, string otherValue){}
How would it know where to put the value? What if you only provided one string?
I think it would be best to follow others' advice and map the two TicketFlag objects.

Related

How do I get AutoMapper to map classes whose type isn't known at compile time?

Let's say that I have a class Schedule that contains a property called Event that's a nested class. Let's say that there are many types of events, so the actual value of the property would be some subclass (or descendant) of Event. The event could be a Conference or a ReligiousCeremony or yada yada yada.
Now let's say that all of these classes are in an "old" namespace and I'm using AutoMapper to convert these into updated version in a "new" namespace. (Ideal use case for AutoMapper.) v1.Schedule maps to v2.Schedule and v1.Conference maps to v2.Conference and so on.
The issue I have is that because Schedule.Event isn't of type v1.Conference, AutoMapper isn't identifying it as an object type it can map. Specifically, my class definitions are coming from an XSD and the Schedule.Event property is of type object.
Is there a way to get AutoMapper to inspect the actual type of an object and apply the appropriate mapping?
My current work-around (which another developer is testing right now) is as follows:
var v2Schedule = mapper.Map<v2.Schedule>(v1Schedule);
if (v2Schedule.Event is v1.Conference) { v2Schedule.Event = mapper.Map<v2.Conference>(v2Schedule.Event as v1.Conference); }
if (v2Schedule.Event is v1.ReligiousCeremony) { v2Schedule.Event = mapper.Map<v2.ReligiousCeremony>(v2Schedule.Event as v1.ReligiousCeremony); }
(I know about switching on object type, but that's a c#7 feature and I'm still in c#6.)
Looking at the docs, it seems you can tell AutoMapper about the inheritance like so:
var config = new MapperConfiguration(cfg =>
{
//Other mapping
cfg.CreateMap<v1.Event, v2.Event>() //or even <object, v2.Event>
.Include<v1.Conference, v2.Conference>()
.Include<v1.ReligiousCeremony, v2.ReligiousCeremony>();
cfg.CreateMap<v1.Conference, v2.Conference>();
cfg.CreateMap<v1.ReligiousCeremony, v2.ReligiousCeremony>();
});

How to Intercept EF operations?

I am currently using Entity Framework for a project and one of my classes have an Enum representing some values.
So far EF is saving the Enums as numbers in the database, but I wanted to save them as their actual string names. For example, the Enum NY is saved as 1, instead of "NY".
I have already seen some ways to make this work, like having a string property with a hidden Enum private field, but I wanted to know if there is a way I can just Intercept EF when it's doing the CRUD operations and then I can change the Enum to a String in the Data Context class.
No, you cannot do that directly: when you map your class with an Enum property, that property is mapped to a database int column, and you cannot change that in any way. I mean that, as you cannot change the model, there is no way to intercept and convert the Enum property value into an string, because the model stubbornly wants an int.
That said, there are several ways to make it work:
having an string property for the key and a [NotMapped] Enum property that updates that key. SEE THE NOTE: But the key must be public, and thus accesible through the application code.
using a class that have only the enum property and is used in your application domain, and a different class which is used for your EF model, and map the values, for example using ValueInjecter or Automapper
I usually take the first path and use an attribute that allows me to define the string key for each Enum value, so you can reuse this pattern in all the cases in which need to do this.
NOTE: this part of the answer was wrong: you can map any property regardles of the modifier (public, protected, private, internal...). EF conventions only include the public properties, and there are no data annotations that can overcome this limitation. But you can use it with the Fluent API. However, as the property is private,you cannot access it directly using the Fluent API. There are several solutions to do it described here: Code First Data Annotations on non-public properties
If you follow this path, you can have a class like this:
public class MyEntity
{
// ...
[NotMapped]
public EnumType Value
{
get { /* return KeyForEnum converted to EnumType value */ }
set { /* set KeyForEnum value from the received EnumType value*/}
}
// Use some mechanism to map this private property
private string KeyForEnum { get; set; }
// ...
}
As you can see, if you use a class like this, in the app the entity will have a property of EnumType type, but in the database it will be an string.
One of the tricks to be able to map it through Fluent API is this:
1) Add an static property that returns an expression able to select the property from an object of this class, i.e.
public static readonly Expression<Func<MyEntity,string>> KeyForEnumExpression
= me => me.KeyForEnum;
2) Use it in the fluent API to get the property mapped, like so:
modelBuilder
.Entity()
.Property(MyEntity.KeyForEnumExpression)
LAST NOTE: This will modify the POCO class by adding the static readonly property. You can use Reflection instead to build an expression to access the private property, like you can see here: EF 4.1 Code First, ¿map private members?. It's in Spanish, but you can look directly at the code

AutoFixture & AutoMoq: Overriding object generation behavior

I'm proposing using AutoFixture and AutoFixture.xUnit at our company, and have gotten the mandate that for certain objects and fields they want random data that is formatted in an expected way. For example, they want PersonName to only populate with realistic names (instead of GUIDs) and PhoneNumber to only make strings that look like phone numbers. But they DON'T want to add data annotations to the actual objects enforcing this, they would just like the test data generated by AutoFixture to be pretty.
I've dealt a bit with ICustomize classes to implement greedy constructor behavior on a few classes. Is there a similar way to override the data generation for specific objects? To (for example) pull names from a list, or generate data to follow a certain regular expression? (keeping in mind that I can't actually add those regular expressions as attributes on the model)
Ok, solved my problem.
Object generation for a given class type can be accomplished via the Fixture.Register method. You can make a method that returns the type you want to override and that will be used instead of the default.
To get meaningful data I just used Faker.Net.
I got the solution Mark pointed out working, and really liked it for general POJOs, but in my case many of my objects had properties that could only be set via the constructor or aggregate setters (like ChangeContactInfo), so unfortunately I needed something a bit more targeted. Here is an example of my solution implementing a name and address generation override:
public class CustomObjectGeneration : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Register(GenerateAddress);
fixture.Register(GeneratePersonName);
}
private Address GenerateAddress()
{
return new Address(Faker.Address.StreetAddress(), Faker.Address.SecondaryAddress(), Faker.Address.City(),
Faker.Address.ZipCode(), Faker.Address.UsState(), Faker.Address.Country());
}
private PersonName GeneratePersonName()
{
return new PersonName(Faker.Name.Prefix(), Faker.Name.First(), Faker.Name.First(), Faker.Name.Last(), Faker.Name.Suffix());
}
}

LINQ ignore incorrect type being assigned to property

So, I have my own mapper (NOT AutoMapper) which maps models to each other. You give the model you'd like to map to, and with the Map method you push an object in. Beside of this I wrote the Extend method which functions as a override for the Map method to, for example, add properties which are not available in the object being mapped.
Problem:
The problem hereby is that my public Mapper<T> Extend(Func<T, T> func) method doesn't like the different types.
Possible solutions:
There are 2 solutions I'm thinking of:
Ignore the error and map the value within my Extend method. Which isn't possible as far as I know due to the expression being executed immediately.
Create a LINQ method which maps the value for me. Eg; q => q.Ownership = obj.Ownerships.First().Map().
Question:
How can I resolve this error and achieve what I want?

Mapping to a custom type which has an internal constructor

We are trying to map an object - a Tridion Outbound Email Contact - which has a custom dictionary type property with an internal constructor - ExtendedDetailCollection
It's fine mapping from the object onto a Viewmodel
Mapper.CreateMap<Contact,ContactViewModel>()
.ForMember(x=>x.Name, m=>m.MapFrom(x=>x.ExtendedDetails["Name"].StringValue))
but the other way does not work
We have tried:
Mapper.CreateMap<ContactViewModel,Contact>()
.ForMember(x=>x.ExtendedDetails["Name"].Value, m => m.MapFrom(x=>x.Name));
but that throws a runtime exception.
Edit: The message of the exception is:
AutoMapper.AutoMapperConfigurationException : Custom configuration for
members is only supported for top-level individual members on a type.
We have also tried the various type converters and value resolvers but none allow us to get at the object being mapped to, which is what we need to get access to in order to map the ExtendedDetails object.
Mapper.CreateMap<ContactViewModel,Contact>()
.ForMember(x=>x.ExtendedDetails, m => ????);
Is there a pattern for this or is it easier just to use a static method?
If ExtendedDetails is a class you need to createMap for this class and then individual property like Name of the class.

Categories