How to make AutoMapper throw when mapping null into non-nullable value type? - automapper

I have two classes:
public class ClassA
{
public ClassA(int? value)
{
Value = value;
}
public int? Value { get; }
}
public class ClassB
{
public ClassB(int value)
{
Value = value;
}
public int Value { get; }
}
When mapping an instance of ClassA with Value == null into ClassB, AutoMapper seems to ignore the property and provide the default value instead:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ClassA, ClassB>();
});
var mapper = new Mapper(config);
var classA = new ClassA(null);
var classB = mapper.Map<ClassB>(classA);
Debug.Assert(classB.Value == 0);
How do I make AutoMapper throw an exception when mapping an instance of ClassA with Value == null into ClassB instead? Optimally, can I enable this behavior for all mappings from nullable value types to non-nullable value types?

I can add a custom BeforeMap function that tries to identify these kind of situations via reflection, but I'd prefer a simpler solution and/or one that also handled custom mappings of differently named properties, etc.
cfg.ForAllMaps((_, e) => e.BeforeMap(CheckForDisallowedNulls));
void CheckForDisallowedNulls(object source, object destination)
{
foreach (var sourceProperty in source.GetType().GetProperties())
{
var underlyingType = Nullable.GetUnderlyingType(sourceProperty.PropertyType);
if (underlyingType is { })
{
var destinationProperty = destination.GetType().GetProperty(sourceProperty.Name);
if (destinationProperty is { } && destinationProperty.PropertyType == underlyingType && sourceProperty.GetValue(source) == null)
{
throw new ArgumentNullException(sourceProperty.Name);
}
}
}
}

I can create a map from int? to int that throws an exception when the source value is null:
cfg.CreateMap<int?, int>().ConvertUsing((s, _) => s.Value);
However, this requires adding such a map for each nullable value type I want the behavior for.

Related

Ignore member of base class in YamlDotNet

