how can I get subclass properties and set the value? - c#

I m trying to get all properties from a sub class and then set the value.
but I dont not know how I can start. I know how I can get the properties and set the value. but no on a sub class.
public class Program {
public static void Main(string[] args) {
object[] Response = new object[] {
new Cars() {
Details = new Details() {
CanDrive = true,
ID = 123,
IsPolice = true
},
Name = "test",
Speed = 100
}
};
var Result = Test <Cars> (Response);
Console.WriteLine(Result.Speed);
}
private static T Test <T> (object[] Obj) {
var Instance = CreateInstance <T> ();
foreach(var Ob in Obj) {
PropertyInfo[] C = Ob.GetType().GetProperties();
foreach(var n in C) {
PropertyInfo[] P = typeof(T).GetProperties();
foreach(PropertyInfo Val in P) {
if (n.Name == Val.Name) V
Val.SetValue(Instance, n.GetValue(Ob));
}
}
}
return Instance;
}
private static T CreateInstance <T>() => Activator.CreateInstance<T>();
public class Cars {
public int Speed { get; set; }
public string Name { get; set; }
public Details Details { get; set; }
}
public class Details {
public int ID { get; set; }
public bool CanDrive { get; set;}
public bool IsPolice { get; set; }
}
}
how can I get the sub classes? and set the value of the class?
Edit
Updated my Code. for better understanding.

I'm confused why we're going all the way up to reflection for this?
Could this just be as simple as
var car = new Car();
car.Details = new Details()
{
CanDrive = true,
ID = 42,
IsPolice = false
};

public class Program
{
public static void Main(string[] args)
{
var CarInstance = CreateInstance<Cars>();
PropertyInfo[] P = typeof(Cars).GetProperties();
foreach (PropertyInfo Car in P)
{
if(Car.Details.IsPolice) //Access properties of subclass using the name of the property, which is "Details"
Car.SetValue(CarInstance, 12345);
if(Car.Name == "ID")
Car.SetValue(CarInstance, 12345);
}
}
private static T CreateInstance<T>() => Activator.CreateInstance<T>();
public class Cars
{
public int Speed { get; set; }
public string Name { get; set; }
public Details Details { get; set; }
}
public class Details
{
public int ID { get; set; }
public bool CanDrive { get; set; }
public bool IsPolice { get; set; }
}
}
This minor difference may help clear up some confusion:
public class Program
{
public static void Main(string[] args)
{
var CarInstance = CreateInstance<Cars>();
PropertyInfo[] P = typeof(Cars).GetProperties();
foreach (PropertyInfo Car in P)
{
if(Car.ThisCarDetails.IsPolice) //Access properties of subclass using the name of the property, which is "ThisCarDetails"
Car.SetValue(CarInstance, 12345);
if(Car.Name == "ID")
Car.SetValue(CarInstance, 12345);
}
}
private static T CreateInstance<T>() => Activator.CreateInstance<T>();
public class Cars
{
public int Speed { get; set; }
public string Name { get; set; }
public Details ThisCarDetails { get; set; }
}
public class Details
{
public int ID { get; set; }
public bool CanDrive { get; set; }
public bool IsPolice { get; set; }
}
}

Related

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

Deserialize json response to multiple types dynamically using json.net

