Different parent classes map different subclasses problem - c#

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.

Related

C# get derived class propertiy

I've this base class that contains list of other classes
public class Blacklist
{
public int Id { get; set; }
public virtual IEnumerable<Card> Cards { get; set; }
}
Where Card class looks like
public class Card
{
public int Id { get; set; }
public string Cuid { get; set; }
public int BlacklistId { get; set; }
}
Then I have implemented a derived class that extends Blacklist class
public class BlacklistTwo : Blacklist
{
public new IEnumerable<CardTwo> Cards { get; set; }
}
where CardTwo class extends the base Card class
The problem occurs when I try to invoke a method that accept the base class as parameter with the derived instance. The type of outer class is alright but type of cards stays implemented as base class .
Example:
Insert(
new BlacklistTwo(){
Id = 1,
Cards = new List<CardsTwo>()
{ new CardTwo() { Id = 123123, Cuid = "123213", BlacklistId = 1}});
public void Insert(Blacklist blacklist)
{
blacklist.GetType(); // returns BlacklistTwo
blacklist.Cards.GetType(); // returns IEnumerable<Card> insted of IEnumerable<CardTwo>
}
It works when I set the parameter of method to dynamic but I would like to avoid it if possible.
As pointed out in comments - you don't actually override the property since you use the 'new' keyword. I think this may be what you are trying to achive:
public interface ICard
{
int CardId { get; set; }
string Cuid { get; set; }
int BlacklistId { get; set; }
//.. Other methods and properties
}
public class Card : ICard
{
public int CardId { get; set; }
public string Cuid { get; set; }
public int BlacklistId { get; set; }
}
public class CardTwo : ICard
{
public int CardId { get; set; }
public string Cuid { get; set; }
public int BlacklistId { get; set; }
}
public class Blacklist
{
public int Id { get; set; }
public virtual IEnumerable<ICard> Cards { get; set; }
}
public class BlacklistTwo : Blacklist
{
public override IEnumerable<ICard> Cards { get; set; }
}
And then:
public Test()
{
ICard card1 = new Card();
card1.CardId = 123123;
card1.Cuid = "123213";
card1.BlacklistId = 1;
ICard card2 = new CardTwo();
card2.CardId = 123123;
card2.Cuid = "123213";
card2.BlacklistId = 1;
Insert(new BlacklistTwo()
{
Id = 1,
Cards = new List<ICard>() { card1 ,card2 }
});
if (card1 is Card c1)
{
//Yes - this is a type of Card
}
if (card2 is CardTwo c2)
{
//Yes - this is a type of CardTwo
}
}
You could use an interface or an abstract class, and and probably even avoid extending the blacklist class

How do I create a generic List using abstract class?

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.

Trying to work out these interfaces

I'm trying to create some interfaces. The IReportSection object will have one string and a collection of items, which could be different depending on what we're working with. Do I need to make it generic?
The IReport will have one string and a collection of IReportSection.
Here's how I'm trying to define it now.
public interface IReport
{
string ReportName { get; set; }
ICollection<IReportSection> ReportSections { get; }
}
public interface IReportSection
{
string ReportSectionName { get; set; }
ICollection ReportItems { get; }
}
public abstract class ReportSectionBase : IReportSection
{
public string ReportSectionName { get; set; }
public ICollection ReportItems { get; set; }
}
And my models:
pulic class ProjectSubmissionViewModel
{
public int ProjectSubmissionId { get; set; }
public string SubmissionTitle { get; set; }
}
pulic class AffiliateViewModel
{
public int AffiliateId { get; set; }
public string AffiliateName { get; set; }
}
This is how I'm trying to use it in code:
public class ChapterAffiliates : ReportSectionBase
{
public string ReportSectionName { get { return "Chapter Affiliates"; } }
public ICollection<AffiliateViewModel> ReportItems { get; set; }
}
public class ChapterTitles : ReportSectionBase
{
public string ReportSectionName { get { return "Chapter Titles"; } }
public ICollection<ProjectSubmissionViewModel> ReportItems { get; set; }
}
public class SubmissionListViewModel : IReport
{
public ICollection<ProjectSubmissionViewModel> Submissions { get; set; }
public ICollection<AffiliateViewModel> Affiliates{ get; set; }
public string ReportName { get; set; }
public ICollection<IReportSection> ReportSections
{
get
{
var affiliateSection = new ChapterAffiliates
{
ReportItems = Affiliates
};
var titleSection = new ChapterTitles
{
ReportItems = Submissions.Where(s => s.SubmissionTitle.Contains("SomePhrase")).ToList()
};
var sections = new List<IReportSection> { {subSection}, {titleSection} };
return sections;
}
}
}
I'm not sure how to best define this. I'm pretty sure I've done it before, but it's not coming to me.
Are the type parameters for TRType all the same within a certain report? E.g. will you have report sections with different report types in them?
If all types within a report are the same, the solution is relatively simple:
public interface IReport<T> { ... }
If this is not the case - you'll have to do something different, e.g:
public interface IReportSection
{
string ReportSectionName { get; }
ICollection ReportItems { get; }
}
public abstract class ReportSectionBase<TRType> : IReportSection {
...
}
This allows you to put different underlying types in the ReportSections collection related to the report. You'll have to do some more work to get the exact information that you need out of each report section.

Creating base Attribute class with a generic property 'value'

What I've done is created a base class of 'Attribute' in C#. From there I created other classes which inhert Attribute and add any additional properties as necessary. However when I try to create my observable collection which contains all these various attributes I get an underline here
private ObservableCollection<Attribute> _attributes;
under 'Attribute' saying: Using the generic type 'Attribute< TValue >' requires one type arguments. The reason for the base class of Attribute is so I can create multiple attributes as seen below.
Attribute Class
using System.Collections.Generic;
namespace ExampleTool.Model
{
public class Attribute<TValue>
{
public string Key { get; set; }
public TValue Value { get; set; }
}
public class FloatAttr : Attribute<float>
{
public string Label { get; set; }
private float minValue { get; set; }
private float maxValue { get; set; }
}
public class IntAttr : Attribute<int>
{
public string Label { get; set; }
private float minValue { get; set; }
private float maxValue { get; set; }
}
public class StringAttr : Attribute<string>
{
public string Label { get; set; }
}
public class BoolAttr : Attribute<bool>
{
public string Label { get; set; }
}
public class ListStringAttr : List<string>
{
public string Label { get; set; }
}
}
ViewModel - where error occurs...
using System.Collections.Generic;
using System.Collections.ObjectModel;
using ExampleTool.Model;
using ExampleTool.Helper;
namespace ExampleTool.ViewModel
{
public class AttributeViewModel : ObservableObject
{
private ObservableCollection<Attribute> _attributes;
public ObservableCollection<Attribute> Attributes
{
get { return _attributes; }
set
{
_attributes = value;
NotifyPropertyChanged("Attributes");
}
}
public AttributeViewModel()
{
//hard coded data for testing
Attributes = new ObservableCollection<Attribute>();
FloatAttr floatAttr = new FloatAttr();
Attributes.Add(floatAttr);
IntAttr intAttr = new IntAttr();
Attributes.Add(intAttr);
StringAttr stringAttr = new StringAttr();
Attributes.Add(stringAttr);
BoolAttr boolAttr = new BoolAttr();
Attributes.Add(boolAttr);
ListStringAttr listStringAttr = new ListStringAttr();
Attributes.Add(listStringAttr);
}
}
}
Solution idea #1
- simply remove the property of value from the base class and define it in each sub class.
public class Attribute
{
public string Key { get; set; }
}
public class FloatAttr : Attribute
{
public float Value { get; set; }
public string Label { get; set; }
private float minValue { get; set; }
private float maxValue { get; set; }
}
public class IntAttr : Attribute
{
public int Value { get; set; }
public string Label { get; set; }
private float minValue { get; set; }
private float maxValue { get; set; }
}
public class StringAttr : Attribute
{
public string Value { get; set; }
public string Label { get; set; }
}
public class BoolAttr : Attribute
{
public bool Value { get; set; }
public string Label { get; set; }
}
public class ListStringAttr : Attribute
{
public List<string> Value { get; set; }
public string Label { get; set; }
}
Your base Attribute class is a generic type, then you must add type argument to it's usage. But you can't add just T:
private ObservableCollection<Attribute<T>> _attributes;
because T is not your type parameter. You should add new non-generic base class:
public class AttributeBase
{
public string Key { get; set; }
}
public class Attribute<TValue> : AttributeBase
{
public TValue Value { get; set; }
}
And implement AttributeRetriever like in this question:
public Attribute<T> GetAttribute<T>() where T: DatabaseItem, new()
{
return _attributes.OfType(typeof(Attribute<T>)).FirstOrDefault as Attribute<T>;
}
Good news are that your WPF View can works fine without type parameter because Binding uses reflection. Then if you no need to have an access to your properties in code you no need to implement retriever too.

Mapping foreign keys of subclasses

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);

Categories

Resources