I have a class which I want to serialize with YamlDotNet:
public class AwesomeClass : PropertyChangedBase
{
private bool _element1;
private bool _enabled;
public bool Element1
{
get { return _element1; }
set
{
_element1 = value;
NotifyOfPropertyChange(() => Element1);
}
}
public bool Enabled
{
get { return _enabled; }
set
{
_enabled = value;
NotifyOfPropertyChange(() => Enabled);
}
}
}
My problem is, in the base class is an element named: IsNotifying
Is there a way to exclude this element from serialization, without the change of the base class?
You could override the property in the derived class and apply the YamlIgnore attribute there. While the sample below works, I suspect for more complicated class hierarchies you would really need to ensure no behavior changes.
public class AwesomeClass : PropertyChangedBase
{
[YamlIgnore]
public new bool IsNotifying
{
get { return base.IsNotifying; }
set { base.IsNotifying = value; }
}
[YamlIgnore]
public override bool Blah
{
get { return base.Blah; }
set { base.Blah = value; }
}
}
public class PropertyChangedBase
{
public bool IsNotifying
{
get;
set;
}
public virtual bool Blah
{
get;
set;
}
}
I had a similar problem (needed to filter properties of a particular type from classes I couldn't change, so using the attribute was not an option) and is what I came up with:
Create a custom type inspector:
public class MyTypeInspector : TypeInspectorSkeleton
{
private readonly ITypeInspector _innerTypeDescriptor;
public MyTypeInspector(ITypeInspector innerTypeDescriptor)
{
_innerTypeDescriptor = innerTypeDescriptor;
}
public override IEnumerable<IPropertyDescriptor> GetProperties(Type type, object container)
{
var props = _innerTypeDescriptor.GetProperties(type, container);
props = props.Where(p => !(p.Type == typeof(Dictionary<string, object>) && p.Name == "extensions"));
props = props.Where(p => p.Name != "operation-id");
return props;
}
}
Create the serializer as follows:
var builder = new SerializerBuilder();
builder.WithTypeInspector(inspector => new MyTypeInspector(inspector));
var serializer = builder.Build();

Automapper error: Expressions mapping from methods not supported yet

Any idea what might cause the error "Expressions mapping from methods not supported yet." when trying to map two objects? I cannot find any reference to this error anywhere.
EDITED---
I have more information. I have a property in my DTO declared as:
public LookupItem RegionType { get; set; }
However, when I invoke the mapping, it generates the error, "Expressions mapping from methods not supported yet.".
However, if I change the string in the property name "Type" to anything else like "Typeo" or "ASDF", the mapping succeeds. In other words, if change the property name to "RegionTypeo". Am I breaking any convention rules here? There seems to be something wrong with including the string "Type" in my property name.
Below is the generated error:
Result Message:
Test method Rep.Tests.PlanServiceTest.GetBuildings threw exception:
System.NotImplementedException: Expressions mapping from methods not supported yet.
Result StackTrace:
at AutoMapper.PropertyMap.ResolveExpression(Type currentType, Expression instanceParameter)
at AutoMapper.QueryableExtensions.Extensions.CreateMemberBindings(IMappingEngine mappingEngine, Type typeIn, TypeMap typeMap, Expression instanceParameter)
at AutoMapper.QueryableExtensions.Extensions.CreateMapExpression(IMappingEngine mappingEngine, Type typeIn, Type typeOut, Expression instanceParameter)
at AutoMapper.QueryableExtensions.Extensions.CreateMapExpression(IMappingEngine mappingEngine, Type typeIn, Type typeOut)
at AutoMapper.QueryableExtensions.Extensions.<>c__DisplayClass12.<CreateMapExpression>b__0(TypePair tp)
at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory)
at AutoMapper.Internal.DictionaryFactoryOverride.ConcurrentDictionaryImpl2.GetOrAdd(TKey key, Func2 valueFactory)
at AutoMapper.QueryableExtensions.Extensions.CreateMapExpression[TSource,TDestination](IMappingEngine mappingEngine)
at AutoMapper.QueryableExtensions.ProjectionExpression1.ToTResult
at Rep.Services.PlanService.GetBuildings() in c:\Dev\REP\Rep\Services\PlanService.cs:line 369
at Rep.Tests.PlanServiceTest.GetBuildings() in c:\Dev\REP\Rep.Tests\PlanServiceTest.cs:line 50
Based on the source code, you can see that the exception is thrown when you try mapping functions on your objects:
public ExpressionResolutionResult ResolveExpression(Type currentType, Expression instanceParameter)
{
Expression currentChild = instanceParameter;
Type currentChildType = currentType;
foreach (var resolver in GetSourceValueResolvers())
{
var getter = resolver as IMemberGetter;
if (getter != null)
{
var memberInfo = getter.MemberInfo;
var propertyInfo = memberInfo as PropertyInfo;
if (propertyInfo != null)
{
currentChild = Expression.Property(currentChild, propertyInfo);
currentChildType = propertyInfo.PropertyType;
}
else
{
throw new NotImplementedException("Expressions mapping from methods not supported yet.");
}
}
else
{
var oldParameter = CustomExpression.Parameters.Single();
var newParameter = instanceParameter;
var converter = new ConversionVisitor(newParameter, oldParameter);
currentChild = converter.Visit(CustomExpression.Body);
currentChildType = currentChild.Type;
}
}
return new ExpressionResolutionResult(currentChild, currentChildType);
}
Based on OP clarification, I cannot reproduce the problem with the following:
public class Class1
{
public string StringType { get; set; }
public Func<Class1> FuncType { get; set; }
public Class1 Class1Type { get; set; }
}
public class Class2
{
public string StringType { get; set; }
public Func<Class1> FuncType { get; set; }
public Class1 Class1Type { get; set; }
}
/* ... */
AutoMapper.Mapper.CreateMap<Class1, Class2>();
var c1 = new Class1() { Class1Type = new Class1(), FuncType = () => new Class1(), StringType = "Class1" };
var c2 = AutoMapper.Mapper.Map<Class1, Class2>(new Class1());

