What is the best approach for using AutoMapper with value objects with static factory methods:
public class ImmutableDetail
{
public static ImmutableDetail Create(string detail) => new ImmutableDetail(detail);
private ImmutableDetail(string detail)
{
Detail = detail;
}
public string Detail { get;}
}
Where I want to be able to:
var immutableDetails = Mapper.Map<ImmutableDetail>(source);
With below classes:
public class DummySource
{
public string Detail { get; set; }
}
public class ImmutableDetail
{
public static ImmutableDetail Create(string detail) { return new ImmutableDetail(detail); }
private ImmutableDetail(string detail)
{
Detail = detail;
}
public string Detail { get; private set; }
}
you can make a mapping like this:
Mapper.CreateMap<DummySource, ImmutableDetail>().ConstructUsing((DummySource ds) => ImmutableDetail.Create(ds.Detail));
var source = new DummySource { Detail = "Hello" };
var immutableDetails = Mapper.Map<ImmutableDetail>(source);
Related
I have a static class in my system keeping track of the frequency of measurements, the number of samples currently read, what sensors are on an which are off and all those nice details.
Now I make a measurement and want to create a report in the report I want to save all the information stored in the static class. something like this :
public static class Details{
public static int samplesRead { get; set;}
public static int frequency { get; set;}
public static List<devices> devices { get; set;}
}
public class Patient{...} // name, surname , blabla
public class ResultsSet {
public DateTime date;
public Patient patient;
public *DetailsObject* details;
}
public void main {
patient p = new patient(...);
... make the measurements ...
var results = new ResultSet();
results.patient = p;
results.DateTime = DateTime.Now();
results.details = **here the magic ** Details.ToObject();
results.Serialize(myFilePath);
}
How can one acomplish that conversion to a single defined object?
it is the capability of making an snapshot of the static class in an object. [...] Just make an object.
So what you could do is to create a DTO that has the same properties as your static class:
public class DetailsSnapshot
{
public int samplesRead { get; set; }
public int frequency { get; set; }
public List<device> devices { get; set; }
}
Not you can map and return such an object at any given time:
public static class Details{
public static int samplesRead { get; set;}
public static int frequency { get; set; }
public static List<device> devices { get; set; }
public static DetailsSnapshot MakeSnapShot()
{
return new DetailsSnapshot
{
samplesRead = samplesRead,
frequency = frequency,
devices = devices.ToList()
};
}
}
You can have then such an snap-shot-object in your results:
public class ResultsSet
{
public DateTime date;
public Patient patient;
public DetailsSnapshot detailsSnapShot;
}
and make the snap shot (here the magic) the following way:
results.detailsSnapShot = Details.MakeSnapShot();
EDIT:
There is also a way using reflection. With this approach you would scan your Details class for the properties and extract the values. You could return a Dictionary which basically maps the names of the properties to the values:
public static Dictionary<string, object> MakeSnapShotReflection()
{
PropertyInfo [] allPorperties = typeof(Details).GetProperties(BindingFlags.Public | BindingFlags.Static);
Dictionary<string, object> valuemapping = new Dictionary<string, object>();
for (int i = 0; i < allPorperties.Length; i++)
{
valuemapping.Add(allPorperties[i].Name, allPorperties[i].GetValue(null));
}
return valuemapping;
}
This way would allow you to extend the Details class with further properties without worrying about extending anything else.
Or the short version:
public static Dictionary<string, object> MakeSnapShotReflection()
{
PropertyInfo[] allPorperties = typeof(Details).GetProperties(BindingFlags.Public | BindingFlags.Static);
return allPorperties.ToDictionary(key => key.Name, value => value.GetValue(null));
}
With this approach you could still use intellisens to access the correct values:
Test Data:
public static class Details
{
public static int samplesRead { get; set;} = 100;
public static int frequency { get; set; } = 2700;
public static List<device> devices { get; set; } = new List<device>()
{
new device { Name = "sensor1" },
new device { Name = "sensor 2" }
};
}
public class device
{
public string Name { get; set; }
}
Test Code to access values:
void Main()
{
Dictionary<string, object> details = Details.MakeSnapShotReflection();
Console.WriteLine(details[nameof(Details.frequency)]);
Console.WriteLine(details[nameof(Details.samplesRead)]);
foreach (var element in details[nameof(Details.devices)] as IEnumerable<device>)
{
Console.WriteLine(element.Name);
}
}
OUTPUT:
2700
100
sensor1
sensor 2
If you want to save and restore it, make it a non-static class and serialise/deserialise it using JSON or XML. You can then go JsonConvert.SerialiseObject and JsonConvert.Deserialise object. Nice and simple.
If you want to ensure only one instance, make the class a singleton.
public class Details
{
private static readonly Details _instance = new Details();
static Details()
{
}
private Details()
{
}
public Details Intance
{
get
{
return _instance;
}
}
public int samplesRead { get; set;}
public int frequency { get; set;}
public List<devices> devices { get; set; }
}
Then you can access it's properties this way:
Details.Instance.samplesRead
If the class has to be static, you can use reflection to serialise it:
public static string SerializeStaticProperties(Type type)
{
var properties = type.GetProperties(BindingFlags.Static | BindingFlags.Public);
var data = new List<Property>();
foreach (var property in properties)
{
data.Add(new Property
{
Name = property.Name,
Type = property.PropertyType,
Value = JsonConvert.SerializeObject(property.GetValue(null))
});
}
return JsonConvert.SerializeObject(data);
}
public static void DeserializeStaticProperties(Type type, string json)
{
var data = JsonConvert.DeserializeObject<List<Property>>(json);
foreach (var item in data)
{
var property = type.GetProperty(item.Name, BindingFlags.Static | BindingFlags.Public);
if (property != null)
{
property.SetValue(null, JsonConvert.DeserializeObject(item.Value, item.Type));
}
}
}
public class Property
{
public string Name { get; set; }
public Type Type { get; set; }
public string Value { get; set; }
}
During a runtime mapping operation (like when you use ResolveUsing or a custom TypeConverter) is it possible to get the container classes (or types at least) of the source and destination members?
I know that when you map one object to another, that the objects don't have to be members of some "parent" or "container" object, but I'm talking about the situation when AutoMapper is recursively copying a complex object.
Here's an example:
Here I'm copying (or setting it up at least) Cars & Boats of "kind A" to "kind B".
public class VehicleCopyProfile : AutoMapper.Profile
{
public VehicleCopyProfile()
{
this.CreateMap<CarA, CarB>();
this.CreateMap<BoatA, BoatB>();
this.CreateMap<WindshieldA, WindshieldB>(
.ConvertUsing((s, d, resContext) =>
{
// *** How can I tell if s is coming from a Car or a Boat? ***
});
}
}
// Cars & Boats each have a Windshield
public class CarA
{
public WindshieldA Windshield {get;set;}
}
public class BoatA
{
public WindshieldA Windshield {get;set;}
}
public class WindshieldA
{
public string Name {get;set;}
}
public class CarB
{
public WindshieldB Windshield {get;set;}
}
public class BoatB
{
public WindshieldB Windshield {get;set;}
}
public class WindshieldB
{
public string Name {get;set;}
}
Here is a solution using AutoMapper ResolutionContext Items as proposed by #Lucian Bargaoanu in comment. The idea is to use Before and After Map to store information in the Resolution Context. I use a Stack to keep track of the whole chain of relationship.
namespace SO51101306
{
public static class IMappingExpressionExtensions
{
public static IMappingExpression<A, B> RegisterChainOfTypes<A, B>(this IMappingExpression<A, B> mapping)
{
mapping.BeforeMap((a, b, ctx) => {
ctx.PushTypeInChainOfTypes(typeof(A));
});
mapping.AfterMap((a, b, ctx) => {
ctx.PopLastTypeInChainOfTypes();
});
return mapping;
}
}
public static class ResolutionContextExtensions
{
const string chainOfTypesKey = "ChainOfTypes";
private static Stack<Type> GetOrCreateChainOfTypesStack(ResolutionContext ctx)
{
var hasKey = ctx.Items.ContainsKey(chainOfTypesKey);
return hasKey ? (Stack<Type>)ctx.Items[chainOfTypesKey] : new Stack<Type>();
}
public static void PushTypeInChainOfTypes(this ResolutionContext ctx, Type type)
{
var stack = GetOrCreateChainOfTypesStack(ctx);
stack.Push(type);
ctx.Items[chainOfTypesKey] = stack;
}
public static Type PopLastTypeInChainOfTypes(this ResolutionContext ctx)
{
var stack = (Stack<Type>)ctx.Items[chainOfTypesKey];
return stack.Pop();
}
public static bool HasParentType(this ResolutionContext ctx, Type parentType)
{
var stack = GetOrCreateChainOfTypesStack(ctx);
return stack.Contains(parentType);
}
}
public class CarCopyProfile : Profile
{
public CarCopyProfile()
{
CreateMap<CarA, CarB>().RegisterChainOfTypes();
CreateMap<BoatA, BoatB>().RegisterChainOfTypes();
CreateMap<WindshieldA, WindshieldB>()
.ConvertUsing((wa,wb,ctx)=> {
if(ctx.HasParentType(typeof(CarA)))
{
Console.WriteLine("I'm coming from CarA");
//Do specific stuff here
}
else if (ctx.HasParentType(typeof(BoatA)))
{
Console.WriteLine("I'm coming from boatA");
//Do specific stuff here
}
return wb;
});
}
}
public class CarA
{
public WindshieldA Windshield { get; set; }
}
public class BoatA
{
public WindshieldA Windshield { get; set; }
}
public class CarB
{
public WindshieldB Windshield { get; set; }
}
public class BoatB
{
public WindshieldB Windshield { get; set; }
}
public class WindshieldA
{
public string Name { get; set; }
}
public class WindshieldB
{
public string Name { get; set; }
}
class Program
{
static void Main(string[] args)
{
Mapper.Initialize(c => c.AddProfile<CarCopyProfile>());
var carA = new CarA{Windshield = new WindshieldA()};
var boatA = new BoatA{Windshield = new WindshieldA()};
var carB = Mapper.Map<CarB>(carA);
var boatB = Mapper.Map<BoatB>(boatA);
}
}
}
This will output:
I'm coming from CarA
I'm coming from boatA
Another way is to use custom value resolver:
class CustomResolver<T1, T2>:IValueResolver ... { ... }
this.CreateMap<CarA, CarB>()
.ForMember(x => x.Windshield , opt => opt.ResolveUsing(new CustomResolver<CarA, CarB>()));
Then in you CustomResolver implementation:
var windshieldB = Mapper.Map<WindshieldB>(windshieldA, x => {x.Items["type1"] = typeof(T1); x.Items["type2"] = typeof(T2);});
And then:
this.CreateMap<WindshieldA, WindshieldB>(
.ConvertUsing((s, d, resContext) =>
{
// resContext.Options.Items["type1"]
});
See http://docs.automapper.org/en/stable/Custom-value-resolvers.html
What should I be calling the "BFactory" below. Its not really a Factory since there is no selection of a concrete class happening, and its not necessarily creating an object each time. Its kind of a Pool but the users do not return the Bs they get to the pool after they are done with them. It could be called a Cache but performance is not the primary intention. The intention is that everyone who is using the same BFactory will get the same B when they pass the same A which starts to sound kind of like a singleton-ish.
public class A
{
public int MyProperty { get; set; }
}
public class B
{
public B(A wrapped)
{
Wrapped = wrapped;
}
public A Wrapped { get; set; }
}
public class BFactory
{
private Dictionary<A,B> _created = new Dictionary<A,B>();
public B GetB(A a)
{
if (_created.ContainsKey(a) == false)
{
_created[a] = new B(a);
}
return _created[a];
}
}
here is a slightly more real example:
The value from MyModel is shown in several locations in the app by binding a TextBlock to the ValueString property of MyViewModel. The user can select to present the value as a percent or a decimal and it should be updated in all locations if it is updated in one.
public class MyModel
{
public int Value { get; set; }
}
public class MyViewModel
{
private readonly MyModel _model;
public MyViewModel(MyModel model)
{
_model = model;
}
public string ValueString
{
get { return string.Format(FormatString, _model.Value); }
}
public string FormatString { get; set; }
}
public class MyViewModelFactory
{
private readonly Dictionary<MyModel, MyViewModel> _created = new Dictionary<MyModel, MyViewModel>();
public MyViewModel GetViewModel(MyModel model)
{
if (_created.ContainsKey(model) == false)
{
_created[model] = new MyViewModel(model);
}
return _created[model];
}
}
I have the following code:
_eventInstanceRepository.GetInnerCollection().Update(
Query.EQ("_id", listingEventModel.Id),
Update.PushWrapped<string[]>("ArtistIds", ids.ToArray()));
Which is designed to update the following document:
public class ListingEvent
{
public ObjectId Id { get; set; }
public string[] ArtistIds { get; set; }
}
ids is a List
Any ideas why this isn't updating the docs?
[UPDATE]
Also tried this!
foreach (var id in ids)
{
_eventInstanceRepository.GetInnerCollection().Update(
Query.EQ("_id", listingEventModel.Id),
Update.Push("ArtistIds", id));
}
No luck...
[UPDATE]
Going back to RavenDb - at least for now. I don't see how MongoDb is a viable option the whole time there are no real sources discussing (slightly more complex than flat structure) document updates on the internet and the examples I can find simply do not work.
[UPDATE]
Here is the repository code:
public class Repository<T> : IRepository<T>
{
private readonly MongoCollection<T> _docs;
public Repository(MongoCollection<T> docs)
{
_docs = docs;
}
public IList<T> GetAll()
{
return _docs.FindAll().Select<T, T>(x => x.As<T>()).ToList();
}
//HACK!
public MongoCollection<T> GetInnerCollection(){
return _docs;
}
public void Save(T doc)
{
_docs.Save(doc);
}
public void Save(IEnumerable<T> docsToSave)
{
foreach (var doc in docsToSave) Save(doc);
}
public void Dispose()
{
throw new NotImplementedException();
}
public void Delete(string id)
{
var query = Query.EQ("_id", id);
_docs.Remove(query);
}
}
Working sample code for appending a list of strings to an existing list of strings using a strongly-typed Push:
class Event
{
public ObjectId Id { get; set; }
public string Name { get; set; }
public List<string> Participants { get; set; }
}
class Program
{
static void Main(string[] args)
{
MongoClient client = new MongoClient("mongodb://localhost/test");
var db = client.GetServer().GetDatabase("test");
var collection = db.GetCollection("events");
var event0 = new Event { Name = "Birthday Party",
Participants = new List<string> { "Jane Fonda" } };
collection.Insert(event0);
collection.Update(Query.EQ("_id", event0.Id),
Update<Event>.PushAll(p => p.Participants,
new List<string> { "John Doe", "Michael Rogers" }));
}
}
Imagine a class as follows.. It's a class provided to me to work with.. I cannot change its source..
public class MyClass
{
object _Object { get; set; }
public void FuncA1() { _Object = new object(); }
public void FuncA2() { _Object = new List<object>(); }
public int FuncB1() { _Object = 0; return 0; }
public int FuncB2() { _Object = 123; return 123; }
public string FuncC1() { _Object = null; return null; }
public string FuncC2() { _Object = "Hello"; return "Hello"; }
}
Im trying to create a wrapper for this class, such that I can group its many functions into categories..
MyWrapper.Voids.FuncA1();
MyWrapper.Voids.FuncA2();
MyWrapper.Integers.FuncB1();
MyWrapper.Integers.FuncB2();
MyWrapper.Strings.FuncC1();
MyWrapper.Strings.FuncC2();
The only solution I can think of for this scenario is to design the wrapper like this:
public class MyWrapper
{
MyClass _Instance { get; set; }
public _Void Voids { get; private set; }
public _Integer Integers { get; private set; }
public _String Strings { get; private set; }
public class _Void
{
MyWrapper _Parent { get; set; }
public void FuncA1() { _Parent._Instance.FuncA1(); }
public int FuncA2() { return _Parent._Instance.FuncA2(); }
}
public class _Integer
{
...
}
public class _String
{
...
}
public MyWrapper()
{
_Instance = new MyClass();
Voids = new _Voids(this);
Integers = new _Integer(this);
Strings = new _String(this);
}
}
This solution works, but has a number of problems:
- The inner classes are forced to be public, which allows them to be instantiated by the user..
- I am forced to maintain a reference of the parent object in the child classes..
Is there a better way of doing this?
EDIT: The code posted initially was a bit confusing, in the sense that it was diverting attention away from the core issue and more into the issues of whether a function would cause exceptions or not if they all work on the same object..
NOTE: This is not actual code.. I hacked together this example to show what I'm trying to do.. CREATE A WRAPPER AROUND AN OBJECT (I cannot change the original object's code) AND GROUP FUNCTIONS INTO CATEGORIES..
FINAL EDIT: following suggestion by Juharr.. here's what ive done to accomplish what i wanted.. for the betterment of others..
public interface IVoid
{
void FuncA1();
void FuncA2();
}
public interface IInteger
{
int FuncB1();
int FuncB2();
}
public class MyWrapper
{
public MyClass Instance { get; private set; }
public IVoid Voids { get; private set; }
public IInteger Integers { get; private set; }
private abstract class MyBase
{
protected MyWrapper Parent { get; set; }
protected MyClass Instance { get { return Parent.Instance; } }
public MyBase(MyWrapper oParent) { Parent = oParent; }
}
private class MyVoid : MyBase, IVoid
{
public MyVoids (MyWrapper oParent) : base(oParent) { }
public void FuncA1() { Instance.FuncA1(); }
public void FuncA2() { Instance.FuncA2(); }
}
private class MyInteger : MyBase, IInteger
{
public MyInteger (MyWrapper oParent) : base(oParent) { }
public int FuncB1() { return Instance.FuncB1(); }
public int FuncB2() { return Instance.FuncB2(); }
}
public MyWrapper()
{
Instance = new MyClass();
Voids = new MyVoid(this);
Integers = new MyInteger(this);
}
}
You could write public interfaces instead. Then your inner classes don't have to be public. So something like this.
public interface IIntger
{
void Set(int iValue);
int Get();
}
public class MyWrapper
{
MyClass _Instance { get; set; }
public IInteger Integer { get; private set; }
private class _Integer : IInteger
{
MyWrapper _Parent { get; set; }
public void Set(int iValue) { _Parent._Instance.IntegerSet(iValue); }
public int Get() { return _Parent._Instance.IntegerGet(); }
}
public MyWrapper()
{
_Instance = new MyClass();
Integer = new _Integer(this);
}
}
EDIT:
To answer the second part of your question you will either need the reference to the parent class or a reference to the class you are wrapping. So you could have this instead.
public class MyWrapper
{
public IInteger Integer { get; private set; }
private class _Integer : IInteger
{
MyClass _Instance { get; set; }
public _Integer(MyClass myClass) { _Instance = myClass; }
public void Set(int iValue) { _Instance.IntegerSet(iValue); }
public int Get() { return _Instance.IntegerGet(); }
}
public MyWrapper(MyClass instance)
{
Integer = new _Integer(instance);
}
}