When having the following scenario I am unhappy with the consuming code that is littered with the line
var queryResult = _queryDispatcher.Dispatch<CustomerByIdQuery, CustomerByIdQueryResult>(customerByIdQuery).Customer;
I would prefer to have the code work this way for the consumer:
var queryResult = _queryDispatcher.Dispatch(customerByIdQuery).Customer;
Is there a way to accomplish this using generics?
Here is the code
interface IQuery{}
interface IQueryResult{}
interface IQueryHandler<TQuery, TQueryResult> : where TQueryResult:IQueryResult where TQuery:IQuery
{
TQueryResult Execute(TQuery query);
}
interface IQueryDispatcher
{
TQueryResult Dispatch<TQuery, TQueryResult>(TQuery query) where TQuery:IQuery where TQueryResult:IQueryResult
}
class GenericQueryDispatcher : IQueryDispatcher
{
public TQueryResult Dispatch<TQuery, TQueryResult>(TQuery parms)
{
var queryHandler = queryRegistry.FindQueryHandlerFor(TQuery);
queryHandler.Execute
}
}
class CustomerByIdQuery : IQuery
{
public int Id { get; set; }
}
class CustomerByIdQueryResult : IQueryResult
{
public Customer {get; set;}
}
class CustomerByIdQueryHandler : IQueryHandler
{
public CustomerByIdQueryResult Execute(TQuery query)
{
var customer = _customerRepo.GetById(query.Id);
return new CustomerByIdQueryResult(){Customer = customer};
}
}
public class SomeClassThatControlsWorkFlow
{
IQueryDispatcher _queryDispatcher;
public SomeClassThatControlsWorkFlow(IQueryDispatcher queryDispatcher)
{
_queryDispatcher = queryDispatcher;
}
public void Run()
{
var customerByIdQuery = new CustomerByIdQuery(){Id=1};
//want to change this line
var customer = _queryDispatcher.Dispatch<CustomerByIdQuery, CustomerByIdQueryResult>(customerByIdQuery).Customer;
}
}
Here is what I would like to have :
public class ClassWithRunMethodIWouldLikeToHave
{
IQueryDispatcher _queryDispatcher;
public SomeClassThatControlsWorkFlow(IQueryDispatcher queryDispatcher)
{
_queryDispatcher = queryDispatcher;
}
public void Run()
{
var customerByIdQuery = new CustomerByIdQuery(){Id=1};
//want to change this line
var customer = _queryDispatcher.Dispatch(customerByIdQuery).Customer;
}
}
Yes, it's possible, but you have to make the Dispatch method's parameter generic (that way, the compiler can infer the type parameters from the method parameter). To do this, it looks like you'll first need a generic version of the IQuery and IQueryResult interfaces:
interface IQuery<TQuery, TQueryResult> : IQuery {}
interface IQueryResult<T> : IQueryResult
{
T Result { get; }
}
Next, make CustomerByIdQuery and CustomerByIdQueryResult implement the respective generic interfaces:
class CustomerByIdQuery : IQuery, IQuery<int, Customer>
{
public int Id { get; set; }
}
class CustomerByIdQueryResult : IQueryResult, IQueryResult<Customer>
{
public Customer Result {get; set;}
}
Now you can add an overload for Dispatch that accepts the generic parameter:
interface IQueryDispatcher
{
IQueryResult<TQueryResult> Dispatch<TQuery, TQueryResult>(IQuery<TQuery, TQueryResult> parms);
}
class GenericQueryDispatcher : IQueryDispatcher
{
public TQueryResult Dispatch<TQuery, TQueryResult>(IQuery<TQuery, TQueryResult> parms)
{
// TODO implement
}
}
The above will allow you to write:
var customerByIdQuery = new CustomerByIdQuery{Id=1};
var customer = _queryDispatcher.Dispatch(customerByIdQuery).Result;
I can't get rid of the cast, but this is working pretty close to what I want.
public interface IQueryDispatcher
{
TQueryResult Dispatch<TParameter, TQueryResult>(IQuery<TQueryResult> query)
where TParameter : IQuery<TQueryResult>
where TQueryResult : IQueryResult;
}
public interface IQueryHandler<in TQuery, out TQueryResult>
where TQuery : IQuery<TQueryResult>
where TQueryResult : IQueryResult
{
TQueryResult Retrieve(TQuery query);
}
public interface IQueryResult { }
public interface IQuery { }
public interface IQuery<TQueryResult> : IQuery { }
public class QueryDispatcher : IQueryDispatcher
{
readonly IQueryHandlerRegistry _queryRegistry;
public QueryDispatcher(IQueryHandlerRegistry queryRegistry)
{
_queryRegistry = queryRegistry;
}
public TQueryResult Dispatch<TQuery, TQueryResult>(IQuery<TQueryResult> query)
where TQuery : IQuery<TQueryResult>
where TQueryResult : IQueryResult
{
var handler = _queryRegistry.FindQueryHandlerFor<TQuery, TQueryResult>(query);
//CANT GET RID OF CAST
return handler.Retrieve((TQuery)query);
}
}
public interface IQueryHandlerRegistry
{
IQueryHandler<TQuery, TQueryResult> FindQueryHandlerFor<TQuery, TQueryResult>(IQuery<TQueryResult> query)
where TQuery : IQuery<TQueryResult>
where TQueryResult : IQueryResult;
}
public class GetCustByIdAndLocQuery : IQuery<CustByIdAndLocQueryResult>
{
public string CustName { get; set; }
public int LocationId { get; set; }
public GetCustByIdAndLocQuery(string name, int locationId)
{
CustName = name;
LocationId = locationId;
}
}
public class CustByIdAndLocQueryResult : IQueryResult
{
public Customer Customer { get; set; }
}
public class GetCustByIdAndLocQueryHandler : IQueryHandler<GetCustByIdAndLocQuery, CustByIdAndLocQueryResult>
{
readonly ICustomerGateway _customerGateway;
public GetCustByIdAndLocQueryHandler(ICustomerGateway customerGateway)
{
_customerGateway = customerGateway;
}
public CustByIdAndLocQueryResult Retrieve(GetCustByIdAndLocQuery query)
{
var customer = _customerGateway.GetAll()
.SingleOrDefault(x => x.LocationId == query.LocationId && x.CustomerName == query.CustName);
return new CustByIdAndLocQueryResult() { Customer = customer };
}
}
public interface ICustomerGateway
{
IEnumerable<Customer> GetAll();
}
public class Customer
{
public string CustomerName { get; set; }
public int LocationId { get; set; }
public bool HasInsurance { get; set; }
}
Related
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);
}
I would like to create a method that can return the generic type defined in the class, here is a detailed example;
https://dotnetfiddle.net/SApVp3
using System;
public class Program
{
public static void Main()
{
// This would be some string imported from a CSV file
var customerData = "Customer,1,Ford";
var personData = "Person,675,Henry,Ford";
var customerImporter = new ImportData<CompanyMaster>();
customerImporter.ImportDataFromFile(customerData);
var personImporter = new ImportData<PersonMaster>();
personImporter.ImportDataFromFile(personData);
}
}
public class GenericRepository<TBase>
where TBase : EntityBase
{
public void Insert(TBase entity)
{
//.. generic Insert to database
}
}
public class ImportData<TBase>
where TBase : EntityBase
{
GenericRepository<TBase> _genericRepository;
//ctor
public void ImportDataFromFile(string data)
{
// convert the string data to TBase
_genericRepository = new GenericRepository<TBase>();
}
}
public class CsvConverter<TBase> where TBase: EntityBase{
public TBase ConvertTo(string someString)
{
if (someString.StartsWith("Customer"))
{
return GetCompany(someString);
}
else return GetPerson(someString);
}
private CompanyMaster GetCompany(string companyString){
return new CompanyMaster();
}
private PersonMaster GetPerson(string companyString){
return new PersonMaster();
}
}
public abstract class EntityBase
{
public int Id { get; set; }
public DateTime CreatedDate { get; set; }
}
public class CompanyMaster : EntityBase
{
public string CompanyName { get; set; }
}
public class PersonMaster : EntityBase
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
This currently throws;
Compilation error (line 47, col 11): Cannot implicitly convert type 'CompanyMaster' to 'TBase'
Compilation error (line 50, col 15): Cannot implicitly convert type 'PersonMaster' to 'TBase'
Can this be made to work?
You need to do an upcast using:
public TBase ConvertTo(string someString)
{
if ( someString.StartsWith("Customer") )
{
return (TBase)Convert.ChangeType(GetCompany(someString), typeof(TBase));
}
else
{
return (TBase)Convert.ChangeType(GetPerson(someString), typeof(TBase));
}
}
Or as suggested by #canton7:
if ( someString.StartsWith("Customer") )
{
return (TBase)(object)GetCompany(someString);
}
else
{
return (TBase)(object)GetPerson(someString);
}
Difference between casting and using the Convert.To() method
I am trying to use the DataConnection class of linq2db in an abstract base class like so:
public abstract class BaseDbDao<T> : DataConnection where T : IDatabase
{
public string DBName => DBContext.Database;
public T DBContext { get; }
public DataConnection GetDataConnection()
{
return (this);
}
internal BaseDbDao(RelativityHelper helper, int workspaceId)
: base(GetDataProvider(), helper.GetDBContextNew(workspaceId).GetConnection())
{
DBContext = (T)helper.GetDBContextNew(workspaceId);
}
public static IDataProvider GetDataProvider()
{
LinqToDB.Common.Configuration.AvoidSpecificDataProviderAPI = true;
return new SqlServerDataProvider("", SqlServerVersion.v2017);
}
}
I am then calling it up from another class like so:
public class EdMeDBDao : BaseDbDao<MSSQLDBContext>
{
public ITable<EdMeTableRow> EdMeDatabase => GetTable<EdMeTableRow>();
public EdMeDBDao(RelativityHelper helper)
: base(helper, "123456")
{
}
public List<RelativityWorkspace> GetActiveProjects()
{
using (var db = GetDataConnection())
{
var a = GetTable<EdMeTableRow>().CaseName
};
}
}
However, I can't seem to call var a = GetTable<EdMeTableRow>().CaseName as ITable<EdMeTableRow> does not contain a definition for 'CaseName'
EdMeTableRow is defined as:
[Table(Name = "NuixWorkflowCases", Schema = "dbo")]
public class EdMeTableRow
{
public string CaseName { get; set; }
}
How can I access EdMeTableRow class from the EdMeDBDao class?
I have below object model with simple inheritance:
public class RuntimeApiManagerBase
{
}
public class CatalogRuntimeApiManagerBase : RuntimeApiManagerBase
{
public void Method1()
{
}
}
public class DocumentRuntimeApiManagerBase : RuntimeApiManagerBase
{
public void Method2()
{
}
public void Method3()
{
}
}
public class BaseObject
{
public BaseObject(RuntimeApiManagerBase runtimeApiMgr)
{
RuntimeApiMgr = runtimeApiMgr;
}
public RuntimeApiManagerBase RuntimeApiMgr { get; set; }
}
public class Catalog : BaseObject
{
public Catalog() : base(new CatalogRuntimeApiManagerBase())
{
}
}
public class Document : BaseObject
{
public Document() : base(new DocumentRuntimeApiManagerBase())
{
}
}
Now, I want to access RuntimeApiMgr Property's Methods based on exactly derived type. However, it displays nothing which is logical:
Catalog c1 = new Catalog();
// c1.RuntimeApiMgr. => No Method1
Document d1 = new Document();
// d1.RuntimeApiMgr. => No Method2 and Method3
Is that possible using different structure like Generics or something else?
Thanks for your time.
Use generics:
public class BaseObject<T>
where T : RuntimeApiManagerBase
{
public BaseObject(T runtimeApiMgr)
{
RuntimeApiMgr = runtimeApiMgr;
}
public T RuntimeApiMgr { get; set; }
}
public class Catalog : BaseObject<CatalogRuntimeApiManagerBase>
{
public Catalog() : base(new CatalogRuntimeApiManagerBase())
{
}
}
public class Document : BaseObject<DocumentRuntimeApiManagerBase>
{
public Document() : base(new DocumentRuntimeApiManagerBase())
{
}
}
In that case your c1.RuntimeApiMgr will be of the type CatalogRuntimeApiManagerBase and will have Method1
You can use RuntimeApiManagerBase as generic and have a type constraint where T must be a subclass of RuntimeApiManagerBase
public class BaseObject<T> where T : RuntimeApiManagerBase
{
public BaseObject(T runtimeApiMgr)
{
RuntimeApiMgr = runtimeApiMgr;
}
public T RuntimeApiMgr { get; set; }
}
public class Catalog : BaseObject<CatalogRuntimeApiManagerBase>
{
public Catalog() : base(new CatalogRuntimeApiManagerBase())
{
}
}
public class Document : BaseObject<DocumentRuntimeApiManagerBase>
{
public Document() : base(new DocumentRuntimeApiManagerBase())
{
}
}
Catalog c1 = new Catalog();
c1.RuntimeApiMgr.Method1();
Document d1 = new Document();
d1.RuntimeApiMgr.Method2();
Alternative to solution with generics is an old fashioned approach with casting.
Maybe something like this:
Catalog c1 = new Catalog();
(c1.RuntimeApiMgr as CatalogRuntimeApiManagerBase).Method1();
Document d1 = new Document();
(d1.RuntimeApiMgr as DocumentRuntimeApiManagerBase).Method2();
Or create new properties with the same name in Caltalog and Document classes:
public class Catalog : BaseObject
{
public Catalog() : base(new CatalogRuntimeApiManagerBase())
{
}
public new CatalogRuntimeApiManagerBase RuntimeApiMgr { get; set; }
}
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.