AutoMapper cannot convert enum to nullable int? - automapper

I got AutoMapperMappingException exception
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown. ---> System.InvalidCastException: Invalid cast from 'DummyTypes' to 'System.Nullable`1[[System.Int32, ...
when
public enum DummyTypes : int
{
Foo = 1,
Bar = 2
}
public class DummySource
{
public DummyTypes Dummy { get; set; }
}
public class DummyDestination
{
public int? Dummy { get; set; }
}
[TestMethod]
public void MapDummy()
{
Mapper.CreateMap<DummySource, DummyDestination>();
Mapper.AssertConfigurationIsValid();
DummySource src = new DummySource()
{
Dummy = DummyTypes.Bar
};
Mapper.Map<DummySource, DummyDestination>(src);
}
Should not AutoMapper map this implicitly without any extra explicit rule?
P.S. I cannot change the definition of DummyDestination.Dummy to enum. I have to deal with such interfaces.

It looks like no, it won't take care of this automatically for you. Interestingly, it will map an enum to a regular int.
Looking at AutoMapper's source, I think the problematic line is:
Convert.ChangeType(context.SourceValue, context.DestinationType, null);
Assuming context.SourceValue = DummyTypes.Foo and context.DestinationType is int?, you would end up with:
Convert.ChangeType(DummyTypes.Foo, typeof(int?), null)
which throws a similar exception:
Invalid cast from 'UserQuery+DummyTypes' to
'System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0
So I think really the question is why can't we cast a variable of type enum to int? That question has already been asked here.
This seems like a bug in AutoMapper. Anyway the workaround is to map the property manually:
Mapper.CreateMap<DummySource, DummyDestination>()
.ForMember(dest => dest.Dummy, opt => opt.MapFrom(src => (int?)src.Dummy));

Just in case if anyone want to try using a type converter
Mapper.CreateMap<int?, DummyTypes.Foo?>().ConvertUsing(new FooTypeConverter());
public class FooTypeConverter: TypeConverter<int?, DummyTypes.Foo?>
{
protected override DummyTypes.Foo? ConvertCore(int? source)
{
return source.HasValue ? (DummyTypes.Foo?)source.Value : null;
}
}
Cheers

Related

Update a mixed type activity in GetStream.IO using the java stream client loses the type attribute

I have taken the MixedType example code that comes with the java stream client (https://github.com/GetStream/stream-java) and added a update step using updateActivities. After the update the activity stored in stream loses the 'type' attribute. Jackson uses this attribute when you get the activities again and it is deserialising them.
So I get:
Exception in thread "main" Disconnected from the target VM, address: '127.0.0.1:60016', transport: 'socket'
com.fasterxml.jackson.databind.JsonMappingException: Could not resolve type id 'null' into a subtype of [simple type, class io.getstream.client.apache.example.mixtype.MixedType$Match]
at [Source: org.apache.http.client.entity.LazyDecompressingInputStream#29ad44e3; line: 1, column: 619] (through reference chain: io.getstream.client.model.beans.StreamResponse["results"]->java.util.ArrayList[1])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
at com.fasterxml.jackson.databind.DeserializationContext.unknownTypeException(DeserializationContext.java:849)
See here where I have updated the example:
https://github.com/puntaa/stream-java/blob/master/stream-repo-apache/src/test/java/io/getstream/client/apache/example/mixtype/MixedType.java
Any idea what is going on here?
The issue here is originated by Jackson which cannot get the actual instance type of an object inside the collection due to the Java type erasure, if you want to know more about it please read this issue: https://github.com/FasterXML/jackson-databind/issues/336 (which also provides some possible workarounds).
The easiest way to solve it, would be to manually force the value of the property type from within the subclass as shown in the example below:
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", visible = true)
#JsonSubTypes({
#JsonSubTypes.Type(value = VolleyballMatch.class, name = "volley"),
#JsonSubTypes.Type(value = FootballMatch.class, name = "football")
})
static abstract class Match extends BaseActivity {
private String type;
public String getType() {
return type;
}
}
static class VolleyballMatch extends Match {
private int nrOfServed;
private int nrOfBlocked;
public VolleyballMatch() {
super.type = "volley";
}
public int getNrOfServed() {
return nrOfServed;
}
public void setNrOfServed(int nrOfServed) {
this.nrOfServed = nrOfServed;
}
public void setNrOfBlocked(int nrOfBlocked) {
this.nrOfBlocked = nrOfBlocked;
}
public int getNrOfBlocked() {
return nrOfBlocked;
}
}
static class FootballMatch extends Match {
private int nrOfPenalty;
private int nrOfScore;
public FootballMatch() {
super.type = "football";
}
public int getNrOfPenalty() {
return nrOfPenalty;
}
public void setNrOfPenalty(int nrOfPenalty) {
this.nrOfPenalty = nrOfPenalty;
}
public int getNrOfScore() {
return nrOfScore;
}
public void setNrOfScore(int nrOfScore) {
this.nrOfScore = nrOfScore;
}
}

