Automapper IEnumerable within class is not being mapped to RepeatedField - c#

I want to map between two classes:
public class A {
public IEnumerable<C> someList
}
and
public class B {
public RepeatedField<D> someList
}
where RepeatedField is a class from Google.Protobuf.Collections that handles gRPC data.
EDIT: As it turns out, the way that gRPC creates classes via its prototype is not exactly like creating a class like B. See my answer.
I create an Automapper MappingConfiguration like this
return new MapperConfiguration(cfg =>
{
cfg.CreateMap<C, D>().ReverseMap();
cfg.CreateMap<A, B>().ReverseMap();
});
and then it gets registered via ASP.NET Startup class.
If I do something like this in another class
A instanceA; // assume A's list has values inside
var listofD = this.mapper.Map<List<D>>(A.someList)
it correctly returns a list with values inside. However:
A instanceA; // assume A's list has values inside
B instanceB = this.mapper.Map<B>(A);
returns an instance of B, but the list inside of instanceB is empty. How do I fix this?

You need to create a custom type converter for performing the conversion:
private class EnumerableToRepeatedFieldTypeConverter<TITemSource, TITemDest> : ITypeConverter<IEnumerable<TITemSource>, RepeatedField<TITemDest>>
{
public RepeatedField<TITemDest> Convert(IEnumerable<TITemSource> source, RepeatedField<TITemDest> destination, ResolutionContext context)
{
destination = destination ?? new RepeatedField<TITemDest>();
foreach (var item in source)
{
// obviously we haven't performed the mapping for the item yet
// since AutoMapper didn't recognise the list conversion
// so we need to map the item here and then add it to the new
// collection
destination.Add(context.Mapper.Map<TITemDest>(item));
}
return destination;
}
}
And the other way, if required:
private class RepeatedFieldToListTypeConverter<TITemSource, TITemDest> : ITypeConverter<RepeatedField<TITemSource>, List<TITemDest>>
{
public List<TITemDest> Convert(RepeatedField<TITemSource> source, List<TITemDest> destination, ResolutionContext context)
{
destination = destination ?? new List<TITemDest>();
foreach (var item in source)
{
destination.Add(context.Mapper.Map<TITemDest>(item));
}
return destination;
}
}
Which you can register like so:
ce.CreateMap(typeof(IEnumerable<>), typeof(RepeatedField<>)).ConvertUsing(typeof(EnumerableToRepeatedFieldTypeConverter<,>));
ce.CreateMap(typeof(RepeatedField<>), typeof(List<>)).ConvertUsing(typeof(RepeatedFieldToListTypeConverter<,>));
Try it online

I've solved the issue.
A Google.Protobuf.Collections.RepeatedField inside a C# class is readonly, meaning that directly assigning values into it won't work and will only return an empty list on the way back. Therefore, I created a custom type converter between the two larger classes to bring them together. What it does is add values directly into the RepeatedField rather than populating my own RepeatedField and assigning the value into the class.
public static class mapConfig
{
public static ContainerBuilder RegisterObjectMappers(this ContainerBuilder builder)
{
builder.Register(c => GetV1MapperConfiguration().CreateMapper())
.As<IMapper>().SingleInstance();
return builder;
}
private static MapperConfiguration GetMapConfig()
{
return new MapperConfiguration(cfg =>
{
// some mappings here
cfg.CreateMap<C, D>().ReverseMap();
cfg.CreateMap<A, B>().ConvertUsing<AToBConverter>();
});
}
}
public class AToBConverter : ITypeConverter<A, B>
{
public B Convert(A source, B destination, ResolutionContext context)
{
var b = new B
{
// internal values here aside from the repeated field(s)
};
// Need to use the Add method to add values rather than assign it with an '=' sign
foreach (var someValue in source.someList)
{
b.someList.Add(context.Mapper.Map<D>(someValue));
}
return b;
}
}
Converting from RepeatedField to a List or any other IEnumerable in a mapped class isn't any trouble and didn't require another converter for me.

