Mapster ConstructUsing with class with multiple ctor problem (bug?) - c#

I have a problem with Mapster. There is some classes
public class DtoClass
{
public string Name { get; set; }
public ParamValueDto Value { get; set; }
}
public class ParamValueDto
{
public int Value { get; set; }
}
public class TargetClass
{
public string Name { get; set; }
public object Value { get; set; }
public TargetClass(string name, object value)
{
Name = name;
Value = value;
}
}
public class TargetParamValue
{
public int Value { get; set; }
public TargetParamValue(int value)
{
Value = value;
}
}
Config:
config.ForType<DtoClass, TargetClass>()
.MapToConstructor(true)
.ConstructUsing(x => new TargetClass(x.Name, new TargetParamValue(x.Value)));
Everything is fine, the objects are mapped as expected, but if you add one more constructor to the TargetClass
public class TargetClass
{
public string Name { get; set; }
public object Value { get; set; }
public TargetClass(string name, object value)
{
Name = name;
Value = value;
}
public TargetClass(Foo foo) //Where Foo is any class
{
Name = "fsdf";
Value = new ParamValueDto();
}
}
In the final result, Value will not contain TargetParamValue as expected, but ParamValueDto, as if result ConstructUsing is ignored (but called) or overwritten. The constructor is called correctly - and TargetParamValue comes to value, but the final result will have an incorrect param (with ParamValueDto in Value).
(New ctor - not called)

Related

Project a name-value list into an object

I want to be able to save an arbitrary flat object into the name-value list.
public class NameValueListEntity
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[InverseProperty(nameof(NameValueListContentEntity.Entity))]
public ICollection<NameValueListContentEntity> Content { get; set; }
}
public class NameValueListContent
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("entity_fk")]
public NameValueListEntity Entity { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
public class ObjectToSave
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
}
I could use reflection to manually assemble/parse the list, but it will create a lot of overhead. Lots of NameValueListContent objects will be needlessly created both during the saving and the reading. Could it somehow be omitted? Especially during the reading, which is very performance-sensitive in my case.
Assume you have a AppDbContext class that holds your NameValueListContent class objects named as NVListContents. You can read and write the name-value list of objects by doing the following:
public class AppDbContext : DbContext
{
public DbSet<NameValueListContent> NVListContents { get; set; }
public AppDbContext()
: base()
{ }
}
public class SomeClass
{
private AppDbContext context { get; set; }
public SomeClass(AppDbContext _context)
{
context = _context;
}
public List<ObjectToSave> ReadObjects()
{
return context.NVListContents
.Select(nvlc => new ObjectToSave { Prop1 = nvlc.Name, Prop2 = nvlc.Value
}).ToList();
}
public bool WriteObjects(int id, string name, string value)
{
var query = context.NVListContents
.FirstOrDefault(nvlc => nvlc.Id == id);
if(query != null)
{
query.Name = name;
query.Value = value;
context.Update(query);
context.SaveChanges();
return true;
}
else
{
return false;
}
}
}
Hope, this answers to your question.

How to access a property in another class?