Return derived type when return type is base type c#

I have a Base abstract Class and a Derived Class
Base abstract class
public abstract class BaseData: IListingData
{
private int? _photos;
public string DataKey { get; set; }
public abstract List<Images> Photos { get; set; }
}
Derived Class
public class DerivedData1 : BaseData
{
public override List<Images> Photos
{
get
{ return new List<Images>(); } set {}
}
}
public class DerivedData2 : BaseData
{
public override List<Images> Photos
{
get
{ return new List<Images>(); } set {}
}
}
I have a Service function:
public List<ListingData> FilterListings(PredicateHandler predicates)
{
//Retrieved from database and based on certain predicates, it will create List of DerivedData1 or DerivedData2
Return new List<DerivedData1>(); //This is where the ERROR is.
}
I am unable to return Derived Type. I tried casting and I get the following same compile error. Cannot convert expression type 'System.Collections.Generic.List< DerivedData1>' to return type 'System.Collections.Generic.List< ListingData>'
I also tried changing the return type of the service function FilterListings() to the Interface IListingData, but I still encounter the a casting error.
I searched on other Stackoverflow posts. Which answers the questions of returning a derived Type from within a Base class. But I think this is a different scenario.
Bottom line, My service-class function has a return type Animal() and from inside the function I want to return Dog()
What am I missing?
In your example code, you cannot return new List of DerivedData1 where the function return type is List of ListingData.
The reason is there is no hierarchical relations between the two list types.
What you can do is:
public List<ListingData> FilterListings(PredicateHandler predicates)
{
var list = new List<BaseData>();
var list.Add(new DerivedData1());
var list.Add(new DerivedData2());
return list;
}
I'd use List<object> if I were in your place, and cast object to whatever is needed when iterating (for example). Your issue is that List<Base> and List<DerivedFromBase> are treated as unrelated (which is the intended behaviour, even if uncomfortable).

Automapper: using Type Converter and Value Resolvers for the same types

