I have below OSM material data structure that is different than entities and i have methods inside entities which i am forming compatible OSM material using AddToOsm method
public class FenestrationMaterial : Material
{ }
public class StandardOpaqueMaterial : OpaqueMaterial
{ }
public class OpaqueMaterial : Material
{ }
public class SurfaceConstruction : IIdentity<Guid>
{
[Key]
public Guid Id { get; set; }
public Guid SurfaceTypeId { get; set; }
public IntendedSurfaceType SurfaceType { get; set; }
public List<Guid> LayerIds { get; set; }
public Construction AddToOsm(Model model, APIDbContext dbContext)
{
var construction = new Construction(model);
using var materials = new MaterialVector();
var fenestrationMaterialById = new Dictionary<Guid, FenestrationMaterial>();
var standardOpaqueMaterialById = new Dictionary<Guid, StandardOpaqueMaterial>();
var opaqueMaterialById = new Dictionary<Guid, OpaqueMaterial>();
foreach (var materialId in LayerIds.Where(i => i != default))
{
if (ProjectUtils.EntityById<OpaqueProjectMaterial>(dbContext, materialId) != default)
{
var OpaqueProjectMaterial = ProjectUtils.EntityById<OpaqueProjectMaterial>(dbContext, materialId);
materials.Add(
standardOpaqueMaterialById.GetOrCreate(OpaqueProjectMaterial.Id, () => OpaqueProjectMaterial.AddToOsm(model))
);
continue;
}
if (ProjectUtils.EntityById<AirGapMaterial>(dbContext, materialId) != default)
{
var airGapMaterial = ProjectUtils.EntityById<AirGapMaterial>(dbContext, materialId);
materials.Add(
opaqueMaterialById.GetOrCreate(airGapMaterial.Id, () => airGapMaterial.AddToOsm(model))
);
continue;
}
if (ProjectUtils.EntityById<GlazingMaterial>(dbContext, materialId) != default)
{
var glazingMaterial = ProjectUtils.EntityById<GlazingMaterial>(dbContext, materialId);
materials.Add(
fenestrationMaterialById.GetOrCreate(glazingMaterial.Id, () => glazingMaterial.AddToOsm(model))
);
continue;
}
}
construction.setLayers(materials);
return construction;
}
}
and then i do have entities for airGapmaterial, OpaqueProjectMaterial and GlazingMaterial
public class AirGapMaterial : ISourceOfData, IIdentity<Guid>
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
......
......
public OpaqueMaterial AddToOsm(Model model)
{
if (model is null)
{
throw new ArgumentNullException(nameof(model));
}
var airGapMaterial = new AirGap(model);
airGapMaterial.setName(this.Name);
.......
return airGapMaterial;
}
}
public class GlazingMaterial : ISourceOfData, IIdentity<Guid>
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; }
.......
......
public FenestrationMaterial AddToOsm(Model model)
{
if (model is null)
{
throw new ArgumentNullException(nameof(model));
}
var glazingMaterialComplexModel = new StandardGlazing(model);
glazingMaterialComplexModel.setName(this.Name);
........
return glazingMaterialComplexModel;
}
}
Is there any way I can use generic variable in-place of FenestrationMaterial and StandardOpaqueMaterial in the initialization of dictionaries (fenestrationMaterialById, standardOpaqueMaterialById) and extract the below common methods into single one?
I am looking kind of like this if possible
var fenestrationMaterialById = new Dictionary<Guid, T>();
That took a bit longer then I expected. Turns out the ProjectUtils bit really does throw a wrench into the works. In the following I trimmed things down to just the core concepts, added types missing from your post and did some refactoring to try and stream line things as much as possible. There is still plenty of room for improvement.
I also had to make a ton of assumptions about the underlying data and undocumented components. It looks to me like it would do what you're looking for but its certainly possible that I've made an assumption that is breaking on your end. I also tried to follow the pattern with the Util and DbContext usage, however, to me, those look pretty weak and should probably be reworked.
The core section you wanted to refactor is now:
public Construction AddToOsm(APIDbContext dbContext, Model model, IEnumerable<Guid> ids)
{
if(model is null) throw new ArgumentNullException(nameof(model));
var materialSources = ids.Where(i => i != default)
.Select(i => _projectUtils.EntityById<SourceData>(dbContext, i))
.Select(i => _projectUtils.ResolveToMaterialSource(i));
using var materialVector = new MaterialVector();
foreach (var materialSource in materialSources)
{
materialVector.Add(materialSource.AddToOsm(model));
}
return new Construction(model, materialVector);
}
And here's the rest, it should compile.
public class Model { }
public class Construction
{
public Construction(Model model, MaterialVector materials)
{
SetLayers(materials);
}
internal void SetLayers(MaterialVector materials)
{
}
}
public interface ISourceOfData
{
public string Name { get; set; }
}
public interface IIdentity<T>
{
[Key]
public Guid Id { get; set; }
}
public class IntendedSurfaceType { }
public class APIDbContext { }
public class KeyAttribute : Attribute { }
public class MaterialVector : IDisposable
{
public void Dispose()
{
}
internal void Add(IMaterial p)
{
}
}
public interface IMaterialTypeResolver
{
public IMaterialSource ResolveType(SourceData i);
}
public class MaterialTypeResolver : IMaterialTypeResolver
{
public IMaterialSource ResolveType(SourceData sourceData)
{
//Problem here - I don't know how your ProjectUtils.EntityById works internally
//So I'm guessing here that it uses a factory pattern
//this method needs to return an IMaterialSource...
//GlazingMaterialSource
//AirGapMaterialSource
//ect ect
//for a given set of database values
//essentially you need something coming from the database to tell the code
//which type to create, we pass that the data base values
//and it passes them later to the IMaterial in the AddToOsm call
//so the IMaterial implementation can do whatever custom work
return new OpaqueProjectMaterialSource(sourceData);
}
}
public class ProjectUtils
{
private IMaterialTypeResolver _MaterialTypeResolver;
public ProjectUtils(IMaterialTypeResolver materialTypeResolver)
{
_MaterialTypeResolver = materialTypeResolver;
}
public T EntityById<T>(APIDbContext dbContext, Guid materialId) where T : new()
//here you'd use the dbContext to populated this
//though this one at a time stuff is pretty inefficient
//it should be done as a single query if possible
=> new T();
public IMaterialSource ResolveToMaterialSource(SourceData i) => _MaterialTypeResolver.ResolveType(i);
}
public interface IMaterial
{
public string Name { get; set;}
}
public abstract class MaterialBase : IMaterial
{
public string Name {get;set;}
public MaterialBase(Model model, ISourceOfData source)
{
this.Name = source.Name;
}
}
public class AirGapMaterial : MaterialBase
{
public AirGapMaterial(Model model, ISourceOfData source) : base(model, source)
{
}
}
public class StandardGlazingMaterial : MaterialBase
{
public StandardGlazingMaterial(Model model, ISourceOfData source) : base(model, source)
{
}
}
public class FenestrationMaterial : MaterialBase
{
public FenestrationMaterial(Model model, ISourceOfData source) : base(model, source)
{
}
}
public class StandardOpaqueMaterial : OpaqueMaterial
{
public StandardOpaqueMaterial(Model model, ISourceOfData source) : base(model, source)
{
}
}
public class OpaqueMaterial : MaterialBase
{
public OpaqueMaterial(Model model, ISourceOfData source) : base(model, source)
{
}
}
public class SurfaceConstruction : IIdentity<Guid>
{
[Key]
public Guid Id { get; set; }
private ProjectUtils _projectUtils;
public SurfaceConstruction(ProjectUtils projectUtils)
{
_projectUtils = projectUtils;
}
public Construction AddToOsm(APIDbContext dbContext, Model model, IEnumerable<Guid> ids)
{
if(model is null) throw new ArgumentNullException(nameof(model));
var materialSources = ids.Where(i => i != default)
.Select(i => _projectUtils.EntityById<SourceData>(dbContext, i))
.Select(i => _projectUtils.ResolveToMaterialSource(i));
using var materialVector = new MaterialVector();
foreach (var materialSource in materialSources)
{
materialVector.Add(materialSource.AddToOsm(model));
}
return new Construction(model, materialVector);
}
}
public interface IMaterialSource
{
public IMaterial AddToOsm(Model model);
}
public class SourceData : ISourceOfData, IIdentity<Guid>
{
[Key]
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
//add all the database loaded properties here
}
public abstract class MaterialSourceBase : IMaterialSource
{
protected SourceData SourceData {get;set;}
protected MaterialSourceBase(SourceData sourceData)
{
SourceData = sourceData;
}
public abstract IMaterial AddToOsm(Model model);
}
//So at this point you could generalize all of this down to a single
//factory pattern backed function as all we care about are
//given a set of database values, give me an IMaterial
public class OpaqueProjectMaterialSource : MaterialSourceBase
{
public OpaqueProjectMaterialSource(SourceData sourceData) : base(sourceData)
{
}
public override IMaterial AddToOsm(Model model) => new StandardOpaqueMaterial(model, base.SourceData);
}
public class AirGapMaterialSource : MaterialSourceBase
{
public AirGapMaterialSource(SourceData sourceData) : base(sourceData)
{
}
public override IMaterial AddToOsm(Model model) => new AirGapMaterial(model, base.SourceData);
}
public class GlazingMaterialSource : MaterialSourceBase
{
public GlazingMaterialSource(SourceData sourceData) : base(sourceData)
{
}
public override IMaterial AddToOsm(Model model) => new StandardGlazingMaterial(model, base.SourceData);
}
Related
I have a problem with a IoC with Simple Injector for Decorator..
How to register it in the Ioc Simple injector ?
var data = new Data();
var test = new Data2Decorator (data,
new Data1Decorator(data, new XxxData(data)))
I tried it this way.
_container.Register<Ixxx1>();
_container.Register<Ixxx2>();
_container.Register<Ixxx, Data>();
_container.Register<IData, XxxData>();
container.RegisterDecorator<IData, Data1Decorator>();
_container.RegisterDecorator<IData, Data2Decorator>():
public interface Ixxx1
{
public IEnumerable<...> x1 { get; set; }
}
public class Data1Decorator : DataDecorator
{
public readonly Ixxx1 _xxx1;
public Data1Decorator(Ixxx1 xxx1, IData data) : base(data)
{
_xxx1 = xxx1;
}
public override Task Load()
{
return Task.WhenAll(new List<Task>() { ..., base.Load() })
.ContinueWith((b) =>
{
_xxx1.x1 = ...
});
}
}
Common storage Data
public interface Ixxx :
Ixxx1,
Ixxx2
{
}
public class Data: Ixxx
{
public IEnumerable<...> x1 { get; set; }
public IEnumerable<...> x2 { get; set; }
}
_container.Register<Ixxx, Data>();
_container.Register<IData<Ixxx>, XxxData>();
_container.RegisterDecorator<IData<Ixxx>, Data1Decorator<Ixxx>>();
_container.RegisterDecorator<IData<Ixxx>, Data2Decorator<Ixxx>>():
public interface IData<out TData>
{
TData Data { get; }
Task Load();
}
public abstract class DataDecorator<TData> : IData<TData>
{
public readonly IData<TData> _data;
public TData Data => _data.Data;
protected DataDecorator(IData<TData> data) => _data = data;
public virtual Task Load() => _data.Load();
}
public interface Ixxx :
Ixxx1,
Ixxx2
{
}
public class XxxData : IData<Ixxx>
{
public Ixxx Data { get; }
public XxxData(Ixxx data) => Data = data;
public Task Load() => Task.Run(() => { });
}
public interface Ixxx1
{
public IEnumerable<...> x1 { get; set; }
}
public class Data1Decorator<TData> : DataDecorator<TData>
where TData : Ixxx1
{
public Data1Decorator(IData data) : base(data)
{
}
public override Task Load()
{
return Task.WhenAll(new List<Task>() { ..., base.Load() })
.ContinueWith((b) =>
{
Data.x1 = ...
});
}
}
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
IQueryable extension method is fine in normal method, but not within a generic method.
Compile error: IQueryable does not contain a definition for MyExtension and the best extension method overload DataExtensions.MyExtension(IQueryable Fred) requires a receiver of type IQueryable Fred
Goal is turn the // do something interesting below into a generic method that would work acrross all FlintstoneObject types.
public static class RepetitiveCodeBelow
{
public static int RepetitiveCode()
{
var count = 0;
using (var context = new DataContext())
{
foreach (var data in context.Freds.AsNoTracking().Where(item => item.PrimaryKey > 0).MyExtension())
{
// do something interesting
}
foreach (var data in context.Barnies.AsNoTracking().Where(item => item.PrimaryKey > 0).MyExtension())
{
// do something interesting
}
// more types omitted
}
return count;
}
}
Working version:
public List<Fred> GetFredList()
{
using (var context = new DataContext())
{
return context.Freds.AsNoTracking().MyExtension().ToList();
}
}
Wont compile:
public List<T> GetList<T>() where T : FlintstoneObject<T>, new()
{
using (var context = new DataContext())
{
return context.Set<T>().AsNoTracking().MyExtension().ToList();
}
}
Full sample
public abstract class FlintstoneObject<T> where T : class
{
public abstract int PrimaryKey { get; set; }
}
public class Fred : FlintstoneObject<Fred>
{
public override int PrimaryKey { get; set; }
}
public class Barny : FlintstoneObject<Barny>
{
public override int PrimaryKey { get; set; }
}
public static class DataExtensions
{
public static IQueryable<Fred> MyExtension(this IQueryable<Fred> queryable)
{
return queryable;
}
}
public class DataContext : DbContext
{
public DbSet<Fred> Freds { get; set; }
public DbSet<Barny> Barnies { get; set; }
public List<Fred> GetFredList()
{
using (var context = new DataContext())
{
return context.Freds.AsNoTracking().MyExtension().ToList();
}
}
public List<T> GetList<T>() where T : FlintstoneObject<T>, new()
{
using (var context = new DataContext())
{
return context.Set<T>().AsNoTracking().MyExtension().ToList();
}
}
}
Your code is probably lacking of this extension:
public static IQueryable<T> MyExtension<T>(this IQueryable<T> queryable)
{
return queryable;
}
to make it compilable.
Anyway, what you intended to do with your generic stuff looks strange:
public class Fred : FlintstoneObject<Fred>
I thought T should be some class other than Fred which is inheriting the generic type FlintstoneObject<T>.
Is it possible that each class object has its own static data store?
I mean, just to perform actions like:
class Program
{
static void Main(string[] args)
{
var car1 = new Car();
car1.Save(); ////saves in its own storage
var own1 = new Owner();
own1.Save(); //saves in its own storage as well
}
}
In code I tried, I get such error
'System.InvalidCastException`
at this place
var repo = (Repository<IEntity>) CurrentRepository;
Whats wrong and how could I make it?
Whole code is here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
//var car1 = new Car();
//car1.Save(); ////saves in its own storage
//var own1 = new Owner();
//own1.Save(); //saves in its own storage as well var car1 = new Car();
}
}
public interface IEntity
{
long Id { get; }
}
public class Owner : Entity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Car Car { get; set; }
public Owner(string firstName, string lastName, Car car) : base(new Owner())
{
FirstName = firstName;
LastName = lastName;
Car = car;
}
public Owner() : base()
{
}
}
public class Car : Entity
{
public string Name { get; set; }
public int Year { get; set; }
public Car() : base()
{
}
public Car(string name, int year)
{
Name = name;
Year = year;
}
}
public abstract class Entity : IEntity
{
public long Id { get; }
public static object CurrentRepository { get; set; }
public Entity(Entity ent)
{
Type entityType = ent.GetType();
var instance = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(entityType));
CurrentRepository = instance;
}
public Entity()
{
}
public void Save()
{
var repo = (Repository<IEntity>)CurrentRepository;
repo.Save(this);
}
public void Delete()
{
var repo = (Repository<IEntity>)CurrentRepository;
repo.Delete(this);
}
}
public interface IRepository<T> where T : IEntity
{
void Save(T entity);
void Delete(T entity);
T Find(long id);
}
public class Repository<T> : IRepository<T> where T : class, IEntity
{
protected BaseStorage<T> CustomDataStorage;
public Repository()
{
CustomDataStorage = new BaseStorage<T>();
}
public void Save(T entity)
{
CustomDataStorage.Add(entity);
}
public void Delete(T entity)
{
CustomDataStorage.Remove(entity);
}
public T Find(long id)
{
throw new NotImplementedException();
}
}
public class BaseStorage<T> : IStorage<T>
{
List<T> data = new List<T>();
public void Add(T entity)
{
data.Add(entity);
}
public void Remove(T entity)
{
throw new NotImplementedException();
}
}
public interface IStorage<T>
{
void Add(T entity);
void Remove(T entity);
}
}
In code I tried, I get such error
'System.InvalidCastException`
at this place var repo = (Repository) CurrentRepository;
Whats wrong and how could I make it?
That's because you can't directly cast the CurrentRepository, a type of (Repository<Car> or Repository<Owner>) into a Repository<IEntity>.
See here for more information on this...
To achieve what you wanted here, you could try it this way:
Make the Entity class generic (Entity<T>) and constraint the generic type as IEntity
Make the CurrentRepository a type of Repository<Entity<T>>
Move the static initialization of CurrentRepository from instance constructor to the static constructor.
Make the Owner and Car object subclass of Entity<T> with T refers to its own type making it a self referencing generics
Code:
public class Owner : Entity<Owner>
{
...
}
public class Car : Entity<Car>
{
...
}
public abstract class Entity<T> : IEntity
where T: IEntity
{
public long Id { get; }
public static Repository<Entity<T>> CurrentRepository { get; set; }
static Entity()
{
CurrentRepository = new Repository<Entity<T>>();
}
public Entity()
{
}
public void Save()
{
CurrentRepository.Save(this);
}
public void Delete()
{
CurrentRepository.Delete(this);
}
}
One option to consider would be to change Entity as below.
The ConcurrentDictionary is to ensure that you get one repository per type.
public abstract class Entity : IEntity
{
public long Id { get; }
public static ConcurrentDictionary<Type, dynamic> CurrentRepository = new ConcurrentDictionary<Type, dynamic>();
public Entity(Entity ent)
{
GetRepository(ent);
}
private static dynamic GetRepository(Entity ent)
{
Type entityType = ent.GetType();
return CurrentRepository.GetOrAdd(entityType, type =>
{
var instance = Activator.CreateInstance(typeof(Repository<>).MakeGenericType(entityType));
return instance;
});
}
public Entity()
{
}
public void Save()
{
var repo = GetRepository(this);
repo.Save((dynamic)this);
}
public void Delete()
{
var repo = GetRepository(this);
repo.Delete((dynamic)this);
}
}
i am trying a kind of generic repository method,in controller class
var result = typeof(ISiteRepository)
.GetMethod("UpdateLanguage")
.MakeGenericMethod(objectType)
.Invoke(this.siteRepository, new object[] { obj });
it given an error me below like
GenericArguments[0], 'Site.City', , on 'Void UpdateLanguageT' violates the constraint of type 'T'.
Domain classes
public abstract class Entity : IEntity
{
[Key]
public virtual int Id { get; set; }
}
public class Language:Entity
{
public string Name { get; set; }
public int LanguageId { get; set; }
}
public abstract class Language<T> : Language where T : Language
{
public ICollection<T> Languages { get; set; }
}
public class City : Language<CityLanguage>
{
public int CountryId { get; set; }
public string Code { get; set; }
public virtual Country Country { get; set; }
}
public class CityLanguage : Language
{
public string Description { get; set; }
}
Repository class
public interface ISiteRepository
{
void UpdateLanguage<T>(T entity) where T : Language<T>;
}
public class SiteRepository : ISiteRepository
{
public SiteRepository(DbContext context)
: base(context)
{
}
public void UpdateLanguage<T>(T entity) where T : Language<T>
{
var item = GetByKey<T>(entity.Id);
var languages = entity.Languages;
}
}
Controller
public partial class SiteSettingsController : BaseController
{
ISiteRepository siteRepository;
public SiteSettingsController(ISiteRepository siteRepository)
{
this.siteRepository = siteRepository;
}
[HttpPost]
public virtual ActionResult LanguageObjectUpdate(LanguageListModel model,string type)
{
//type like City
Type objectType = Type.GetType(type);
var obj = Activator.CreateInstance(objectType);
obj.GetType().GetProperty("Id").SetValue(obj, model.Id, null);
Type languageObjectType = objectType.GetProperty("Languages").DeclaringType.GetGenericArguments()[0];
object objectLanguages = GetGenericListInstance(languageObjectType);
foreach (var language in model.Languages)
{
object languageObject = Activator.CreateInstance(languageObjectType);
languageObject.GetType().GetProperty("LanguageId").SetValue(languageObject, language.LanguageId, null);
foreach (var languageItem in language.Items)
{
languageObject.GetType().GetProperty(languageItem.Label).SetValue(languageObject, languageItem.Value, null);
}
((IList)objectLanguages).Add(languageObject);
}
obj.GetType().GetProperty("Languages").SetValue(obj, objectLanguages, null);
var result = typeof(ISiteRepository)
.GetMethod("UpdateLanguage")
.MakeGenericMethod(objectType)
.Invoke(this.siteRepository, new object[] { obj });
return PartialView("Result";
}
private static object GetGenericListInstance(Type languageObjectType)
{
Type listType = typeof(List<>);
Type[] listTypeGenericArguments = { languageObjectType };
Type objectLanguagesType = listType.MakeGenericType(listTypeGenericArguments);
object objectLanguages = Activator.CreateInstance(objectLanguagesType);
return objectLanguages;
}
Your generic method UpdateLanguage<T>(T entity) requires T to be a Language<T>. But since Language<> is a class, that seems a little hard to obtain. The error you get tells that the constraint is not met.