I have a simple API which result in different types of config for different tasks, So I am looking to find a way to parse the response dynamically using if/else. But for some reason, the config is not deserialized because that's a string already and Serialize() do it again.
So, the JsonConvert.DeserializeObject<typeAhandler>(jsonString) doesn't work then.
Here is my code :
void Main()
{
var json = new ApiResponse {name = "name1", type = "a", config = "{'title' : 'hello world!', 'id' : 1}"};
var jsonString = JsonConvert.SerializeObject(json);
Console.WriteLine(jsonString);
if (json.type == "a")
{
var handler = JsonConvert.DeserializeObject<typeAhandler>(jsonString);
handler.Dump();
}
else if(json.type == "b")
{
var handler = JsonConvert.DeserializeObject<typeBhandler>(jsonString);
handler.Dump();
}
}
public class ApiResponse
{
public string name { get; set; }
public string type {get; set;}
public string config {get;set;}
}
// Type A Handler
public class typeAhandler
{
public string name { get; set; }
public typeAconfig config { get; set; }
}
public class typeAconfig
{
public string title { get; set; }
public int id { get; set; }
}
// Type B Handler
public class typeBhandler
{
public string name { get; set; }
public typeBconfig config { get; set; }
}
public class typeBconfig
{
public string[] items { get; set; }
}
class Program
{
static void Main(string[] args)
{
var json = new ApiResponse { Name = "name1", Type = "a", Config = "{'title' : 'hello world!', 'id' : 1}" };
//var json = new ApiResponse { Name = "name1", Type = "b", Config = "{'items' : ['item1', 'item2']}" };
string apiResult = JsonConvert.SerializeObject(json);
var deserialized = JsonConvert.DeserializeObject<ApiResponse>(apiResult);
if (deserialized.Type == "a")
{
// You deserialized 'Name' before and already you have the value in 'deserialized' variable
var result = new TypeAHandler() { Name = deserialized.Name };
// You need just 'Deserialize' the 'Config' property
result.Config = JsonConvert.DeserializeObject<TypeAConfig>(deserialized.Config);
Console.WriteLine("Name: {0}", result.Name);
Console.WriteLine("Id: {0}", result.Config.Id);
Console.WriteLine("Title: {0}", result.Config.Title);
}
else if (deserialized.Type == "b")
{
var result = new TypeBHandler() { Name = deserialized.Name };
result.Config = JsonConvert.DeserializeObject<TypeBConfig>(deserialized.Config);
Console.WriteLine("Name: {0}", result.Name);
Console.WriteLine("Items: ");
foreach (var item in result.Config.Items)
{
Console.WriteLine(item.ToString());
}
}
Console.ReadKey();
}
}
public class ApiResponse
{
public string Name { get; set; }
public string Type { get; set; }
public string Config { get; set; }
}
public class TypeAHandler
{
public string Name { get; set; }
public TypeAConfig Config { get; set; }
}
public class TypeAConfig
{
public int Id { get; set; }
public string Title { get; set; }
}
public class TypeBHandler
{
public string Name { get; set; }
public TypeBConfig Config { get; set; }
}
public class TypeBConfig
{
public string[] Items { get; set; }
}
Because your Config property is not a nested object, I deserialized it separately.
This is the result when type:a
This is the result when type:b
I would do it like this:
static void Main()
{
var json = new ApiResponse {name = "name1", type = "a", config = "{'title' : 'hello world!', 'id' : 1}"};
var jsonString = JsonConvert.SerializeObject(json);
Console.WriteLine(jsonString);
if (json.type == "a")
{
var handler = JsonConvert.DeserializeObject<typeAhandler>(jsonString);
// handler.Dump();
}
else if(json.type == "b")
{
var handler = JsonConvert.DeserializeObject<typeBhandler>(jsonString);
// handler.Dump();
}
}
public class ApiResponse
{
public string name { get; set; }
public string type { get; set; }
public string config { get; set; }
}
// Type A Handler
public class typeAhandler
{
[JsonProperty("name")]
public string name { get; set; }
[JsonIgnore] public typeAconfig config { get; set; }
[JsonProperty("config")]
public dynamic DynamicConfig
{
set => config = JsonConvert.DeserializeObject<typeAconfig>(value);
}
}
public class typeAconfig
{
public string title { get; set; }
public int id { get; set; }
}
// Type B Handler
public class typeBhandler
{
[JsonProperty("name")]
public string name { get; set; }
[JsonIgnore]
public typeBconfig config { get; set; }
[JsonProperty("config")]
public dynamic DynamicConfig
{
set => config = JsonConvert.DeserializeObject<typeBconfig>(value);
}
}
public class typeBconfig
{
public string[] items { get; set; }
}
The key is to expose the config type as dynamic to the JsonComnvert and then behind scene convert it to your desired type and use it in your app
Consider doing it as below -
Maintain a dictionary of handler to avoid if...else.
public static void Main(string[] args)
{
var json = new ApiResponse { name = "name1", type = "a", config = "{'title' : 'hello world!', 'id' : 1}" };
var jsonString = JsonConvert.SerializeObject(json);
Console.WriteLine(jsonString);
IDictionary<string, Type> handlers = new Dictionary<string, Type>
{
{ "a", typeof(typeAconfig) },
{ "b", typeof(typeBconfig) }
};
var handler = DeserializeHandler(jsonString, handlers);
}
The function DeserializeHandler is as below -
public static object DeserializeHandler(string jsonString, IDictionary<string, Type> handlers)
{
var jsonObject = JsonConvert.DeserializeObject<JToken>(jsonString);
var type = jsonObject["type"].ToString();
var handlerType = handlers[type];
var config = jsonObject["config"].ToString();
var handler = JsonConvert.DeserializeObject(config, handlerType);
return handler;
}
I am not 100% clear on what you want but you could do something like this using custom attributes to avoid the if/else and reflection to do your object construction.
internal class Program
{
public static void Main(string[] args)
{
var json = new ApiResponse { Name = "name1", Type = "a", Config = "{'title' : 'hello world!', 'id' : 1}" };
var jsonString = JsonConvert.SerializeObject(json);
ParseAndDumpConfig(jsonString);
json = new ApiResponse { Name = "name2", Type = "b", Config = "{'Items' : ['Item-1', 'Item-2', 'Item-3']}" };
jsonString = JsonConvert.SerializeObject(json);
ParseAndDumpConfig(jsonString);
}
private static void ParseAndDumpConfig(string jsonString)
{
Console.WriteLine();
Console.WriteLine("Parsing Api Response:");
Console.WriteLine(jsonString);
var json = JsonConvert.DeserializeObject<ApiResponse>(jsonString);
if (_typeMap.TryGetValue(json.Type, out var t))
{
var config = JsonConvert.DeserializeObject(json.Config, t);
Type genericType = typeof(TypeHandler<>).MakeGenericType(t);
var constructedObject = Activator.CreateInstance(genericType, new object[] { json.Name, config });
var result = Convert.ChangeType(constructedObject, genericType);
Console.WriteLine($"Parsed to [{result.GetType().FullName}]:");
Console.WriteLine(result);
}
else
{
Console.WriteLine($"Config map for \"{json.Type}\" not found.");
}
}
private static readonly Dictionary<string, Type> _typeMap = Assembly.GetExecutingAssembly().GetTypes()
.Where(x => x.CustomAttributes.Any(y => y.AttributeType == typeof(DeserializeTypeMapAttribute)))
.ToDictionary(x => x.GetCustomAttribute<DeserializeTypeMapAttribute>().Name);
}
public class ApiResponse
{
public string Name { get; set; }
public string Type { get; set; }
public string Config { get; set; }
}
public class TypeHandler<TConfig>
{
public string Name { get; set; }
public TConfig Config { get; set; }
public TypeHandler(string name, TConfig config)
{
Name = name;
Config = config;
}
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[DeserializeTypeMap("a")]
public class TypeAconfig
{
public string Title { get; set; }
public int Id { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[DeserializeTypeMap("b")]
public class TypeBconfig
{
public string[] Items { get; set; }
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
[AttributeUsage(AttributeTargets.Class)]
public class DeserializeTypeMapAttribute : Attribute
{
public string Name { get; }
public DeserializeTypeMapAttribute(string name)
{
Name = name;
}
}
4 times in 3 days I've posted the same answer for different questions :-)
Use a specific JsonConvert class: C# Convert Json to object with duplicated property name with different data type

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

Map a flatten SQL response to an object containing lists of lists with Automapper

The error message that I'm receiving at runtime is:
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
List'1 -> MobileRoot (Destination member list)
System.Collections.Generic.List'1[[Strata.CS.Jazz.Biz.Dashboard.MobileInfo, Strata.CS.Jazz.Biz, Version=2019.10.0.0, Culture=neutral, PublicKeyToken=null]] -> Strata.Jazz.Web.Controllers.MobileController+MobileRoot (Destination member list)
Unmapped properties:
Users
From what I can tell from the error message is that AutoMapper needs to know how to handle the ForMember Users create in the MobileRoot, and then propagate that for each of the subsequent lists down the chain. Can anyone tell me how to do this efficiently using AutoMapper? I know how to do this with Linq using GroupBy and Select, so it is my thought that this should be do-able with AutoMapper.
The query I have returns this class:
public class MobileInfo
{
public string NameFull { get; set; }
public string EmailAddress { get; set; }
public string SolutionName { get; set; }
public int SortOrder { get; set; }
public string Name { get; set; }
public bool IsLegacy { get; set; }
public string Description { get; set; }
public string WidgetName { get; set; }
public int Row { get; set; }
public int Col { get; set; }
public int Height { get; set; }
public int Width { get; set; }
public string WidgetClassName { get; set; }
public string Data { get; set; }
}
I would like to use Automapper with profiles to have it return this:
internal class MobileRoot
{
public IEnumerable<MobileUser> Users { get; set; }
}
internal class MobileUser
{
public string NameFull { get; set; }
public string EmailAddress { get; set; }
public IEnumerable<MobileSolution> Solutions { get; set; }
}
internal class MobileSolution
{
public string Solution { get; set; } // MobileInfo.SolutionName
public int SortOrder { get; set; }
public IEnumerable<MobileDashboard> Dashboards { get; set; }
}
internal class MobileDashboard
{
public string Dashboard { get; set; } // MobileInfo.Name
public bool IsLegacy { get; set; }
public string Description { get; set; }
public IEnumerable<MobileWidget> Widgets { get; set; }
}
internal class MobileWidget
{
public string Widget { get; set; } // MobileInfo.WidgetName
public int Row { get; set; }
public int Col { get; set; }
public int Height { get; set; }
public int Width { get; set; }
public string WidgetClassName { get; set; }
public string Data { get; set; }
}
The Profiles I have defined so far are:
public class ProfileMobileRoot : Profile
{
public ProfileMobileRoot()
{
CreateMap<MobileInfo, MobileRoot>();
}
}
public class ProfileMobileUser : Profile
{
public ProfileMobileUser()
{
CreateMap<MobileInfo, MobileUser>();
}
}
public class ProfileMobileSolution : Profile
{
public ProfileMobileSolution()
{
CreateMap<MobileInfo, MobileSolution>();
}
}
public class ProfileMobileDashboard : Profile
{
public ProfileMobileDashboard()
{
CreateMap<MobileInfo, MobileRoot>();
}
}
public class ProfileMobileWidget : Profile
{
public ProfileMobileWidget()
{
CreateMap<MobileInfo, MobileWidget>();
}
}
You can do something like below. It's a little late so my solution isn't so sophisticated... but it works ;)
public class ProfileMobileRoot : Profile
{
public ProfileMobileRoot()
{
CreateMap<MobileInfo, MobileWidget>()
.ForMember(x=>x.Name, opt=>opt.MapFrom(x=>x.WidgetName));
CreateMap<IEnumerable<MobileInfo>, IEnumerable<MobileDashboard>>()
.ConvertUsing<DashboardConverter>();
CreateMap<IEnumerable<MobileInfo>, IEnumerable<MobileSolution>>()
.ConvertUsing<SolutionConverter>();
CreateMap<IEnumerable<MobileInfo>, IEnumerable<MobileUser>>()
.ConvertUsing<UserConverter>();
CreateMap<IEnumerable<MobileInfo>, MobileRoot>()
.ForMember(x => x.Users, opt => opt.MapFrom(x => x.ToList()));
}
}
class UserConverter : ITypeConverter<IEnumerable<MobileInfo>, IEnumerable<MobileUser>>
{
public IEnumerable<MobileUser> Convert(IEnumerable<MobileInfo> source, IEnumerable<MobileUser> destination, ResolutionContext context)
{
var groups = source.GroupBy(x => new { x.NameFull, x.EmailAddress});
foreach (var v in groups)
{
yield return new MobileUser()
{
EmailAddress = v.Key.EmailAddress,
NameFull = v.Key.NameFull,
Solutions = context.Mapper.Map<IEnumerable<MobileSolution>>(source.Where(x =>
v.Key.NameFull == x.NameFull && v.Key.EmailAddress== x.EmailAddress)).ToList()
};
}
}
}
class SolutionConverter : ITypeConverter<IEnumerable<MobileInfo>, IEnumerable<MobileSolution>>
{
public IEnumerable<MobileSolution> Convert(IEnumerable<MobileInfo> source, IEnumerable<MobileSolution> destination, ResolutionContext context)
{
var groups = source.GroupBy(x => new { x.SolutionName, x.SortOrder});
foreach (var v in groups)
{
yield return new MobileSolution()
{
Solution = v.Key.SolutionName,
SortOrder = v.Key.SortOrder,
Dashboards= context.Mapper.Map<IEnumerable<MobileDashboard>>(source.Where(x =>
v.Key.SolutionName== x.SolutionName&& v.Key.SortOrder== x.SortOrder)).ToList()
};
}
}
}
class DashboardConverter : ITypeConverter<IEnumerable<MobileInfo>, IEnumerable<MobileDashboard>>
{
public IEnumerable<MobileDashboard> Convert(IEnumerable<MobileInfo> source, IEnumerable<MobileDashboard> destination, ResolutionContext context)
{
var groups = source.GroupBy(x => new {x.Name, x.IsLegacy, x.Description});
foreach (var v in groups)
{
yield return new MobileDashboard()
{
Dashboard = v.Key.Name,
Description = v.Key.Description,
IsLegacy = v.Key.IsLegacy,
Widgets = context.Mapper.Map<IEnumerable<MobileWidget>>(source.Where(x =>
v.Key.IsLegacy == x.IsLegacy && v.Key.Name == x.Name && v.Key.Description == x.Description))
};
}
}
}

Use AutoMapper to Run Function to Setup Mutiple Properties Based on Other Parameters

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

Categories

Resources