I have a lots of DTOs that must be mapped to my domain's objects. In general, the mapping to monetary values need to apply a rounding rule. That's apply for more than 95% of the cases, but I have some data that need a different rounding rule. I was planned to do the next:
1) Create a ITypeConverter for the general rounding rule, that apply for default in for this type conversions.
2) Create a ValueResolver for the special rounding cases, and specify their use in the member mapping.
I created a test case to try my approach, but after the application of the ValueResolver for the special cases, AutoMapper uses the TypeConverter to get the final value, using the general rule.
That's a wrong approach?? May be I'm missing something??
Then the test case:
[TestFixture]
public class RoundingTests
{
public class MoneyConverter : ITypeConverter<decimal, decimal>
{
public decimal Convert(ResolutionContext context)
{
return Math.Round((decimal)context.SourceValue, 2, MidpointRounding.AwayFromZero);
}
}
public class Money12Resolver : ValueResolver<decimal, decimal>
{
protected override decimal ResolveCore(decimal source)
{
return Math.Round(source, 12, MidpointRounding.AwayFromZero);
}
}
public class PercentResolver : ValueResolver<decimal, decimal>
{
protected override decimal ResolveCore(decimal source)
{
return Math.Round(source, 4, MidpointRounding.AwayFromZero);
}
}
internal class Source
{
public decimal Price { get; set; }
public decimal Percent { get; set; }
public decimal Prorate { get; set; }
}
internal class Destination
{
public decimal Price { get; set; }
public decimal Percent { get; set; }
public decimal Prorate { get; set; }
}
[Test]
public void MappingTest()
{
Mapper.CreateMap<decimal, decimal>().ConvertUsing<MoneyConverter>();
Mapper.CreateMap<Source, Destination>()
.ForMember(d => d.Percent, o => o.ResolveUsing<PercentResolver>().FromMember(s => s.Percent))
.ForMember(d => d.Prorate, o => o.ResolveUsing<Money12Resolver>().FromMember(s => s.Prorate));
Mapper.AssertConfigurationIsValid();
var source = new Source
{
Price = 12345.6789m,
Percent = 0.123456m,
Prorate = 123.123451234512345m
};
var convertion = Mapper.Map<Destination>(source);
Assert.That(convertion, Is.Not.Null);
Assert.That(convertion.Price, Is.EqualTo(12345.68m));
Assert.That(convertion.Percent, Is.EqualTo(0.1235m));
Assert.That(convertion.Prorate, Is.EqualTo(123.123451234512m));
}
}
Test Result:
Expected: 0.1235m
But was: 0.12m
You're telling AutoMapper to convert all decimal->decimal mappings using your MoneyConverter. AutoMapper does, in fact, use your resolver (set a break point to see), but the result of resolving is a decimal value that is then used by the MoneyConverter you applied.
Note: This appears to be the design of AutoMapper; I don't see a way to override a type converter.
Not all of your decimal properties represent money, so you may want to take a different approach. Also ask yourself, are you rounding purely for values used for presentation, or are you potentially losing precision you'd want to retain in those domain objects? If you really need the rounding, you could be explicit and set a resolver per member, skipping the converter completely. Alternatively, you could ignore the members which are exceptions and handle that with a .ConstructUsing(...).
But since your original question relates to using a Resolver and Converter for the same type, here's one way you can make it work:
Basically we want the converter to skip the default conversion for certain properties. We can't do it via configuration, so we'll have to do it at run-time. Assuming you have access to the Destination class, just decorate properties with non-default behavior using a custom attribute.
class PercentAttribute : Attribute
{ }
class Destination
{
[Percent]
public decimal Percent { get; set; }
...
}
In your converter, you can then look for properties which are [Percent] and return the source value, which came from your PercentResolver.
public class MoneyConverter : ITypeConverter<decimal, decimal>
{
public decimal Convert(ResolutionContext context)
{
var propInfo = context.PropertyMap.DestinationProperty.MemberInfo;
bool isPercent = propInfo.GetCustomAttributes(typeof(PercentAttribute), true).Any();
if (isPercent) return (decimal)context.SourceValue;
return Math.Round((decimal)context.SourceValue, 2, MidpointRounding.AwayFromZero);
}
}
If you're going to go that route, just have the converter do Money, Percent, and Money12 conversions based on attributes, and skip the resolvers completely.

AutoMapper ConstructServicesUsing is ignored

I have a Person and a PersonViewModel. I created a map from Person => PersonViewModel. The problem is that PersonViewModel's only constructor needs an argument (it has a dependency that I want to be injected) and AutoMapper is complaining because it says it needs a parameterless constructor.
To fix it, I used the ConstructServicesUsing method, but I haven't been successful with it :(
To illustrate the case, I created a test for you to see what I'm doing. It's pretty simple:
[TestMethod]
public void TestConstructServicesUsing()
{
Mapper.Initialize(configuration =>
{
configuration.ConstructServicesUsing(FactoryMethod);
configuration.CreateMap<Person, PersonViewModel>();
});
Mapper.AssertConfigurationIsValid();
var person = new Person();
var personViewModel = Mapper.Map<Person, PersonViewModel>(person);
}
private object FactoryMethod(Type type)
{
throw new NotImplementedException();
}
}
The rest of the code is the classes and interface definitions. They are almost empty.
public class SomeyDependency : ISomeDependency
{
}
public class PersonViewModel
{
private readonly ISomeDependency service;
public PersonViewModel(ISomeDependency service)
{
this.service = service;
}
public string Name { get; set; }
}
public class Person
{
public string Name { get; set; }
}
public interface ISomeDependency
{
}
As you see, I provide AutoMapper with a FactoryMethod, but it never get called.
When it reaches the last line of the test (Mapper.Map<...>()) it throws an excepton saying:
AutoMapper.AutoMapperMappingException:
Mapping types:
Person -> PersonViewModel
MappingWithContainerTests.Person -> MappingWithContainerTests.PersonViewModel
Destination path:
PersonViewModel
Source value:
MappingWithContainerTests.Person ---> System.ArgumentException: Type needs to have a constructor with 0 args or only optional args
Parameter name: type
What's the problem?
Why isn't the FactoryMethod being called?
As #khorvat mention where is missing .ConstructUsingServiceLocator(), for concrete mapping.
Also you can set constructor directly by
.ConstructUsing(source => Method(source.anySourceOptions))
Or as exception said:
PersonViewModel, must have a constructor with 0 args or only optional
args. You have only one constructor with 1 not optional argument
you may create one more constructor without args:
public PersonViewModel()
{
this.service = new SomeDependency();
}
I'm using .NET Core 3.1 and Automapper.Extensions.Microsoft.DependencyInjection.
This does not work for me (Same error as yours):
public class AutoMapping : Profile
{
public AutoMapping()
{
CreateMap<Context, MainViewModel>()
.ReverseMap()
.ConstructUsingServiceLocator();
}
}
But this does work:
public class AutoMapping : Profile
{
public AutoMapping()
{
CreateMap<Context, MainViewModel>()
.ConstructUsingServiceLocator()
.ReverseMap();
}
}
I still do not fully understand the cause.