Related

Instantiate a generic list

I want to instantiate a generic list of objects like:
public static class TablesClass
{
private static IList<Tables<T>> TablesInstance { get; set; }
static TablesClass() => Tables = new List<Tables<T>>();
public static void AddTable(Table<t> table) => Tables.Add(table);
}
I can't change Tables<T>, this is a nuget package class.
How may i achieve this? All i have tried just does not work (setting a type T to class, using object instead T and casts - not desired solution).
Can somebody help me?
TablesClass is not a generic class and you are not telling the compiler what type T is supposed to be somewhere.
If you want to be able to add different kinds of objects into the same IList<Tables<T>> list, T must be a common base type for all these objects.
For example, if you want to be able to add apples, pears and bananas to the list, the type parameter T may be specified as Fruit provided that Fruit is the base class for all these types.
Obviously you will need to cast from Fruit if you want to be able to access any member of an item in the list that is specific to a concrete implementation of Fruit class but this is inevitable. You don't throw a bunch of different kinds of fruits into a single basket and expect to be able to always pick up a specific fruit, do you?
Your nuget class must be this style:
public abstract class Tables
{
}
//the Generic class must has a base, by which you can list them
public class Tables<T> : Tables // where T: something base class of your object
{
//...
}
then your class must be:
public static class TablesClass
{
//Search source code of your Nuget package, find its base class of Generic class, the list must be defined as its base
private static IList<Tables> Tables { get; set; }
static TablesClass()
{
Tables = new List<Tables>();
}
public static void AddTable(Tables table)
{
Tables.Add(table);
}
}
then you can use it like this:
public class Test
{
public static void Mains()
{
TablesClass.AddTable(new Tables<A>());
TablesClass.AddTable(new Tables<B>());
TablesClass.AddTable(new Tables<C>());
}
}
If you want to do this kind of thing you need to hold the references using object, but make a method that allows you to store and fetch each table using strong-typing.
Try a class like this:
public class Repository
{
private Dictionary<Type, Dictionary<string, object>> _store
= new Dictionary<Type, Dictionary<string, object>>();
public void Store<T>(string key, T value)
{
if (!_store.ContainsKey(typeof(T)))
{
_store.Add(typeof(T), new Dictionary<string, object>());
}
_store[typeof(T)][key] = value;
}
public T Fetch<T>(string key)
{
return (T)_store[typeof(T)][key];
}
public bool TryFetch<T>(string key, out T value)
{
var success = _store.ContainsKey(typeof(T)) && _store[typeof(T)].ContainsKey(key);
value = success ? this.Fetch<T>(key) : default(T);
return success;
}
public bool TryInject<T>(string key, Action<T> inject)
{
var success = this.TryFetch<T>(key, out T value);
if (success)
{
inject(value);
}
return success;
}
}
Then you can strongly-type the objects into the repository (collection) and strongly-type fetching them out like this:
var repository = new Repository();
repository.Store("a", new TableA());
repository.Store("b", new TableB());
repository.Store("c", new TableC());
repository.Store("d", new TableD());
/* Somewhere else in your code */
TableA a = repository.Fetch<TableA>("a");
TableB b = repository.Fetch<TableB>("b");
TableC c = repository.Fetch<TableC>("c");
TableD d = repository.Fetch<TableD>("d");
The key value (i.e. "a") is optional - you can remove it from the code - but it is useful if you need to store more that one object of a specific type.
Way much simple solution: List<dynamic>.

Ignoring items in collection with Automapper when mapping collection

