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>());
Related
Profile.cs
public class TestConfigProfile : Profile
{
public TestConfigProfile()
{
CreateMap<BaseBO, BaseVO>();
CreateMap<A_BO, A_VO>();
CreateMap<SubBO1, SubVO1>();
}
public class A_BO
{
public BaseBO Sub { get; set; }
}
public class A_VO
{
public BaseVO Sub { get; set; }
}
public class BaseBO
{
public int Id { get; set; }
public string Name { get; set; }
}
public class BaseVO
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SubBO1 : BaseBO
{
public int Size { get; set; }
}
public class SubVO1 : BaseVO
{
public int Size { get; set; }
}
}
test code like this...
public void TestConvert()
{
TestConfigProfile.A_BO bo = new TestConfigProfile.A_BO();
bo.Sub = new TestConfigProfile.SubBO1()
{
Id = 1,
Name = "SubBO1",
Size = 4421
};
TestConfigProfile.A_VO vo = _mapper.Map<TestConfigProfile.A_BO, TestConfigProfile.A_VO>(bo);
}
The result is as follows, but it does not meet my expectations, how can I configure this? Also I don't want to use a parent class.
Successfully mapped to a subclass.
With AutoMapper, mapping inheritance is opt-in.
Therefore, when you map from BaseBO to BaseVO, you need to include the derived mappings.
public TestConfigProfile()
{
CreateMap<BaseBO, BaseVO>()
.Include<SubBO1, SubVO1>(); // Include necessary derived mappings
CreateMap<A_BO, A_VO>();
CreateMap<SubBO1, SubVO1>();
}
See this working example.
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.
Not really sure how to explain it in a one-liner but here's my situation.
I have an abstract base class called "Deal" which has a property in it which is of a class "Inventory"
Now a deal can be either Cash, Finance, Lease, Wholesale or Rent-To-Own which is why it's of type Deal. It's also abstract as each of the deal types have some properties that don't apply to other but they all have a set of shared so the various deal types (i.e. - Finance, Lease, etc.) inherit from the base class Deal and have their own added methods and properties.
The same is true with another abstract class of "Inventory". Inventory can be things like car, truck, boat, RV, etc.) so therefore I have a class of Inventory.
Here's the issue:
A deal must contain a unit in inventory. For example, you finance a car or you lease an RV then you have a Deal with an inventory property of type Inventory or whatever the case may be.
When I create the Deal class and I add a property to it like SoldUnit as type Inventory, the T is inherited from the Deal class. How can I create a generic property of type Inventory inside of the Deal class?
Deal won't really work because of the number of possibilities, so how could I make a generic "SoldUnit" property which references an Inventory class inside of the Deal class? And while maintaining strict typing? I know I could make a property called SoldUnit with a type of object or dynamic, but is there a better way?
Thanks!
EDIT:
Sorry, here is an excerpt of the code: It doesn't work because type T is already a DealType, but it's the two properties [ ret.Vehicle = new Car(); and ret.SoldUnit = new Car(); ] that I'm trying to populate with a specific inventory type (car, truck, rv, boat, etc.) in the Get() method. The specific inventory type is determined by a flag in the database field.
Thanks again!
public abstract class Deal<T>
{
public string Uid { get; set; } = Guid.NewGuid().ToString();
public DealTypes DealType { get; set; } = DealTypes.Finance;
public Buyer Customer { get; set; }
public Inventory<T> Vehicle { get; set; }
public Location Location { get; set; }
public Inventory<T> SoldUnit { get; set; }
public abstract T Add(string id);
public abstract T Update(string id);
public abstract T Get(string id);
}
public abstract class Inventory<T>
{
public string Uid { get; internal set; } = Guid.NewGuid().ToString();
public Location Location { get; set; }
public string StockNumber { get; set; } = "";
}
public class Cash : Deal<Cash>
{
public decimal TradeAllowance { get; set; }
public decimal TradePayoff { get; set; }
public decimal TotalDown { get; set; }
public decimal DueOnDelivery { get; set; }
public override Cash Get(string id)
{
var ret = new Cash();
ret.Customer = new Buyer();
ret.Vehicle = new Car();
ret.Location = new Location();
ret.SoldUnit = new Car();
}
}
public class Car : Inventory<Car>
{
public decimal GrossWeight { get; set; }
public decimal NetWeight { get; set; }
public string Branded { get; set; } = "";
public string FuelType { get; set; } = "";
public string Trim { get; set; } = "";
public string Body { get; set; } = "";
public int Cylinders { get; set; }
public decimal EngineSize { get; set; }
public string TransmissionType { get; set; } = "";
public string Suspension { get; set; } = "";
public string InteriorColor { get; set; } = "";
public bool OdometerExceedsLimit { get; set; }
public bool OdometerTrueMileageUnknown { get; set; }
public bool OdometerFiveDigit { get; set; }
public string TitleNumber { get; set; } = "";
public DateTime? TitleDate { get; set; }
public string TitleState { get; set; } = "";
public DateTime? TitleDue { get; set; }
public DateTime? TitleReceived { get; set; }
}
I've simplified your code down somewhat to the necessary elements to make this work, but you do need to have your base classes defined like Deal<D, I> where D : Deal<D, I> where I : Inventory<D, I> & Inventory<D, I> where D : Deal<D, I> where I : Inventory<D, I> respectively.
Your code would then look like this:
public abstract class Deal<D, I> where D : Deal<D, I> where I : Inventory<D, I>
{
public I Vehicle { get; set; }
public abstract D Get(string id);
}
public abstract class Inventory<D, I> where D : Deal<D, I> where I : Inventory<D, I>
{ }
public class Cash : Deal<Cash, Car>
{
public override Cash Get(string id)
{
return new Cash() { Vehicle = new Car() };
}
}
public class Car : Inventory<Cash, Car>
{ }
This is what you need to do to have a strongly-typed Vehicle property and Get (etc) method.
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
I have following abstract class:
public abstract class ClauseComponent
{
public int ClauseComponentId { get; set; }
public abstract string[] Determinate(ClimateChart chart);
public abstract List<ClauseComponent> GiveCorrectPath(ClimateChart chart);
public abstract String GetHtmlCode(Boolean isYes);
public virtual void Add(Boolean soort, ClauseComponent component)
{
throw new ApplicationException();
}
public ClauseComponent()
{
}
}
The Clause class inherits from the abstract class:
public class Clause : ClauseComponent
{
public virtual ClauseComponent YesClause { get; set; }
public virtual ClauseComponent NoClause { get; set; }
public String Name { get; private set; }
public virtual Parameter Par1 { get; set; }
public virtual Parameter Par2 { get; set; }
public int Waarde { get; set; }
public String Operator { get; set; }
public Clause()
{
}
public Clause(String name, Parameter par1, String op, int waarde)
{
this.Name = name;
this.Par1 = par1;
this.Operator = op;
this.Waarde = waarde;
}
public Clause(String name, Parameter par1, Parameter par2)
{
this.Name = name;
this.Par1 = par1;
this.Par2 = par2;
}
}
This is the mapper of the abstract class (I dont have a mapper for the subclass):
public ClauseComponentsMapper()
{
ToTable("ClauseComponents");
// Primary key
HasKey(c => c.ClauseComponentId);
// Properties
Property(c => c.ClauseComponentId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
I have this in my DB:
Now I want to give a proper name to the mapping, how can I accomplish this?
I have never done the mapping on abstract classes and subclasses so I'm a little bit in the blue here.
One way is to create properties for the mapping columns, and in the mapping class, map the virtual property using the mapping column property.
E.g.
public class Clause : ClauseComponent
{
public int MyCustomPar1Id{ get; set; }
[ForeignKey("MyCustomPar1Id")]
public virtual Parameter Par1 { get; set; }
}
Or Fluent Api:
modelBuilder.Entity<Clause >().HasRequired(p => p.Par1 ) // Or Optional
.HasForeignKey(p => p.MyCustomPar1Id);