How to set up Entity Framework to map two classes to the same table

I've been bumbling along with EF5 but I cant seem to get two domain classes to map to a single database table.
The error I get is:
Message: "The type 'Basd.Erp.Wms.Purchasing.SupplierProfile' has already been configured as an entity type. It cannot be reconfigured as a complex type."
This is my DbContext:
public class PurchasingContext : DisconnectedEntityContext
{
public DbSet<SupplierCard> Suppliers { get; set; }
public DbSet<PurchaseCategory> PurchaseCategories { get; set; }
public PurchasingContext() : this("Basd.Erp.Wms") { }
public PurchasingContext(string connectionStringName) : base(connectionStringName) { }
public static PurchasingContext GetInstance(EfDataProvider provider) { return new PurchasingContext(provider.ConnectionStringName); }
}
}
These are my classes:
namespace Basd.Erp.Wms.Purchasing
{
public class SupplierCard : ContactCard, ISupplierCard
{
private ICollection<PurchaseCategory> _purchaseCategories;
public ICollection<PurchaseCategory> PurchaseCategories
{
get { return _purchaseCategories; }
set { SetNotifyField(ref _purchaseCategories, value, () => PurchaseCategories); }
}
public SupplierProfile Profile { get; protected set; }
private SupplierCard()
{
this.Profile = new SupplierProfile();
this.PurchaseCategories = new Collection<PurchaseCategory>();
}
public SupplierCard(long id, string alf, string name)
: this(id, alf, new SimpleNameHolder(name), new Collection<IPhysicalAddress>(), new DigitalAddresses()) { }
public SupplierCard(long id, string alf, INameHolder nameHolder,
ICollection<IPhysicalAddress> physicalAddresses, IDigitalAddresses digitalAddresses)
: this(id, alf, nameHolder, physicalAddresses, digitalAddresses, null) { }
public SupplierCard(long id, string alf, INameHolder nameHolder,
ICollection<IPhysicalAddress> physicalAddresses, IDigitalAddresses digitalAddresses, IValidatableObject validator)
: base(id, alf, nameHolder, physicalAddresses, digitalAddresses, validator)
{
this.Profile = new SupplierProfile();
this.PurchaseCategories = new Collection<PurchaseCategory>();
}
}
}
public class SupplierProfile : AbstractAspect
{
private TradingEntity _incType;
public TradingEntity BusinessType
{
get { return _incType; }
set
{
if (_incType != null) { this.DeregisterSubPropertyForChangeTracking(this.BusinessType); }
_incType = value; this.OnPropertyChanged("TradingType");
this.RegisterSubPropertyForChangeTracking(this.BusinessType);
}
}
private bool _emailOk;
private bool _smailOk;
public bool MarketingEmailOk
{
get { return _emailOk; }
set { _emailOk = value; this.OnPropertyChanged("MarketingEmailOk"); }
}
public bool MarketingSmailOk
{
get { return _smailOk; }
set { _smailOk = value; this.OnPropertyChanged("MarketingSmailOk"); }
}
public SupplierProfile()
: base()
{
this.BusinessType = new TradingEntity(ContactLegalType.Limited);
}
}
}
These are my configuration classes:
[Export(typeof(IEntityConfiguration))]
public class SupplierCardConfiguration
: EntityTypeConfiguration<SupplierCard>, IEntityConfiguration
{
public SupplierCardConfiguration()
{
this.ToTable("SupplierCard", "erp_wms");
HasKey(u => u.Id);
Property(u => u.Id).HasColumnName("SupplierId");
Ignore(u => u.UsePropertyNotifications);
Property(u => u.Profile.MarketingEmailOk).HasColumnName("MarketingEmailOk");
HasMany(i => i.PurchaseCategories)
.WithMany(c => c.Suppliers)
.Map(mc =>
{
mc.MapLeftKey("CategoryId");
mc.MapRightKey("SupplierId");
mc.ToTable("SupplierPurchaseCategory", "erp_wms");
});
}
public void AddConfiguration(ConfigurationRegistrar registrar)
{
registrar.Add(this);
}
}
[Export(typeof(IEntityConfiguration))]
public class SupplierProfileConfiguration
: EntityTypeConfiguration<SupplierProfile>, IEntityConfiguration
{
public SupplierProfileConfiguration()
{
this.ToTable("SupplierCard", "erp_wms");
Ignore(u => u.UsePropertyNotifications);
Property(u => u.MarketingEmailOk).HasColumnName("MarketingEmailOk");
}
public void AddConfiguration(ConfigurationRegistrar registrar)
{
registrar.Add(this);
}
}
UPDATE:
Ok so Ive tried ignoring SupplierProfile as per suggestion that changed nothing. I then tried removing the configuration class for Supplier Profile and left
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<SupplierProfile>();
base.OnModelCreating(modelBuilder);
}
and that generated an error:
{"The property 'Profile' is not a declared property on type
'SupplierCard'. Verify that the property has not been explicitly
excluded from the model by using the Ignore method or
NotMappedAttribute data annotation. Make sure that it is a valid
primitive property."}
[System.InvalidOperationException]: {"The property 'Profile' is not a declared property on type 'SupplierCard'. Verify that the
property has not been explicitly excluded from the model by using the
Ignore method or NotMappedAttribute data annotation. Make sure that it
is a valid primitive property."}
I then tried removing the
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<SupplierProfile>();
base.OnModelCreating(modelBuilder);
}
while leaving out the configuration class for SupplierProfile and that generates the error:
Message: "Invalid column name
'Profile_BusinessType_ContactLegalType'.\r\nInvalid column name
'Profile_BusinessType_TradingSince'.\r\nInvalid column name
'Profile_BusinessType_State'.\r\nInvalid column name
'Profile_BusinessType_UsePropertyNotifications'.\r\nInvalid column
name 'MarketingEmailOk'.\r\nInvalid column name
'Profile_MarketingSmailOk'.\r\nInvalid column name
'Profile_State'.\r\nInvalid column name
'Profile_UsePropertyNotifications'.\r\nInvalid column name
'OwnerId'.\r\nInvalid column name 'State'."
So like I said, just **bumbling** along ;)
After reading this I think it might have something to do with your relationship in your SupplierCard class.
public class SupplierCard : ContactCard, ISupplierCard
{
public SupplierProfile Profile { get; protected set; }
}
I'm guessing it registering as a complex type when SupplierCard is mapped.
A suggested way to fix it is to ignore it.
modelBuilder.Ignore<SupplierProfile>();
I've never run into this problem myself, so not sure if this'll help.
So after a lot of mucking around it turns out the underlying problem is a bug in Entity Framework 5. This bug has been fixed in EF6 beta. All other errors were in fact just masking this underlying error.
The following explaination is not terribly good as I dont fully understand it myself.
Short answer is: Use EF6 or otherwise modify EF5 source code.
Turns out that if you have a class in assembly B, that has a property of a type of enum defined in Assembly A, EF5 gets confused and thinks the enum is missing or somehow unavailable and sets about trying to generate the type itself.
So I had:
Assembly A containing enum type AA.
Assembly B referencing Assembly A so a contained class BB could have a property of type AA.
An EF5 data layer Assembly referencing both Assembly A & B.
An EF5 configuration layer Assembly referencing both Assembly A & B.
And it failed.
But if I "simply" move enum type AA into Assembly B then everything works.
This is of course is completely useless because then I set up all kinds of dependencies on Assembly B for any Assembly that has a member who needs an enum AA. But that is the test.
To top it off there also appears to be some particular set of circumstances in which everything I just said does not apply due to the order assemblies are loaded at runtime. The order of this loading cannot be forced i.e. it's non-determinant so it's pot luck.

Resources