Automapping a destination collection property with a private set - c#

public sealed class ParentSource
{
public string Name { get; }
public List<ChildSource> Children { get; }
public ParentSource(string name)
{
Name = name;
Children = new List<ChildSource>();
}
}
public sealed class ChildSource
{
public string Name { get; }
public ChildSource(string name)
{
Name = name;
}
}
public sealed class ParentDestination
{
public string Name { get; }
public List<ChildDestination> Children { get; }
public ParentDestination(string name)
{
Name = name;
Children = new List<ChildDestination>();
}
}
public sealed class ChildDestination
{
public string Name { get; }
public ChildDestination(string name)
{
Name = name;
}
}
To my understanding, all I need are the following mappings:
CreateMap<ParentSource, ParentDestination>();
CreateMap<ChildSource, ChildDestination>();
Call the mapping:
var parentSource = new ParentSource("parent");
var childSource = new ChildSource("child");
parentSource.Children.Add(childSource);
var parentDestination = mapper.Map<ParentSource, ParentDestination>(parentSource);
The child collection of the destination parent doesn't contain any members unless I add a public setter to the Children property. How can I make this work without a public setter?

Related

Mapster ConstructUsing with class with multiple ctor problem (bug?)

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)

List in interface

I have interface where I am setting a list and trying to fill the list later on, however I am getting null exception in the LoadSet() method.
public interface ISettings
{
List<CustomSetting> CustomSettings { get; set; }
}
public class SettingsService : ISettings
{
CameraResolution cameraResolution = new CameraResolution();
public string Name { get; set; }
public List<CustomSetting> CustomSettings { get; set; }
public SettingsService()
{
Name = customSettings.Name;
CustomSettings = new List<CustomSetting>();
LoadSet();
}
public void LoadSet()
{
var detail = new CustomSetting
{
Name = cameraResolution.Name,
Value = cameraResolution.Value,
};
CustomSettings.Add(detail);
}
}

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

Ignore Mapping of inner list property in AutoMapper --EF6,C#

I have a collection property of DTO like this
public ICollection<Applicant> Applicants{get;set;}
Applicant Model
public class Applicant
{
public int Id{get;set;}
public string name{get;set;}
public ICollection<ApplicantSkillsVM> ApplicantSkills { get; set; }
}
public class ApplicantSkillsVM
{
public int Id {get;set;}
public Skill skill{get;set;}
}
I want to map my List<iApplicant> DTO to entity given that I want to take ApplicantSkillsVM but ignore skill inside ApplicantSkillsVM.
I have a model which is list List<Applicant> and that contains another list List<ApplicantSkillsVM> and ApplicantSkillsVM has a property skill. I want to ignore this (skill) while mapping. Its simple.
How can I do this in latest the AutoMapper version with EF6?
Here a running sample:
internal class Program
{
#region Methods
private static void Main(string[] args)
{
// Configure the mappings
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ApplicantSkillVM, ApplicantSkill>().ForMember(x => x.Skill, x => x.Ignore()).ReverseMap();
cfg.CreateMap<ApplicantVM, Applicant>().ReverseMap();
});
var config = new MapperConfiguration(cfg => cfg.CreateMissingTypeMaps = true);
var mapper = config.CreateMapper();
ApplicantVM ap = new ApplicantVM
{
Name = "its me",
ApplicantSkills = new List<ApplicantSkillVM>
{
new ApplicantSkillVM {SomeInt = 10, SomeString = "test", Skill = new Skill {SomeInt = 20}},
new ApplicantSkillVM {SomeInt = 10, SomeString = "test"}
}
};
List<ApplicantVM> applicantVms = new List<ApplicantVM> {ap};
// Map
List<Applicant> apcants = Mapper.Map<List<ApplicantVM>, List<Applicant>>(applicantVms);
}
#endregion
}
/// Your source classes
public class Applicant
{
#region Properties
public List<ApplicantSkill> ApplicantSkills { get; set; }
public string Name { get; set; }
#endregion
}
public class ApplicantSkill
{
#region Properties
public Skill Skill { get; set; }
public int SomeInt { get; set; }
public string SomeString { get; set; }
#endregion
}
// Your VM classes
public class ApplicantVM
{
#region Properties
public List<ApplicantSkillVM> ApplicantSkills { get; set; }
public string Description { get; set; }
public string Name { get; set; }
#endregion
}
public class ApplicantSkillVM
{
#region Properties
public Skill Skill { get; set; }
public int SomeInt { get; set; }
public string SomeString { get; set; }
#endregion
}
public class Skill
{
#region Properties
public int SomeInt { get; set; }
#endregion
}
}
Initially my model ApplicantSkillsVM didnt have reference Id for Skill which should be nullable
So my model had to look like
public class ApplicantSkillsVM{
public int Id {get;set;}
public int? skillId{get;set;} //updated property
public Skill skill{get;set;}
}
The problem resolved