I am working with a WPF .Net Core 3 project.
In my UnbalancedViewModel I need to access an ID from another class (TestRunDto.cs).
UnbalancedViewModel
public class UnbalancedViewModel : ViewModelBase, IUnbalancedViewModel
{
private TestRunApi _testRunApi;
public UnbalancedViewModel(TestRunApi testRunApi, INotificationManager notifications)
{
_testRunApi = testRunApi;
}
private void StartTestRunJobExecuted(object obj)
{
_testRunApi.StartTestRun(1); ////I need the Id from TestRunDto (TestRunDto.Id)
}
}
TestRunApi
public async Task<TestRunLiveValueDto> GetTestRunLiveValue(int jobRunId)
{
await using var dbContext = new AldebaDbContext(_connectionString);
return await TestRunInteractor.GetTestRunLiveValue(jobRunId, dbContext);
}
public async Task StartTestRun(int testRunId)
{
await using var dbContext = new AldebaDbContext(_connectionString);
await TestRunInteractor.StartTestRun(dbContext, testRunId);
}
TestRunLiveValueDto
public class TestRunLiveValueDto
{
public TestRunDto TestRun { get; }
public bool ShowInstantaneousValue { get; set; }
public bool EnableStart { get; set; }
public bool EnableStop { get; set; }
public bool EnableMeasure { get; set; }
public int RecipeRpm { get; }
public string ActualRecipeName { get; }
public int DefaultSetOfPlaneId { get; }
public ICollection<BalancePlaneDto> ListBalancePlane { get; }
public ICollection<SetOfPlaneDto> ListSetOfPlane { get; }
public ICollection<SensorVibrationDto> SensorVibrations { get; set; }
public ICollection<EstimationDto> InstantaneousValues { get; set; }
public ICollection<EstimationDto> EstimationsValues { get; set; }
private TestRunLiveValueDto(TestRunDto testRun, bool enableStart, bool enableStop, int recipeRpm, ICollection<SensorVibrationDto> sensorVibrations)
{
EnableStart = enableStart;
EnableStop = enableStop;
TestRun = testRun;
RecipeRpm = recipeRpm;
SensorVibrations = sensorVibrations;
}
public static TestRunLiveValueDto Create(TestRunDto testRun, bool enableStart, bool enableStop, int recipeRpm, ICollection<SensorVibrationDto> sensorVibrations)
=> new TestRunLiveValueDto(testRun, enableStart, enableStop, recipeRpm, sensorVibrations);
}
TestRunDto
public class TestRunDto
{
public int Id { get; set; }
public int JobRunId { get; set; }
public string Name { get; set; }
public int TestRunNumber { get; set; }
public RunState State { get; set; }
public ICollection<BalancePlaneDto> BalancePlanes { get; set; } // Todo remove
private TestRunDto(int id, int jobRunId, RunState state, string name, int testRunNumber)
{
Id = id;
JobRunId = jobRunId;
Name = name;
TestRunNumber = testRunNumber;
State = state;
}
public static TestRunDto Create(int id, int jobRunId, RunState state, string name, int testRunNumber)
=> new TestRunDto(id, jobRunId, state, name, testRunNumber);
}
I have been trying to understand this, but I can not get a hold of the proper method to do this. Do I first declare a new TestRunDto class in my viewmodel or am I supposed to access it some other way?
You need to ensure class A has a reference to an instance of class B to access the properties, for example one way of doing this is to pass class A to B in a method where you can manipulate or access properties.
public class FooA
{
public string PropertyA { get; set; }
}
public class FooB
{
public string PropertyB { get; set; }
public void CanAccessFooA(FooA a)
{
a.PropertyA = "See, I can access this here";
}
}
Another is to pass class A to B in the constructor (known as dependency-injection)
public class FooB
{
FooA _a;
public FooB(FooA a)
{
// Pass instance of FooA to constructor
// (inject dependency) and store as a member variable
this._a = a;
}
public string PropertB { get; set; }
public void CanAccessFooA()
{
if (this._a != null)
this._a.PropertyA = "See, I can access this here";
}
}
Exactly how to structure your code is up to you, but the principle remains the same: Class B can only access Class A if it has a reference to an instance of it.
Look into 'Dependency Injection' as there are many techniques to achieve this.
Edit
One such technique might be abstracting the code to provide the ID to both, like so
public class IdProvider
{
public int Id { get; set; }
}
public class FooA
{
private int _id;
public FooA(IdProvider idProvider)
{
_id = idProvider.Id;
}
}
public class FooB
{
private int _id;
public FooB(IdProvider idProvider)
{
_id = idProvider.Id;
}
}
Now both classes have the same ID;
StartTestRun takes the tesRunId as it's parameter.
public async Task StartTestRun(int testRunId)
{
I think you need to call StartTestRunJobExecuted with this testRunId.
You will to change
private void StartTestRunJobExecuted(object obj)
to
private void StartTestRunJobExecuted(int testRunIdn)
{
_testRunApi.StartTestRun(testRunId); ////I need the Id from TestRunDto (TestRunDto.Id)
}
(This based on me guessing).

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.

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.

how to recursively call a generic method analyzing properties

I'm creating a method that will analyze an instance of a class that I have created, checking each of the properties on that class for string types and then checking if those string properties are null or empty.
Code:
public class RootClass
{
public string RootString1 { get; set; }
public string RootString2 { get; set; }
public int RootInt1 { get; set; }
public Level1ChildClass1 RootLevel1ChildClass11 { get; set; }
public Level1ChildClass1 RootLevel1ChildClass12 { get; set; }
public Level1ChildClass2 RootLevel1ChildClass21 { get; set; }
}
public class Level1ChildClass1
{
public string Level1String1 { get; set; }
public string Level1String2 { get; set; }
public int Level1Int1 { get; set; }
}
public class Level1ChildClass2
{
public string Level1String1 { get; set; }
public string Level1String2 { get; set; }
public int Level1Int1 { get; set; }
public Level2ChildClass1 Level1Level2ChildClass11 { get; set; }
public Level2ChildClass1 Level1Level2ChildClass12 { get; set; }
public Level2ChildClass2 Level1Level2ChildClass22 { get; set; }
}
public class Level2ChildClass1
{
public string Level2String1 { get; set; }
public string Level2String2 { get; set; }
public int Level2Int1 { get; set; }
}
public class Level2ChildClass2
{
public string Level2String1 { get; set; }
public string Level2String2 { get; set; }
public int Level2Int1 { get; set; }
}
Not all the properties on the class are strings, some of them are instances of other classes, which have their own properties, which also need to be analyzed the same way. Basically, the method will return true if any of the properties are strings with a value on the RootClass or anywhere on sub-levels of the class (for example, if RootLevel1ChildClass11 has a string property with a value).
Here's what I have so far:
public static bool ObjectHasStringData<T>(this T obj)
{
var properties = typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var property in properties)
{
Type propertyType = property.PropertyType;
if (propertyType == typeof(string))
{
try
{
if (!String.IsNullOrEmpty(property.GetValue(obj, null) as string))
return true;
}
catch (NullReferenceException) { } // we want to ignore NullReferenceExceptions
}
else if (!propertyType.IsValueType)
{
try
{
if (ObjectHasStringData(property.GetValue(obj, null)))
return true;
}
catch (NullReferenceException) { } // we want to ignore NullReferenceExceptions
}
}
return false;
}
this works great on the first layer (so any string within the RootClass), but once I start using it recursively on the if (ObjectHasStringData(property.GetValue(obj, null))) line, the return value of property.GetValue() is object, so when calling the method recursively, T is object.
I can get the Type of the current object, but how do I convert the object returned from property.GetValue() to the actual type of the property?
I'd suggest not making that a generic method, just have it accept any object, and use GetType to get the type (unless it's null). The generics doesn't really seem to add anything of value here.
So, remove the type parameter, use obj.GetType(), and don't recurse if the object is null!
Also, (propertyType)obj) won't work, and if it would it would have no use. Casting is only for type safety and determining (at compile time) how to interact with an object. To System.Reflection it doesn't make any difference.
public static bool ObjectHasStringData( this object obj )
{
if( obj == null )
return false;
var properties = obj.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var property in properties)
...
}

Categories

Resources