I am mapping an collection of items
var List<A> myCollection = new List<A>();
public class A
{
bool HasChanges {get;set;}
}
var mappedCollection = Map(myCollection);
then I only want to map the items where HasChanges == true
Is this possible?
With Linq:
var mappedCollection = Map(myCollection.Where(x => x.HasChanges == true).ToList());
Automapper has custom type converters:
https://github.com/AutoMapper/AutoMapper/wiki/Custom-type-converters
// this is your converter
public class ATypeConverter : ITypeConverter<string, A>
{
public A Convert(ResolutionContext context)
{
// implement conversion logic
}
}
// add this in a bootstrapper in your app
Mapper.CreateMap<string, A>().ConvertUsing<ATypeConverter>();
now after you Map your objects it will convert them using your custom mapper, which will allow you to skip the items with change.

Property Type as Generic parameter

I'm trying to figure out how I can make a Generics call take a variable for the Type. In the call below it take a type "DAL.Account" and works fine.
var tst = ctx.GetTable<DAL.Account>().Where(t => t.Sbank == "000134");
I want to change that so that I can pass a variable in place of the "DAL.Account". Something like this but I know that won't work as you can't pass property as a Type.
ctx.GetTable<Criteria.EntityType>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray())
Below is the shell pieces of code I think explains what I'm trying to do. Generics is not my strong suit so I'm looking for some help. Is there anyway that I can make this happen?
//Stores a "Type" that indicates what Object is a Criteria for.
public class AccountCriteria : IGeneratedCriteria
{
...
public Type EntityType
{
get {return typeof(DAL.Account);}
}
}
//I have added a function to the DataContext called "GetTable"
// And then used it as an example in a Console App to test its functionality.
public class ADRPDataContext : NHibernateDataContext
{
...
public CodeSmith.Data.NHibernate.ITable<T> GetTable<T>() where T : EntityBase
{
var tb = new CodeSmith.Data.NHibernate.Table<T>(this);
return tb;
}
}
// console application that uses DataContext.GetTable
class Program
{
static void Main(string[] args)
{
using (var ctx = new ADRPDataContext())
{
var tst = ctx.GetTable<DAL.Account>().Where(t => t.Sbank == "000134");
}
}
}
//ExistsCommand class that uses the EntityType property of the Critera to generate the data.
public class ExistsCommand
{
private IGeneratedCriteria Criteria { get; set; }
protected override void DataPortal_Execute()
{
using (var ctx = new DC.ADRPDataContext())
{
//This was my first attempt but doesn't work becuase you can't pass a property in for a Type.
//But I can figure out how to write this so that it will work.
Result = ctx.GetTable<Criteria.EntityType>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray()).Count() > 0;
}
}
}
You are looking to instantiate a generic type. Some info can be found here
This is a simple example demonstrating how to instantiate a List with a capacity of 3. Here is a method that you can call to create a generic when you don't know the type:
public static Object CreateGenericListOfType(Type typeGenericWillBe)
{
//alternative to the followin:
//List<String> myList = new List<String>(3);
//build parameters for the generic's constructor (obviously this code wouldn't work if you had different constructors for each potential type)
object[] constructorArgs = new Object[1];
constructorArgs[0] = 3;
//instantiate the generic. Same as calling the one line example (commented out) above. Results in a List<String> with 3 list items
Type genericListType = typeof(List<>);
Type[] typeArgs = { typeGenericWillBe };
Type myNewGeneric = genericListType.MakeGenericType(typeArgs);
object GenericOfType = Activator.CreateInstance(myNewGeneric, constructorArgs);
return GenericOfType;
}
And here is some sample code that will show you the example method works:
List<String> Strings = (List<String>)InstantiateGenericTypeWithReflection.CreateGenericListOfType(typeof(String));
//demonstrate the object is actually a List<String> and we can do stuff like use linq extensions (isn't a good use of linq but serves as example)
Strings.Add("frist");
Strings.Add("2nd");
Strings.Add("tird");
Console.WriteLine("item index 2 value: " + Strings.Where(strings => strings == "2").First());
In your example, replace your GetTable<Criteria.EntityType>() with CreateGenericTableOfType(Criteria.EntityType). This will return a generic table of whatever type you pass in. You will of course need to implement the method properly (handle constructor args, change List to Table etc).
I think you need to change the way you're doing this slightly, and instead use generics instead of the EntityType property. Perhaps something along the lines of the following:
// Create an abstract class to be used as the base for classes that are supported by
// ExistsCommand and any other classes where you need a similar pattern
public abstract class ExtendedCriteria<T> : IGeneratedCriteria
{
public ExistsCommand GetExistsCommand()
{
return new ExistsCommand<T>(this);
}
}
// Make the non-generic ExistsCommand abstract
public abstract class ExistsCommand
{
protected abstract void DataPortal_Execute();
}
// Create a generic sub-class of ExistsCommand with the type parameter used in the GetTable call
// where you were previously trying to use the EntityType property
public class ExistsCommand<T> : ExistsCommand
{
protected override void DataPortal_Execute()
{
using (var ctx = new DC.ADRPDataContext())
{
Result = ctx.GetTable<T>().Where(LinqToSQLHelper.BuildWhereStatement(Criteria.StateBag), Criteria.StateBag.Values.ToArray()).Count() > 0;
}
}
}
// Derive the AccountCriteria from ExtendedCriteria<T> with T the entity type
public class AccountCriteria : ExtendedCriteria<DAL.Account>
{
...
}

