I have following original class structure:
public interface IMapFromElement
{
string Prop { get; }
}
public interface IMapFromElementDerived : IMapFromElement
{
string Prop2 { get; }
}
public interface IMapFromElement2 : IMapFromElement
{
}
public interface IMapFromElementDerived2 : IMapFromElementDerived, IMapFromElement2
{
}
public abstract class MapFromElement : IMapFromElement2
{
public string Prop { get; set; }
}
public class MapFromElementDerived : MapFromElement, IMapFromElementDerived2
{
public string Prop2 { get; set; }
}
I'm trying to map them to:
public class MapTo
{
public IMapToElementWritable Element { get; set; }
}
public interface IMapToElementWritable : IMapFromElement
{
new string Prop { get; set; }
}
public interface IMapToElementWritableDerived : IMapFromElementDerived, IMapToElementWritable
{
new string Prop2 { get; set; }
}
public abstract class MapToElement : IMapToElementWritable
{
public string Prop { get; set; }
}
public class MapToElementDerived : MapToElement, IMapToElementWritableDerived
{
public string Prop2 { get; set; }
}
I try to map them with:
var from = new MapFrom
{
Element = new MapFromElementDerived {Prop = "qwerty", Prop2 = "asdf"}
};
Mapper.Initialize(
cfg =>
{
cfg.CreateMap<IMapFrom, MapTo>();
cfg.CreateMap<IMapFromElement, IMapToElementWritable>();
cfg.CreateMap<IMapFromElementDerived, IMapToElementWritableDerived>()
.IncludeBase<IMapFromElement, IMapToElementWritable>()
.ConstructUsing((ResolutionContext item) => new MapToElementDerived());
cfg.Seal();
});
Mapper.AssertConfigurationIsValid();
var result = Mapper.Map<MapTo>(from);
I expected, that I will have as output MapTo with MapToElementDerived as it's Element property value. But really I was unable to achieve it - Automapper creates proxy for IMapToElementWritable instead. Looks like IncludeBase doesn't work (I tried it with Include also, but it haven't helped). Maybe I just write incorrect config.
Looks like there is an issue in Automapper. I've tried to resolve it in https://github.com/AutoMapper/AutoMapper/pull/1037
Related
I have a flat complex type which I need to map to a complex type within a list. I have achieved it using the below code but it is not elegant. The mapping for each individual item has to be specified explicitly even though the types and names match. I wanted to know if there is a more elegant way of doing this without such verbosity and tight coupling?
using AutoMapper;
MapperConfiguration _config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<FlatObject, MyDTO>()
.ForMember(dst => dst.ListObject, opt => opt.MapFrom(src => new List<ListObject> {
new ListObject {
DTOCustObject = new DTOCustObject {
MyString = src.CustType.MyString,
MyInt = src.CustType.MyInt,
MyBool = src.CustType.MyBool,
//Others...
DTOMyObject = new DTOMyObject {
SomeString = src.CustType.MyObject.SomeString
//Others...
}
}
}
}));
});
_config.AssertConfigurationIsValid();
var flatObject = new FlatObject();
flatObject.CustType.MyString = "ABC123";
flatObject.CustType.MyInt = 12345;
flatObject.CustType.MyBool = true;
flatObject.CustType.MyObject.SomeString = "Some String Content";
IMapper mapper = new Mapper(_config);
var myDTO = mapper.Map<MyDTO>(flatObject);
Console.ReadKey();
//###############
//Entity - Source
//###############
public class FlatObject
{
public CustType CustType { get; set; } = new CustType();
}
public class CustType
{
public string? MyString { get; set; }
public int MyInt { get; set; }
public bool MyBool { get; set; }
public MyObject MyObject { get; set; } = new MyObject();
}
public class MyObject
{
public string? SomeString { get; set; }
}
//#################
//DTO - Destination
//#################
public class MyDTO
{
public List<ListObject> ListObject { get; set; } = new List<ListObject>();
}
public class ListObject
{
public DTOCustObject DTOCustObject { get; set; } = new DTOCustObject();
}
public class DTOCustObject
{
public string? MyString { get; set; }
public int MyInt { get; set; }
public bool MyBool { get; set; }
public DTOMyObject DTOMyObject { get; set; } = new DTOMyObject();
}
public class DTOMyObject
{
public string? SomeString { get; set; }
}
public class MyDTO
{
public List<DTOCustObject> DTOCustObjectList{ get; set; } = new List<DTOCustObject>();
}
public class DTOCustObject
{
public string? MyString { get; set; }
public int MyInt { get; set; }
public bool MyBool { get; set; }
public DTOMyObject DTOMyObject { get; set; } = new DTOMyObject();
public DTOCustObject(ICustObjectMapper mapper){
MyString = mapper.GetMyString();
MyInt = mapper.GetMyInt();
MyBool = mapper.GetMyBool();
DTOMyObject = mapper.GetDTOMyObject();
}
}
public class DTOMyObject
{
public string? SomeString { get; set; }
public DTOMyObject(IDTOmyObjectMapper mapper)
{
SomeString = mapper.GetSomeString();
}
}
Now simply implement the Interfaces and their GetMethods on the source object, in the corresponding classes. And presto. No need for any framework of any kind.
This means, no framework, will try change syntax, and force a rewrite. You aren't locked into keeping it updated, there won't be any dependencies on it, etc.
You are free, and the time it takes you to configure, and annotate the code, is equal to the amount of time it takes you to just write the damn interfaces and implementations.
If your objects are autogenerated, simply use the partial keyword, and make the interface implementations in a partial class. Problem solved.
Something to this effect:
public class CustType : ICustObjectMapper
{
public string? MyString { get; set; }
public int MyInt { get; set; }
public bool MyBool { get; set; }
public MyObject MyObject { get; set; } = new MyObject();
string GetMyString(){
return MyString;
}
// etc for the rest of the basic types.
DTOMyObject GetMyObject(){
return new DTOMyObject(MyObject)
}
}
public class MyObject : IDTOmyObjectMapper
{
public string? SomeString { get; set; }
public string GetSomeString()
{
return SomeString;
}
}
Of course, if you don't know how to declare an interface...
public interface ICustObjectMapper {
string GetMyString();
//etc.
}
Now please, for the love of everything on this planet. STOP USING MAPPING FRAMEWORKS IN C#, IT MAKES NO SENSE!
I have a Json class "GetAllDevices()". My JSON response consists of an Array/List of objects, where each object has the below common properties.
public class GetAllDevices
{
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("actions")]
public Action[] Actions { get; set; }
public class Action
{
public string _id { get; set; }
public Action_Def action_def { get; set; }
}
public class Action_Def
{
public string _id { get; set; }
public string name { get; set; }
}
}
I want to create 2 generic lists containing all the above properties based on its "type".
lstfoo1 List contains all the properties(_id, name type and actions) where type="foo1". Similarly, lstfoo2 is a List which contains the above properties where type="foo2".
What I have done so far:
string strJson=getJSON();
Foo1 lstfoo1=new Foo1();
Foo2 lstfoo2=new Foo2();
List<Foo1> foo1list= lstfoo1.GetDeviceData(strJson);
List<Foo2> foo2list = lstfoo2.GetDeviceData(strJson);
public class AllFoo1: GetAllDevices
{
}
public class AllFoo2: GetAllDevices
{
}
public abstract class HomeDevices<T>
{
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1{ get; set; }
public List<AllFoo2> lstfoo2{ get; set; }
public abstract List<T> GetDeviceData(string jsonResult);
}
public class Foo1: HomeDevices<AllFoo1>
{
public Foo1()
{
type = "foo1";
}
public override List<AllFoo1> GetDeviceData(string jsonResult)
{
var lst =Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo1>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
public class Foo2: HomeDevices<AllFoo2>
{
public Foo2()
{
type = "foo2";
}
public override List<AllFoo2> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AllFoo2>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
My question is, is there an easier way to do this using abstract classes? Can I directly convert my "GetAllDevices" class into an abstract class and inherit it and deserialize into it and create a generic list?
This should help, if I understand your problem correctly. Let me know if you have questions or it doesn't work as you need. I put this together really quickly without testing.
The way the Type property is defined could be improved but I left it as you had it.
public class MyApplication
{
public void DoWork()
{
string json = getJSON();
DeviceTypeOne foo1 = new DeviceTypeOne();
DeviceTypeTwo foo2 = new DeviceTypeTwo();
IList<DeviceTypeOne> foo1Results = foo1.GetDeviceData(json); // calls GetDeviceData extension method
IList<DeviceTypeTwo> foo2Results = foo2.GetDeviceData(json); // calls GetDeviceData extension method
}
}
// implemented GetDeviceData as extension method of DeviceBase, instead of the abstract method within DeviceBase,
// it's slightly cleaner than the abstract method
public static class DeviceExtensions
{
public static IList<T> GetDeviceData<T>(this T device, string jsonResult) where T : DeviceBase
{
IEnumerable<T> deviceDataList = JsonConvert.DeserializeObject<IEnumerable<T>>(jsonResult);
IEnumerable<T> resultList = deviceDataList.Where(x => x.Type.Equals(typeof(T).Name));
return resultList.ToList();
}
}
// abstract base class only used to house common properties and control Type assignment
public abstract class DeviceBase : IDeviceData
{
protected DeviceBase(string type)
{
if(string.IsNullOrEmpty(type)) { throw new ArgumentNullException(nameof(type));}
Type = type; // type's value can only be set by classes that inherit and must be set at construction time
}
[JsonProperty("_id")]
public string Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("type")]
public string Type { get; private set;}
[JsonProperty("actions")]
public DeviceAction[] Actions { get; set; }
}
public class DeviceTypeOne : DeviceBase
{
public DeviceTypeOne() : base(nameof(DeviceTypeOne))
{
}
}
public class DeviceTypeTwo : DeviceBase
{
public DeviceTypeTwo() : base(nameof(DeviceTypeTwo))
{
}
}
// implemented GetAllDevices class as IDeviceData interface
public interface IDeviceData
{
string Id { get; set; }
string Name { get; set; }
string Type { get; }
DeviceAction[] Actions { get; set; }
}
// renamed and relocated class Action to DeviceAction
public class DeviceAction
{
public string Id { get; set; }
public DeviceActionDefinition DeviceActionDefinition { get; set; }
}
// renamed and relocated Action_Def to DeviceActionDefinition
public class DeviceActionDefinition
{
public string Id { get; set; }
public string Name { get; set; }
}
It should be simple enough to move the implementation of method GetDeviceData() to the base class.
For this to work, you will need to add a constraint on T so the compiler knows a bit more about the base type. You will also need to implement a constructor to populate the concrete type's type string you use around. This is a necessary measure to ensure the value is always populated as it is used for comparison in the method in question:
public abstract class HomeDevices<T> where T: GetAllDevices
{
public HomeDevices(string concreteType)
{
type = concreteType;
}
public string type { get; set; }
public string _id { get; set; }
public List<AllFoo1> lstfoo1 { get; set; }
public List<AllFoo2> lstfoo2 { get; set; }
//This method is now generic and works for both.
public List<T> GetDeviceData(string jsonResult)
{
var lst = Newtonsoft.Json.JsonConvert.DeserializeObject<List<T>>(jsonResult);
var lst1 = lst.Where(x => x.Type.Equals(type)).ToList();
return lst1;
}
}
I hope that helps.
I'm at loss here. I want to refactor a part of the code that uses no abstract classes. I'm familiar with json2csharp. That converts a JSON file to the C# classes so it can be easily deserialized.
Is there a similar site/tool that accepts as input several C# classes and generates basic abstract classes based on those?
This would make the refactoring easier as I don't need to create all the different abstract classes.
Very simple example:
Input:
public class TestClass1
{
public string TestID { get; set; }
public string TestName { get; set; }
public int TestValue1 { get; set; }
public TestClass1()
{
}
}
public class TestClass2
{
public string TestID { get; set; }
public string TestName { get; set; }
public int TestValue2 { get; set; }
public TestClass2()
{
}
}
Output:
public abstract class ATestClass
{
public string TestID { get; set; }
public string TestName { get; set; }
protected ATestClass()
{
}
}
You can get something working pretty quickly if you use the Roslyn code analysis and code generation. Here’s a quick example how that could work. Note that this is somewhat fragile with detecting common properties since its based on the syntax instead of the actual semantics (making string Foo and String Foo incompatible properties). But for code that is actually generated by another code generator, this should work fine since the input should be consistent.
var input = #"
public class TestClass1
{
public string TestID { get; set; }
public string TestName { get; set; }
public string OtherTest { get; set; }
public int TestValue1 { get; set; }
public TestClass1()
{
}
}
public class TestClass2
{
public string TestID { get; set; }
public string TestName { get; set; }
public int OtherTest { get; set; }
public int TestValue2 { get; set; }
public TestClass2()
{
}
}";
// parse input
var tree = CSharpSyntaxTree.ParseText(input);
// find class declarations and look up properties
var classes = tree.GetCompilationUnitRoot().ChildNodes()
.OfType<ClassDeclarationSyntax>()
.Select(cls => (declaration: cls, properties: cls.ChildNodes().OfType<PropertyDeclarationSyntax>().ToDictionary(pd => pd.Identifier.ValueText)))
.ToList();
// find common property names
var propertySets = classes.Select(x => new HashSet<string>(x.properties.Keys));
var commonPropertyNames = propertySets.First();
foreach (var propertySet in propertySets.Skip(1))
{
commonPropertyNames.IntersectWith(propertySet);
}
// verify that the property declarations are equivalent
var commonProperties = commonPropertyNames
.Select(name => (name, prop: classes[0].properties[name]))
.Where(cp =>
{
foreach (var cls in classes)
{
// this is not really a good way since this just syntactically compares the values
if (!cls.properties[cp.name].IsEquivalentTo(cp.prop))
return false;
}
return true;
}).ToList();
// start code generation
var workspace = new AdhocWorkspace();
var syntaxGenerator = SyntaxGenerator.GetGenerator(workspace, LanguageNames.CSharp);
// create base class with common properties
var baseClassDeclaration = syntaxGenerator.ClassDeclaration("BaseClass",
accessibility: Accessibility.Public,
modifiers: DeclarationModifiers.Abstract,
members: commonProperties.Select(cp => cp.prop));
var declarations = new List<SyntaxNode> { baseClassDeclaration };
// adjust input class declarations
commonPropertyNames = new HashSet<string>(commonProperties.Select(cp => cp.name));
foreach (var cls in classes)
{
var propertiesToRemove = cls.properties.Where(prop => commonPropertyNames.Contains(prop.Key)).Select(prop => prop.Value);
var declaration = cls.declaration.RemoveNodes(propertiesToRemove, SyntaxRemoveOptions.KeepNoTrivia);
declarations.Add(syntaxGenerator.AddBaseType(declaration, syntaxGenerator.IdentifierName("BaseClass")));
}
// create output
var compilationUnit = syntaxGenerator.CompilationUnit(declarations);
using (var writer = new StringWriter())
{
compilationUnit.NormalizeWhitespace().WriteTo(writer);
Console.WriteLine(writer.ToString());
}
This would generate the following output:
public abstract class BaseClass
{
public string TestID
{
get;
set;
}
public string TestName
{
get;
set;
}
}
public class TestClass1 : BaseClass
{
public string OtherTest
{
get;
set;
}
public int TestValue1
{
get;
set;
}
public TestClass1()
{
}
}
public class TestClass2 : BaseClass
{
public int OtherTest
{
get;
set;
}
public int TestValue2
{
get;
set;
}
public TestClass2()
{
}
}
I need help to see if AutoMapper can do this. My code has been simplified, but it gets the point across.
public class SourceObject
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
}
public class DestinationObject
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public string Property4 { get; set; }
public string Property5 { get; set; }
public string Property6 { get; set; }
}
var vm = new ViewModel
{
Objects = Mapper.Map<IList<SourceObject>, IEnumerable<DestinationObject>>(dests)
};
foreach (var destObj in vm.Objects)
{
Utility.SetupProperties(destObj, new AnotherDependency(), destObj.Property3,
someFlag, anotherFlag);
}
Property1 to Property3 are setup by AutoMapper currently. I then have to loop through the list of DestinationObjects to setup Property4 to Property6 with a function, some additional flags. I understand this might not be what AutoMapper is used for, but I really would like to avoid looping through the objects twice (once by AutoMapper, once on my own). The static SetupProperties function is used in other places, so I'd like to keep it as so. Can AutoMapper set this up? Thanks for any help in advance.
It really depends what is happening inside Utility.SetupProperties, but it is possible to have more complex mapping situations with a bit of logic by using the before and after mapping actions in Automapper : https://github.com/AutoMapper/AutoMapper/wiki/Before-and-after-map-actions
Mapper.Initialize(cfg => {
cfg.CreateMap<SourceObject, DestinationObject>()
.BeforeMap((src, dest) =>
{
//Do some work here and set properties.
})
});
Typically, you could use AfterMap and capture whatever additional parameters you wanted to pass in via a closure (as in the second wiki example). However, since you're converting between collections, I don't think there is a clean way to do that per item in this case.
I've done some digging, and I think you can use ITypeConverter<TSource, TDestination> to accomplish the conversion you're attempting.
I had to do some guesswork on implementation and use cases for Utility.SetupProperties, etc. but I think the following proof of concept Console App should demonstrate how to accomplish your custom conversion:
using System;
using System.Collections.Generic;
using AutoMapper;
namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
var sourceObjects = new SourceObject[] {
new SourceObject{Property1 = "prop 1A"},
new SourceObject{Property2 = "Prop 2B"},
new SourceObject{Property3 = "Prop 3C"}};
var someFlag = true;
var anotherFlag = false;
Mapper.Initialize(cfg =>
{
cfg.CreateMap<SourceObject, DestinationObject>().ConvertUsing<CustomConverter>();
});
var vm = new ViewModel
{
Objects = Mapper.Map<IList<SourceObject>, IEnumerable<DestinationObject>>(sourceObjects, opts =>
{
opts.Items.Add("AnotherDependency", new AnotherDependency { Foo = "bar" });
opts.Items.Add("flag1", someFlag);
opts.Items.Add("flag2", anotherFlag);
})
};
foreach (var obj in vm.Objects)
{
Console.WriteLine($"[{obj.Property1}, {obj.Property2}, {obj.Property3}, {obj.Property4}, {obj.Property5}, {obj.Property6}]");
}
}
}
public class CustomConverter : ITypeConverter<SourceObject, DestinationObject>
{
public DestinationObject Convert(ResolutionContext context)
{
return Convert(context.SourceValue as SourceObject, context);
}
public DestinationObject Convert(SourceObject source, ResolutionContext context)
{
var dest = new DestinationObject
{
Property1 = source?.Property1,
Property2 = source?.Property2,
Property3 = source?.Property3
};
var items = context.Options.Items;
Utility.SetupProperties(dest,
items["AnotherDependency"] as AnotherDependency,
dest.Property3,
items["flag1"] as bool? ?? false,
items["flag2"] as bool? ?? false);
return dest;
}
}
public static class Utility
{
public static void SetupProperties(DestinationObject x, AnotherDependency ad, string a, bool b, bool c)
{
x.Property4 = ad.Foo;
if (b || c)
{
x.Property5 = ad?.ToString() ?? a;
}
if (b && c)
{
x.Property6 = ad?.ToString() ?? a;
}
return;
}
}
public class ViewModel
{
public IEnumerable<DestinationObject> Objects { get; set; }
}
public class AnotherDependency { public string Foo { get; set; } }
public class SourceObject
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
}
public class DestinationObject
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public string Property4 { get; set; }
public string Property5 { get; set; }
public string Property6 { get; set; }
}
}
Let's assume I have three classes that are subclasses of a base class:
public class BaseClass
{
public string BaseName { get; set; }
}
public class Subclass1 : BaseClass
{
public string SubName1 { get; set; }
}
public class Subclass2 : BaseClass
{
public string SubName2 { get; set; }
}
public class Subclass3 : BaseClass
{
public string SubName3 { get; set; }
}
I would like to map these to a ViewModel class that looks like this:
public class ViewModel
{
public string BaseName { get; set; }
public string SubName1 { get; set; }
public string SubName2 { get; set; }
public string SubName3 { get; set; }
}
ViewModel simply combines the properties on all of the subclasses and flattens it. I tried to configure the mapping like so:
AutoMapper.CreateMap<BaseClass, ViewModel>();
Then I tried grabbing data from my database like so:
var items = Repo.GetAll<BaseClass>();
AutoMapper.Map(items, new List<ViewModel>());
However, what ends up happening is that only the BaseName property will be populated in the ViewModel. How would I configure AutoMapper so that it will map the properties in the subclasses as well?
There appears to be a bug or limitation in AutoMapper that you need corresponding TSource and TDestination hierarchies. Given:
public class BaseClass {
public string BaseName { get; set; }
}
public class Subclass1 : BaseClass {
public string SubName1 { get; set; }
}
You need the following view models:
public class ViewModel {
public string BaseName { get; set; }
}
public class ViewModel1 : ViewModel {
public string SubName1 { get; set; }
}
The following code then works:
Mapper.CreateMap<BaseClass, ViewModel>()
.Include<Subclass1, ViewModel1>();
Mapper.CreateMap<Subclass1, ViewModel1>();
var items = new List<BaseClass> {new Subclass1 {BaseName = "Base", SubName1 = "Sub1"}};
var viewModels = Mapper.Map(items, new List<ViewModel>());
Try this:
AutoMapper.CreateMap<BaseClass, ViewModel>()
.Include<Subclass1, ViewModel>()
.Include<Subclass2, ViewModel>()
.Include<Subclass3, ViewModel>();
AutoMapper.CreateMap<Subclass1, ViewModel>();
AutoMapper.CreateMap<Subclass2, ViewModel>();
AutoMapper.CreateMap<Subclass3, ViewModel>();
var items = Repo.GetAll<BaseClass>();
AutoMapper.Map(items, new List<ViewModel>());