Extend object with values from parent

Let's say I've got a class:
public class Parent
{
public string Name { get; set; }
public string City { get; set; }
}
and in some function I'm getting the list of objects type Parent, next I'd like to extend those objects with new field with some value, so I'm declaring an extended class like this:
public class Child : Parent
{
public Child(Parent parent)
{
Name = parent.Name;
City = parent.City;
}
public int Age { get; set; }
}
and call the costructor for each extended object. Is there a better way to do that? What if there will be multiple properties in Parent? Maybe there is some more elegant way to achieve that?
I think maybe you're looking for a copy-constructor pattern. Each level defines a protected constructor which copies the relevant properties:
public class Parent
{
public string Name { get; set; }
public string City { get; set; }
//normal constructor
public Parent()
{
}
protected Parent(Parent copy)
{
this.Name = copy.Name;
this.City = copy.City;
}
}
The Child would inherit from Parent, pass it down through to the copy-constructor, then append its new values as desired:
public class Child : Parent
{
public string NewInfo { get; set; }
public Child(Parent copy)
: base(copy)
{
}
}
Usage might look like:
Parent parent = new Parent() { Name = "Name", City = "StackOverflow"};
Child child = new Child(parent) { NewInfo = "Something new!" };
Console.WriteLine(child.Name); //Name
Console.WriteLine(child.City); //StackOverflow
Console.WriteLine(child.NewInfo); //Something new!
The benefit from this is that you can have multiple levels of inheritance with each level managing their own properties.
EDIT: Given your most recent comment:
The motivation to this question is a situation in where I'm getting a
list of objects with data, and want to show this data but with some
additional fields, without touching the base class.
Perhaps the better method then is to wrap the base class:
public class Child
{
private readonly Parent WrappedParent;
public string NewInfo { get; set; }
public string Name
{
get { return WrappedParent.Name; }
set { WrappedParent.Name = value; }
}
public string City
{
get { return WrappedParent.City; }
set { WrappedParent.City = value; }
}
public Child(Parent wrappedParent)
{
this.WrappedParent = wrappedParent;
}
}
Downside is you have to redeclare each property, and you are no longer inheriting (cannot be considered a) "Parent", but then you are definitly "not touching" the base class anymore. Could move the "Parent" properties into an IParent interface if that's better for you, but doing so again is "touching" the base class as you'll have to add the IParent interface declaration to its class definition.
Not sure if I got you wrong, but this could be a more standar solution
public class Parent
{
public Parent(string name, string city)
{
Name = name;
City = city;
}
public string Name { get; set; }
public string City { get; set; }
}
public class Child : Parent
{
public Child(string name, string city, int age) : base(name, city)
{
Age = age;
}
public int Age { get; set; }
}
You can do this
public class Parent
{
public string Name { get; set; }
public string City { get; set; }
public Parent(string name, string city)
{
this.Name = name;
this.City = city;
}
public Parent():this(string.Empty, string.Empty)
{
}
}
public class Child : Parent
{
public Child(Parent parent, int age):base(parent.Name, parent.City)
{
this.Age = age;
}
public int Age { get; set; }
}

Categories

Resources