Determine if a generic type has a corresponding implementation

I have a series of static methods to modify a collection then return the modified collection:
private static IEnumerable<Invoice> ResolveProxies(IEnumerable<Invoice> e) {
// do something to e
return e;
}
private static IEnumerable<Person> ResolveProxies(IEnumerable<Person> e) {
// do something to e
return e;
}
In another part of the application there is a method to decide if a collection is of a certain type, so that it can be converted to that type and have its corresponding ResolveProxies method called:
public static GridModel<T> ToGridModel<T>(this GridModel gridModel) {
// gridModel.Data is just IEnumerable
var collection = gridModel.Data as IEnumerable<T> ?? new List<T>();
return new GridModel<T> {
Data = EvaluateDynamicProxies(collection),
Total = gridModel.Total
};
}
private static IEnumerable<T> EvaluateProxies<T>(IEnumerable<T> collection) {
if (collection is IEnumerable<Invoice>) {
var enumeration = (collection as IEnumerable<Invoice>);
return ResolveProxies(enumeration) as IEnumerable<T>;
}
if (collection is IEnumerable<Person>) {
var enumeration = (collection as IEnumerable<Person>);
return ResolveProxies(enumeration) as IEnumerable<T>;
}
// proxy resolution isn't needed so return the unchanged collection
return collection;
}
Having such repetitive conditional logic is bad code smell. I'm struggling to come up with some way to mark particular types so that I know they have a corresponding proxy resolver method. Something like this perhaps:
public interface IProxyResolver<out T> where T:IEnumerable<T> {
T ResolveProxies();
}
But how would I use this? In effect I need a way to ask the compiler:
Does T have a matching ResolveProxies method?
What is the name of the class or method that resolves proxies for T so that I can get an instance of it and call it?
You could use an inversion of control (IOC) framework. For example, my team uses Castle Windsor. You can register services (usually interfaces) and types that provide the services. It has some nice generics resolution, so you can do things like this:
interface IProxyResolver<T> { /* whatever */ }
class ProxyResolver<T> : IProxyResolver<T> { /* ... */ }
class PersonProxyResolver : ProxyResolver<Person> { }
class InvoiceProxyResolver : ProxyResolver<Invoice> { }
then, you can summon these types like this:
void SomeMethodThatNeedsAProxyResolver<T>(T obj)
{
var resolver = ioc.Resolve<IProxyResolver<T>>();
//...
}
If you've regsitered the classes above, when T is Person or Invoice, you get the correct non-generic subclass of ProxyResolver; if it is any other type, you get the default generic superclass. Of course, you can structure things differently; if you need a specific proxy resolver for every type, that's possible too.
How about using a custom attribute? This is how custom serializers are selected, etc.
You'd start by defining the Attribute class:
public class ProxyResolverAttribute : Attribute {
public Type ResolverType { get; set; }
public ProxyResolver(Type resolverType) { ResolverType = resolverType; }
}
and then put that on the type contained, e.g.
[ProxyResolver(TypeOf(InvoiceProxyResolver))]
public class Invoice ... { ... }
then use reflection to see if the generic type used in the collection specifies a proxy resolver type:
// Untested, beware of bugs
var enumerationGenericType = enumeration.GetType().GetGenericArguments().FirstOrDefault();
var resolverAttribute = enumerationGenericType.GetType().GetCustomAttributes(TypeOf(ProxyResolverAttribute)).FirstOrDefault();
if (resolverAttribute != null) {
var resolverType = resolverAttribute.ResolverType;
// instanciate something of resolverType here
}
EDIT: Reading the comments, if you don't want to apply the attributes to the contained objects, I'd suggest creating custom classes which inherit List and apply the attribute there, e.g.
[ProxyResolver(TypeOf(InvoiceProxyResolver))]
public class InvoiceList : List<Invoice>

