Failing to shorten similar function calls using generics - c#

I'm having huge problems with my code responsible for loading data from a database and converting it to lists of my custom data model objects.
As there are many data tables I'm going to have many Lists and I don't really want to create and assign them manually using copy/paste and just modifying the types.
Here is what I have for now, but there are errors displayed inside the LoadAllModelListsAsProperties method about failing conversions between Type and generic type parameters.
I can't figure out how get around this incompatibility of Type and <T>. What should I do?
public class DataProvider
{
private Dictionary<Type, object> Memory { get; set; }
private static Dictionary<Type, Type> AllModelTypes { get; }
static DataProvider()
{
AllModelTypes = new Dictionary<Type, Type>()
{
{ typeof(AreaModel), typeof(Areas) },
{ typeof(GroupModel), typeof(Groups) },
/* many more.... */
{ typeof(TownModel), typeof(Towns) }
};
}
public DataProvider()
{
// fill Memory dictionary with empty list instances
foreach (Type ModelType in AllModelTypes.Keys)
{
Memory.Add(ModelType, Activator.CreateInstance(
typeof(List<>).MakeGenericType(ModelType)));
}
}
public List<TModel> GetModelList<TModel>(Type modelType)
where TModel : ModelBase
{
// get the list from memory that matches the given type
return (List<TModel>)Memory[modelType];
}
public void LoadAllModelListsAsProperties()
{
var filter = (c) => true; // just simplified as example
foreach (KeyValuePair<Type, Type> item in AllModelTypes)
{
Type modelType = item.Key;
Type linqType = item.Value;
List<ModelBase> modelList = GetModelList<modelType>(modelType);
// !!!! ^^^^^^ error that it can't implicitly convert
// List<modelType> to List<Modelbase>
modelList.Clear();
modelList.AddRange(LoadListOfModels<linqType, modelType>(filter,
modelType.ModelFactoryFromLinq);
// !!!! ^^^^^^ error that "linqType"/"modelType" are variables,
// but get used like types; and that
// Type has no definition for "ModelFactoryFromLinq
}
// normally I would have to call something like that for every list type:
//AreaModels = LoadListOfModels<Areas, AreaModel>(
// filter, AreaModel.ModelFactoryFromLinq);
//GroupModels = LoadListOfModels<Groups, GroupModel>(
// filter, GroupModel.ModelFactoryFromLinq);
//TownModels = LoadListOfModels<Towns, TownModel>(
// filter, TownModel.ModelFactoryFromLinq);
}
public List<TModel> LoadListOfModels<TLinq, TModel>(
Func<TLinq, bool> filter,
Func<TLinq, TModel> modelFactory
)
where TLinq : class, ILinqClass
where TModel : ModelBase
{
using (LinqToSqlDataContext dc = new LinqToSqlDataContext())
{
return dc.GetTable<TLinq>()
.Where(filter)
.Select(modelFactory)
.ToList();
}
}

You cannot use a type variable as type
You can use the Base class as type => GetModelList<ModelBase>(modelType);
You dont need to use type for method LoadListOfModels when you pass the correct objects to the method. The compiler will determine the generic type from the parameters.
What is ModelFactoryFromLinq? If it is a property, you just have to use reflection or you could create a method to return the correct func<..> via the given type. If it is a method try this:
foreach (KeyValuePair<Type, Type> item in AllModelTypes)
{
Type modelType = item.Key;
Type linqType = item.Value;
List<ModelBase> modelList = GetModelList<ModelBase>(modelType);
// !!!! ^^^^^^ error that it can't implicitly convert
// List<modelType> to List<Modelbase>
modelList.Clear();
var ModelFactoryFromLinq = modelType.GetMethod("ModelFactoryFromLinq");
modelList.AddRange(LoadListOfModels(filter, modelFactoryFromLinq));
// !!!! ^^^^^^ error that "linqType"/"modelType" are variables,
// but get used like types; and that
// Type has no definition for "ModelFactoryFromLinq
}

Related

Can the EF Core mapping be overriden to change the type of data from the database to my model?

My model has a column that's of type, HierarchyId. I want this to be a string instead. When I change the property type to string, it's unable to automatically convert that type to a string. Is there a way to override the mapping so I can manually convert it to a string?
This is how I am performing the query:
public List<T> GetData<T>(int startIndex, int batchSize) where T : class
{
var models = new List<T>();
var dbSet = _context.Set<T>();
var data = dbSet.Skip(startIndex).Take(batchSize);
foreach (var item in data as IEnumerable)
{
//Converts the deserialized item to the generic model type that was passed in
var model = (T)Convert.ChangeType(item, typeof(T));
models.Add(model);
}
return models;
}
The error I get happens when I try to loop through the data. This error shows it converting to a byte. I was just trying different things:
Unable to cast object of type
'Microsoft.SqlServer.Types.SqlHierarchyId' to type
'Microsoft.Data.SqlClient.Server.IBinarySerialize
modelBuilder.Entity<Project_Task>()
.Property(p => p.Outliner)
.HasConversion<HierarchyIdConverter>();
public class HierarchyIdConverter : ValueConverter<string, HierarchyId>
{
public HierarchyIdConverter()
: base(
v => HierarchyId.Parse(v),
v => v.ToString())
{
}
}
It was a little confusing because the order of the types and conversions were flipped. But this worked.

Converting c# methods to a generic method

How can I convert these two ConvertDtoListToAddresses and ConvertDtoListToDocuments C# methods to a generic? I've tried passing in two generic type variables, but when I get down to 'Add' in the loop I get stuck on various errors. Converting from a DTO to its respective DBO is done in the constructor of the DBO, which I think is part of the problem.
private void ConvertDtoToPerson(PersonDTO dto)
{
Id = dto.Id;
Fname = dto.FirstName;
Mname = dto.MiddleName;
Lname = dto.LastName;
Suffix = dto.Suffix;
Maiden = dto.MaidenName;
Addresses = ConvertDtoListToAddresses(dto.AddressesDto); // want to convert to generic
Documents = ConvertDtoListToDocuments(dto.DocumentsDto); // want to convert to generic
}
private static ICollection<Address>? ConvertDtoListToAddresses(ICollection<AddressDTO>? addressesDto)
{
if (addressesDto is not null && addressesDto.Any())
{
ICollection<Address> addys = new List<Address>();
foreach (AddressDTO dto in addressesDto)
{
// Converts from dto in constructor
addys.Add(new Address(dto));
}
return addys;
}
return null;
}
private static ICollection<Document>? ConvertDtoListToDocuments(ICollection<DocumentDTO>? documentsDto)
{
if (documentsDto is not null && documentsDto.Any())
{
ICollection<Document> docs = new List<Document>();
foreach (DocumentDTO dto in documentsDto)
{
// Converts from dto in constructor
docs.Add(new Document(dto));
}
return docs;
}
return null;
}
Here is what I tried:
Addresses = ConvertDtoListToType<Address, AddressDTO>(dto.AddressesDto);
private static ICollection<T>? ConvertDtoListToType<T, D>(ICollection<D>? dtoCollection)
{
if (dtoCollection is not null && dtoCollection.Any())
{
ICollection<T> tList = new List<T>();
foreach (D dto in dtoCollection)
{
tList.Add(new T(dto)); // <-- This is where I'm having trouble
}
return tList;
}
return null;
}
Use of a Func<D, T> factory parameter would sort this out.
private static ICollection<T>? ConvertDtoListToType<T, D>(ICollection<D>? dtoCollection, Func<D, T> factory)
{
if (dtoCollection is not null && dtoCollection.Any())
{
ICollection<T> tList = new List<T>();
foreach (D dto in dtoCollection)
{
tList.Add(factory(dto));
}
return tList;
}
return null;
}
Do keep in mind that that is almost the semantic equivalent of this:
private static ICollection<T>? ConvertDtoListToType<T, D>(ICollection<D>? dtoCollection, Func<D, T> factory)
=> dtoCollection?.Select(d => factory(d))?.ToList();
I'd question the idea that an empty dtoCollection should return a null final collection anyway. This is probably a better implementation.
So, having said that, your original method offers very little functionality benefit. It's code for code's sake. A simple Select/ToList pair keeps your code simple.
In any case, you can provide a static method off of Address and Document to provide the Func<D, T> that you need.
public class Address
{
AddressDTO dto;
public static Address CreateFromDto(AddressDTO dto)
=> new Address(dto);
public Address(AddressDTO dto)
{
this.dto = dto;
}
}
Now, calling it is like this:
var addresses = ConvertDtoListToType(addressDtos, Address.CreateFromDto);
Or:
var addresses = addressDtos?.Select(Address.CreateFromDto)?.ToList();
What you need is to be able to provide a constraint on the Type T to say that it has a constructor that takes a parameter of type D. Something like this:
private static ICollection<T>? ConvertDtoListToType<T, D>(
ICollection<D>? dtoCollection) where T : new(D)
{}
But this does not exist in C#. The workaround is to provide a factory method to create your type T given a type D. i.e. Something like:
private static ICollection<T>? ConvertDtoListToType<T, D>(
ICollection<D>? dtoCollection, Func<D, T> factory)
{
// Use factory(dto), instead of new T(dto)
}
But as #Zee says, you should have a look at Automapper, which can convert between types of collections.

parse type hierarchy in assembly

Consider the following types in an assembly: BusinessPartnerList, BusinessPartner, PrivateData, CompanyData, AddressList, Address
Type BusinessPartnerList
{
BusinessPartner[]
}
Type BusinessPartner
{
PrivateData
CompanyData
AddressList
}
Type PrivateData
{
System.String FirstName
System.String SurName
}
Type PrivateData
{
System.String CompanName1
System.String CompanName2
}
Type AddressList
{
Address[]
}
I want to generic parse the type hierarchy, and represent them in a tree e.g. simple nodes
BusinessPartnerList[]
BusinessPartner
PrivateData
CompanyData
AddressList[]
Address
What is the best way to do this?
Unfortunately you didn't use proper C# syntax for your sample data. So I have to make some assumptions:
Type is actually class (or struct).
The contents of the types (BusinessPartner, PrivateData, CompanyData etc.) represent the types of some public properties.
To parse the type hierarchy you can use reflection. Find all public properties of a given type and return their types. Since you only want the types you can use a HashSet which will only contain distinct types:
public static HashSet<Type> GetPropertyTypes(Type type)
{
return new HashSet<Type>(type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select(prop => prop.PropertyType));
}
However, it seems that you don't want to get information on arrays but rather on the type of the array elements. The same goes for lists. So if a type implements IEnumerable<T> you want to get information on the type T:
private static Type GetElementType(Type type)
{
Type enumerableType = type.GetInterfaces().FirstOrDefault(IsGenericEnumerable);
if (enumerableType != null)
{
Type[] genericArguments = enumerableType.GetGenericArguments();
return genericArguments[0];
}
// return 'object' for a non-generic IEnumerable
return typeof(IEnumerable).IsAssignableFrom(type) ? typeof(object) : type;
}
private static bool IsGenericEnumerable(Type type)
{
return type.IsGenericType &&
type.GetGenericTypeDefinition() == typeof(IEnumerable<>);
}
Note that for the type System.String this will return char because string implements IEnumerable<char> (I will adress that later).
The .NET framework does not have a tree structure you can use out of the box. So you need to implement it yourself:
public class Node<T>
{
public Node(T value, IEnumerable<Node<T>> children)
{
Value = value;
Children = children.ToList();
}
public T Value
{
get;
private set;
}
public List<Node<T>> Children
{
get;
private set;
}
}
This is a very basic implementation just for demonstration purposes.
Instead of returning List<Type> the GetPropertyTypes method can now return Node<Type> and it should be renamed to CreateTypeNode:
public static Node<Type> CreateTypeNode(Type type)
{
var children = type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select(prop => GetElementType(prop.PropertyType))
.Select(CreateTypeNode);
return new Node<Type>(type, children);
}
This method uses recursion to create the full tree for the given type.
There is still a problem: What if type A references type B and vice versa? This would end up in an infinite recursive loop. And also: if a type has already been visited there is no need to do that again.
What we need is a cache for the types that have been visited. If a type is in the cache we use the information from the cache:
private static readonly Dictionary<Type, Node<Type>> _visitedTypes = new Dictionary<Type, Node<Type>>();
public static Node<Type> CreateTypeNode(Type type)
{
Node<Type> node;
if (_visitedTypes.TryGetValue(type, out node))
{
return node;
}
// add the key to the cache to prevent infinite recursion; the value will be set later
// if this type will be found again in a recursive call CreateTypeNode returns null
// (null will be filtered out then)
_visitedTypes.Add(type, null);
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
var types = new HashSet<Type>(properties.Select(prop => GetElementType(prop.PropertyType)));
var children = types.Select(CreateTypeNode).Where(n => n != null);
node = new Node<Type>(type, children);
_visitedTypes[type] = node;
return node;
}
I you don't want the string type to be reported as char (because string implements IEnumerable<char>) you can just add a node for string to the cache before you call GetOrCreateTypeNode for the first time:
_visitedTypes.Add(typeof(string), new Node<Type>(typeof(string), new List<Node<Type>>()));
Then check the cache in the GetElementType method:
private static Type GetElementType(Type type)
{
if (_visitedTypes.ContainsKey(type))
{
return type;
}
...
}

Casting List<object> to List<T> at runtime

I 'm trying to build a DI container and I 've stumbled on to the following problem: I have a method that retrieves a list of registered instances for a given type and I want to use that to inject IEnumerable<T> properties in a given object. An example of what I am trying to achieve would be the following:
class A { public IList<IExample> Objects { get; set; } }
class B: IExample {}
class C: IExample {}
Container.Register<IExample>(new B());
Container.Register<IExample>(new C());
var obj = new A();
Container.Inject(A);
Debug.Assert(A.Objects != null && A.Objects.Count == 2);
My Retrieve method returns an IList<object>, mainly because I have no type information at that moment, so I am attempting to convert that list into a List<T> at injection time. Here is a succint form of the methods doing the work:
public virtual IList<object> Retrieve(Type type)
{
var instances = Registry[type];
foreach(var instance in instances)
Inject(type, instance); // omitted
return instances;
}
public virtual void Inject<T>(T instance)
{
var properties = typeof (T).GetProperties();
foreach (var propertyInfo in properties)
{
var propertyType = propertyInfo.PropertyType;
if (!IsIEnumerable(propertyType)) continue;
var genericType = propertyType.GetGenericArguments()[0];
propertyInfo.SetValue(instance,
GetListType(genericType, Retrieve(genericType)), null);
}
}
protected virtual object GetListType(Type type, IEnumerable<object> items)
{
return items.Select(item => Convert.ChangeType(item, type)).ToList();
}
The code returns the error: System.InvalidCastException : Object must implement IConvertible. Sadly, I don't know how to proceed from here. Perhaps I am doing this all wrong. I 've thought of using generics or injecting multiple properties by hand, but I'd really like to not have to do that.
Thanks in advance for any help or ideas.
You could create a generic list like this:
public virtual IList Retrieve(Type type)
{
// ...
listType = typeof(List<>).MakeGenericType(new Type[] { type });
IList list = (IList)Activator.CreateInstance(listType);
// ...
return list
}
this list can be casted to IList<T>, because it is one.
You could consider to use IEnumerable and Cast<T>, but then you don't have an instance of a list. I don'^t know how important it is to have one.

Having trouble with Generics in this .NET code

i'm trying to make a mixed collection of Types. I know the types at the start.. but I can't seem to figure out the syntax to make the collection, etc.
eg.
....
// I leave the typo there, for embarrassment :(
Initialize(new []{ typeof(Cat), typeof(Dog), typeof(JohnSkeet) });
...
public Foo Initialize(IEnumerable<Type> types)
{
// for each type, set up the inmemory storage.
foreach(var type in types)
{
// ????
// Create an empty list, which will only contain this 'type'
// I'm guessing, an IDictionary<type, ICollection<type>>().. thingy ?
}
}
public ICollection<Type> SomeTypeData(Type type)
{
// Return the collection, for this type.
}
Does this mane sense? Is this possible?
Okay, now that I think I know what you want, it would look something like this:
// This can't really be *properly* statically typed
private readonly Dictionary<Type, object> typeMap = new
Dictionary<Type, object>();
public Foo Initialize(IEnumerable<Type> types)
{
Type genericListType = typeof(List<>);
foreach(var type in types)
{
// MakeGenericType is really badly named
Type constructedListType = genericListType.MakeGenericType(type);
typeMap[type] = Activator.CreateInstance(constructedListType);
}
}
// We can't express this particularly safely either,
// although we *could* return the non-generic IList
public object SomeTypeData(Type type)
{
return typeMap[type];
}
// This *is* statically typed, although we need to cast inside
public IList<T> SomeTypeData<T>()
{
return (IList<T>) typeMap[typeof(T)];
}
See this blog post for a similar example.
Note that basically you're trying to represent something which generics simply can't handle, in terms of the internal dictionary type... and the first form of SomeTypeData can't be statically typed either... because that means knowing the type at compile time when we'll only actually be given it at execution time.
It looks to me like you're trying to create some kind of instance repository; a class that stores a list of instances of a given type.
Here's an example implementation. I've included both a generic and non-generic version of the SomeTypeData method:
public class InstanceRepository
{
private IDictionary<Type, ICollection> _Instances = new Dictionary<Type, ICollection>();
public ICollection SomeTypeData(Type type)
{
ICollection instanceList;
if (!_Instances.TryGetValue(type, out instanceList))
{
// this type does not exist in our dictionary, so let's create a new empty list
// we could do this:
//instanceList = new List<object>();
// but let's use reflection to make a more type-specific List<T> instance:
instanceList = (ICollection)Activator.CreateInstance(typeof(List<>).MakeGenericType(type));
// now add it to the dictionary
_Instances.Add(type, instanceList);
}
// Return the collection, for this type.
return instanceList;
}
public IList<T> SomeTypeData<T>()
{
Type type = typeof(T);
ICollection instanceList;
if (!_Instances.TryGetValue(typeof(T), out instanceList))
{
instanceList = new List<T>();
_Instances.Add(type, instanceList);
}
// here we are assuming that all of the lists in our dictionary implement IList<T>.
// This is a pretty safe assumption, since the dictionary is private and we know that
// this class always creates List<T> objects to put into the dictionary.
return (IList<T>)instanceList;
}
}
Below is a usage example:
Generic:
InstanceRepository repository = new InstanceRepository();
var listOfCats = repository.SomeTypeData<Cat>();
listOfCats.Add(new Cat());
Cat firstCat = listOfCats[0];
Console.WriteLine(listOfCats.GetType().FullName);
Non-Generic:
InstanceRepository repository = new InstanceRepository();
var listOfCats = (IList<Cat>)repository.SomeTypeData(typeof(Cat));
listOfCats.Add(new Cat());
Cat firstCat = listOfCats[0];
Console.WriteLine(listOfCats.GetType().FullName);
I guess you want something like
_dict[typeof(Cat)]=new List<Cat>();
_dict[typeof(Dog)]=new List<Dog>();
only programatically based on given types?
Something like this should work:
public void Initialize(IEnumerable<Type> types)
{
foreach(var type in types)
{
var list = Activator.CreateInstance(Type.GetType("System.Collections.Generic.List`1").MakeGenericType(type));
_cache[type] = list;
}
}
public ICollection<T> Get<T>()
{
object list;
if (_cache.TryGetValue(typeof(T), out list)
{
return list as ICollection<T>;
}
else
{
...
}
}
var cats = Get<Cat>();
I'm not sure I fully understand you're question, but if you already have an IEnumerable<Type> which contains an enumeration of Type objects, then why not just use that to initialize some type of Collection (such as List<Type>)?
public ICollection<Type> Initialize(IEnumerable<Type> types)
{
ICollection<Type> collection = new List<Type>(types);
return collection;
}

Categories