forcing my property to accept only certain kinds of data types

public object Value
{
get
{
return _value;
}
set
{
_value = value;
}
}
public enum someEnum
{
sString = 1,
sBoolean = 2,
sInt = 3,
sDate = 4
sData = 5 //Custom data type eg; a class
}
I want my property value to accept and return only the datatypes specified in the someEnum enumerator. I am using vs2012
I tried with enum, it works but I had to hard code enum values like "System.String" and used Enum.TryParse in set modifier.
MyEnum type;
bool res = Enum.TryParse<MyEnum>(Convert.ToString(value.GetType()), out type);
IMHO, it looks better with extension methods
private object _value;
public object Value
{
get
{
return _value;
}
set
{
if (value.IsValidType())
_value = value;
else
throw new Exception("Not a valid type");
}
}
public static class ObjectExtenstions
{
public static bool IsValidType(this object obj)
{
if (obj.GetType() == typeof(System.String) || obj.GetType() == typeof(System.Int32))
return true;
return false;
}
}
Have you tried this:
public someEnum Value
{
get; set;
}

Automapper: Mapping using attributes for non-matching properties

I have a scenario in which I'm creating a mapping utility through which we can map DataReader to entities.
Mapper.CreateMap<IDataReader, T>();
List<T> tList = Mapper.Map<List<T>>(reader);
return tList;
This is working fine if the entity has same columns as in the stored procedure's output. However, I'm working on an some old code where column names are not matching. So I created an attribute through which I could specify corresponding column from sprocs. I specify them using code like this
[DBColumn('EmployeeName')]
public string EmpName { get; set; }
Is there any way I can achieve this using generics/reflection? Note that I have multiple such instances and I'm building an utility. So I can not use ForMember method to achieve that.
UPDATE: I tried creating a custom converter like this. However, my convert function is not getting called at all.
Custom converter
public class CustomConverter<T> : ITypeConverter<System.Data.IDataReader, T>
{
private ResolutionContext _Context = null;
private Dictionary<string, string> _CustomProps { get; set; }
public T Convert(ResolutionContext context)
{
_Context = context;
if (_Context.SourceValue != null &&
!(_Context.SourceValue is System.Data.IDataReader))
{
string message = "Value supplied is of type {0} but expected {1}.\n" +
"Change the type converter source type, or redirect " +
"the source value supplied to the value resolver using FromMember.";
throw new AutoMapperMappingException(_Context, string.Format(
message, typeof(System.Data.IDataReader), _Context.SourceValue.GetType()));
}
_CustomProps = new Dictionary<string, string>();
foreach (PropertyInfo propInfo in context.DestinationType.GetProperties())
{
if (propInfo.CustomAttributes.Any(attr => attr.AttributeType == typeof(DBColumn)))
{
DBColumn propertyValue = (DBColumn)propInfo.GetCustomAttribute(typeof(DBColumn));
_CustomProps.Add(propertyValue.ColumnName, propInfo.Name);
}
}
//return base.Convert(context);
return ConvertCore((System.Data.IDataReader)context.SourceValue);
}
protected T ExistingDestination
{
get
{
if (_Context == null)
{
string message = "ResolutionContext is not yet set. " +
"Only call this property inside the 'ConvertCore' method.";
throw new InvalidOperationException(message);
}
if (_Context.DestinationValue != null &&
!(_Context.DestinationValue is T))
{
string message = "Destination Value is of type {0} but expected {1}.";
throw new AutoMapperMappingException(_Context, string.Format(
message, typeof(T), _Context.DestinationValue.GetType()));
}
return (T)_Context.DestinationValue;
}
}
protected T ConvertCore(System.Data.IDataReader source)
{
T obj = ExistingDestination;
if (obj != null)
{
foreach (KeyValuePair<string, string> keyValuePair in _CustomProps)
{
PropertyInfo prop = obj.GetType().GetProperty(keyValuePair.Key, BindingFlags.Public | BindingFlags.Instance);
if (null != prop && prop.CanWrite)
{
prop.SetValue(obj, System.Convert.ChangeType(source[keyValuePair.Value], prop.PropertyType));
}
}
}
return obj;
}
I'm specifying to use this CustomConverter in below statement.
Mapper.CreateMap<IDataReader, T>().ConvertUsing<CustomConverter<T>>();
List<T> tList = Mapper.Map<List<T>>(reader);
return tList;
Please let me know, where I'm going wrong

How to inherit partial class for stored procedure call

I have this class as parent class:
public partial class GetStuffResult
{
private int _Id;
private string _Name;
public GetStuffResult()
{
}
[Column(Storage="_Id", DbType="INT NOT NULL")]
public int Id
{
get
{
return this._Id;
}
set
{
if ((this._Id != value))
{
this._Id = value;
}
}
}
[Column(Storage="_Name", DbType="NVarChar(100)")]
public string Name
{
get
{
return this._Name;
}
set
{
if ((this._Name != value))
{
this._Name = value;
}
}
}
}
This is base class which has same methods with exception of an extra method:
public partial class GetStuffResult1
{
private int _Score;
private int _Id;
private string _Name;
public GetStuffResult1()
{
}
[Column(Storage="_Score", DbType="INT NOT NULL")]
public int Id
{
get
{
return this._Score;
}
set
{
if ((this._Score != value))
{
this._Score = value;
}
}
}
[Column(Storage="_Id", DbType="INT NOT NULL")]
public int Id
{
get
{
return this._Id;
}
set
{
if ((this._Id != value))
{
this._Id = value;
}
}
}
[Column(Storage="_Name", DbType="NVarChar(100)")]
public string Name
{
get
{
return this._Name;
}
set
{
if ((this._Name != value))
{
this._Name = value;
}
}
}
}
I have done inheritance before but i am totally confused how it will work in this scenario? How can i inherit GetStuffResult so that i can use its 2 methods and dont have to copy paste same code twice in GetStuffResult1.
Will appreciate if someone can give example with code as i am new to .net 3.5 and still trying to learn it.
I am not sure if I correctly understood your question. (Your current code for GetStuffResult1 shouldn't compile as you have define Id property twice.) If you are looking to inherit from GetStuffResult then this would do (See Inheritance):
public partial class GetStuffResult1 : GetStuffResult
{
private int _Score;
public GetStuffResult1()
{
}
[Column(Storage = "_Score", DbType = "INT NOT NULL")]
public int Id
{
get
{
return this._Score;
}
set
{
if ((this._Score != value))
{
this._Score = value;
}
}
}
}
Notice that I have removed _Id and _Name from the child class. This however will give you warning that:
GetStuffResult1.Id' hides inherited member
'ThreadConsoleApp.GetStuffResult.Id'. Use the new keyword if hiding
was intended.
The second thing I am thinking about your question if you are confused about using partial classes and you may need a single class in multiple source file. In that case you may use partial keyword. If that is the case and you don't need inheritance then you need to use a single name for the class. e.g. GetStuffResult. In that particular case your GetStuffResult1 will become:
public partial class GetStuffResult
{
private int _Score;
public GetStuffResult1()
{
}
[Column(Storage = "_Score", DbType = "INT NOT NULL")]
public int Id
{
get
{
return this._Score;
}
set
{
if ((this._Score != value))
{
this._Score = value;
}
}
}
}
This will be similar to having a single class with all the combined properties.
Edit:
To access the base class properties in the child class, you may use base keyword.
base.Id = 0;
base.Name = "SomeName";
To access the base class properties from the object of GetStuffResult1, see the following example.
GetStuffResult1 gsr1 = new GetStuffResult1();
gsr1.Id = 0;
gsr1.Name = "SomeName";
Here gsr1.Name is from the base class, you may use different name for Id in either base or child class so that it can be more clearer.

Resources