Is there any way to infer an Action type, or a full Action?

I find myself (too) often using a construct like the following:
class MyClass
{
public TypeA ObjectA;
public TypeB ObjectB;
public TypeC ObjectC;
public List<TypeD> ListOfObjectD = new List<TypeD>();
public void DoSmth()
{
return SomeConstruct(
/*...*/
new Setter<TypeA>(a => ObjectA = a), // these are the
new Setter<TypeB>(b => ObjectB = b), // things I'm trying
new Setter<TypeC>(c => ObjectC = c), // to make shorter
new Setter<TypeD>(d => ListOfObjectD.Add(d)),
/*...*/
);
}
}
class Setter<T>
{
public Action<T> Action;
public Setter(Action<T> action)
{
Action = action;
}
}
Is there any way for the Setter class to infer the type of the Action and create the standard (T obj) => Member = obj Action by only passing the Member in some way? I'm thinking of something like:
new Setter(ObjectA)
which of course is not valid syntax, but should give you an idea what I'm trying to achieve. I'm using this construct literally hundreds of time in my code, so the code
saved by this small change would be tremendous.
Edit: Added the TypeD example. The part
new Setter<TypeD>(d => ListOfObjectD.Add(d))
can be simplified to
new Setter<TypeD>(ListOfObjectD.Add)
which is awesome because it cuts from the redundant code. If only <TypeD> could also be inferred it would be perfect. I'm looking for something like this for the others.
#Lazarus - basically the purpose is to return setters, so other objects can set certain members of the class (or it can do other stuff defined in the Action) without accessing the class itself, only the Setter object. The full list of reasons is long and convoluted, but the structuring of the program works like a charm and I doubt needs changing (the example of course is simplified and doesn't really make sense as is).
Edit 2: I found a good way to simplify things for List's:
static class SetterHelper
{
public static Setter<T> GetSetter<T>(this List<T> list)
{
return new Setter<T>(list.Add);
}
}
Now I can just use this:
ListOfObjectD.GetSetter()
which works perfectly! why can't I do the same for T directly? I tried this:
static class SetterHelper
{
public static Setter<T> GetSetter<T>(this T item)
{
return new Setter<T>(t => item = t); // THIS DOESN'T SET THE PASSED MEMBER
}
}
Of course it won't work as intended because it will set item, but not the passed member. I tried adding ref as (ref this T item) but it won't compile :(... It would have been perfect.
Best I can offer you is the following syntax:
Setter.For( () => ObjectA );
using this helper class
static class Setter
{
public static Setter<T> For<T>(Expression<Func<T>> e)
{
ParameterExpression[] args = { Expression.Parameter(((e.Body as MemberExpression).Member as FieldInfo).FieldType) };
Action<T> s = Expression.Lambda<Action<T>>(Expression.Assign(e.Body, args[0]), args).Compile();
return new Setter<T>(s);
}
}

Categories

Resources