This question already has answers here:
How do I use reflection to call a generic method?
(8 answers)
Closed 8 years ago.
Given the following Interfaces:
interface IEntity
{
int Id{get;}
}
interface IPerson : IEntity
{
string Name{get;}
int Age{get;}
}
interface ITeacher : IPerson
{
string StaffId{get;}
}
interface IStudent : IPerson
{
string StudentId{get;}
string Courses{get;}
}
interface IRepository
{
T Get<T>(int id) where T : IEntity
}
I have the following classes in my namespace
public class EntityBase() : IEntity
{
int Id{get;set;}
}
public class Teacher : EntityBase, ITeacher{}
public class Sudent : EntityBase, IStudent{}
Currently I am implementing this IRepository as follows:
class Repository: IRepository
{
IDataContext Context{get;set;}
T Get<T>(int id) where T : EntityBase
{
if(typeof(T) == typeof(Teacher))
return Context.Get<ITeacher>(id);
if(typeof(T) == typeof(Sudent))
return Context.Get<ISudent>(id);
throw new Exception("Unknown Interface " + typeof(T).Name);
}
}
Is there a betterway of implementing this? Given that our Context has no knowledge of our data types (Teacher, Student), just its interfaces (ITeacher, IStudent).
Can something like this work?
class Repository: IRepository
{
T Get<T>(int id) where T : EntityBase
{
var MyInterface = FindInterface<T>();
return Context.Get<MyInterface>(id);
}
}
I think this will do:
class Repository: IRepository
{
IDataContext Context{get;set;}
T Get<T>(int id) where T : EntityBase
{
string[] interfaceList = new string[]
{ "ITeacher", "IStudent"};
Type interfaceType = null;
foreach (string s in interfaceList)
{
var types = typeof(T).FindInterfaces((x, y) => x.Name == y.ToString(), s);
if (types.Length > 0)
interfaceType = types[0];
}
if (interfaceType == null)
throw new Exception("Unknown Interface " + typeof(T).Name);
MethodInfo method = typeof(Context).GetMethod("Get");
MethodInfo generic = method.MakeGenericMethod(interfaceType);
var returnValue = generic.Invoke(Context, new object[] { id });
return (T)Convert.ChangeType(returnValue, typeof(T));
}
}
EDIT: As I don't know the name of your namespace, I have used the Name property to filter the interfaces. In real world usage I will suggest that you use FullName just to be sure, like this:
...
string[] interfaceList = new string[]
{ "MyNamespace.ITeacher", "MyNamespace.IStudent"};
...
var types = typeof(T).FindInterfaces((x, y) => x.FullName == y.ToString(), s);
I think you can accomplish this through reflection by finding the Get method on Context class, and invoking it as a generic call for the caller-supplied type T. I haven't tested this, but the code should look something like this:
T Get<T>(int id) where T : EntityBase
{
Type context = Context.GetType();
MethodInfo getMethod = context.GetMethod("Get", BindingFlags.Public);
MethodInfo genericGet = getMethod.MakeGenericMethod(new [] {typeof(T)});
return (T)genericGet.Invoke(Context, new object[] { id } );
}
It looks to me like you mean it the other way around. You don't want to pass interface types to Context.Get<>, do you?
// is this what you mean?
if (typeof(T) == typeof(ITeacher))
return Context.Get<Teacher>(id);
If it is, you'll need to use MakeGenericMethod, see this for an example (note the caching part).
If it is not, you might be misunderstanding some concepts of LINQ and/or Repository pattern.
However I'm curious why did you decide to use interfaces. LINQ objects are POCO anyways, why adding another layer of abstraction which involves (grrsh!) calling generic methods on DataContext via reflection?
A simple return Context.Get<T>(id) could be accomplished as following:
class Repository : IRepository
{
public IDataContext Context { get; set; }
public T Get<T>(int id) where T : IEntity, new()
{
return Context.Get<T>(id);
}
}
Following is your object/interface model with implementation for the Context
interface IEntity
{
int Id{get;}
}
interface IPerson : IEntity
{
}
interface ITeacher : IPerson
{
}
interface IStudent : IPerson
{
}
interface IDataContext
{
T Get<T>(int id) where T:new();
}
interface IRepository
{
T Get<T>(int id) where T : IEntity , new() ;
}
public class EntityBase : IEntity
{
public virtual int Id{get;set;}
}
public class Teacher : EntityBase, ITeacher {
int id=0;
public override int Id {
get { return this.id; }
set { this.id = value; }
}
}
public class Student : EntityBase, IStudent
{
int id=0;
public override int Id {
get { return this.id; }
set { this.id = value; }
}
}
class Context<T>: IDataContext where T: EntityBase, new()
{
ArrayList store;
public Context(int dataSize)
{
store = new ArrayList(dataSize);
for (int i = 0; i < dataSize; i++)
{
T t = new T();
t.Id = i;
store.Add(t);
}
}
public T Get<T>(int i) where T:new()
{
if (i<store.Count)
{
return (T)store[i];
}
else
{
return default(T);
}
}
}
Now finally the main method class to demonstrate that it all hangs together nicely.
using System;
using System.Collections;
class MyClass
{
static void Main(string[] args)
{
Context<Teacher> teachersContext = new Context<Teacher>(100);//contructs a db of 100 teachers
Context<Student> studentsContext = new Context<Student>(100);//contructs a db of 100 teachers
Repository repo = new Repository();
// set the repository context and get a teacher
repo.Context = teachersContext;
Teacher teacher1 = repo.Get<Teacher>(83); //get teacher number 83
Console.WriteLine("Teacher Id:{0} ", teacher1.Id);
// redirect the repositry context and now get a student
repo.Context = studentsContext;
Student student1 = repo.Get<Student>(35); //get student number 35
Console.WriteLine("Student Id: {0} ", student1.Id);
Console.ReadLine();
}
Related
How do I get all types that implementing a specific open generic type?
For instance:
public interface IUserRepository : IRepository<User>
Find all types that implement IRepository<>.
public static IEnumerable<Type> GetAllTypesImplementingOpenGenericType(Type openGenericType, Assembly assembly)
{
...
}
This will return all types that inherit a generic base class. Not all types that inherit a generic interface.
var AllTypesOfIRepository = from x in Assembly.GetAssembly(typeof(AnyTypeInTargetAssembly)).GetTypes()
let y = x.BaseType
where !x.IsAbstract && !x.IsInterface &&
y != null && y.IsGenericType &&
y.GetGenericTypeDefinition() == typeof(IRepository<>)
select x;
This will return all types, including interfaces, abstracts, and concrete types that have the open generic type in its inheritance chain.
public static IEnumerable<Type> GetAllTypesImplementingOpenGenericType(Type openGenericType, Assembly assembly)
{
return from x in assembly.GetTypes()
from z in x.GetInterfaces()
let y = x.BaseType
where
(y != null && y.IsGenericType &&
openGenericType.IsAssignableFrom(y.GetGenericTypeDefinition())) ||
(z.IsGenericType &&
openGenericType.IsAssignableFrom(z.GetGenericTypeDefinition()))
select x;
}
This second method will find ConcreteUserRepo and IUserRepository in this example:
public class ConcreteUserRepo : IUserRepository
{}
public interface IUserRepository : IRepository<User>
{}
public interface IRepository<User>
{}
public class User
{}
Solution implemented without LINQ, searching both generic and non generic interfaces, filtering the return type to classes.
public static class SampleCode
{
public static void Main()
{
IList<Type> loadableTypes;
// instance the dummy class used to find the current assembly
DummyClass dc = new DummyClass();
loadableTypes = GetClassesImplementingAnInterface(dc.GetType().Assembly, typeof(IMsgXX)).Item2;
foreach (var item in loadableTypes) {Console.WriteLine("1: " + item);}
// print
// 1: Start2.MessageHandlerXY
loadableTypes = GetClassesImplementingAnInterface(dc.GetType().Assembly, typeof(IHandleMessageG<>)).Item2;
foreach (var item in loadableTypes) { Console.WriteLine("2: " + item); }
// print
// 2: Start2.MessageHandlerXY
// 2: Start2.MessageHandlerZZ
}
///<summary>Read all classes in an assembly that implement an interface (generic, or not generic)</summary>
//
// some references
// return all types implementing an interface
// http://stackoverflow.com/questions/26733/getting-all-types-that-implement-an-interface/12602220#12602220
// http://haacked.com/archive/2012/07/23/get-all-types-in-an-assembly.aspx/
// http://stackoverflow.com/questions/7889228/how-to-prevent-reflectiontypeloadexception-when-calling-assembly-gettypes
// return all types implementing a generic interface
// http://stackoverflow.com/questions/33694960/find-all-types-implementing-a-certain-generic-interface-with-specific-t-type
// http://stackoverflow.com/questions/8645430/get-all-types-implementing-specific-open-generic-type
// http://stackoverflow.com/questions/1121834/finding-out-if-a-type-implements-a-generic-interface
// http://stackoverflow.com/questions/5849210/net-getting-all-implementations-of-a-generic-interface
public static Tuple<bool, IList<Type>> GetClassesImplementingAnInterface(Assembly assemblyToScan, Type implementedInterface)
{
if (assemblyToScan == null)
return Tuple.Create(false, (IList<Type>)null);
if (implementedInterface == null || !implementedInterface.IsInterface)
return Tuple.Create(false, (IList<Type>)null);
IEnumerable<Type> typesInTheAssembly;
try
{
typesInTheAssembly = assemblyToScan.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
typesInTheAssembly = e.Types.Where(t => t != null);
}
IList<Type> classesImplementingInterface = new List<Type>();
// if the interface is a generic interface
if (implementedInterface.IsGenericType)
{
foreach (var typeInTheAssembly in typesInTheAssembly)
{
if (typeInTheAssembly.IsClass)
{
var typeInterfaces = typeInTheAssembly.GetInterfaces();
foreach (var typeInterface in typeInterfaces)
{
if (typeInterface.IsGenericType)
{
var typeGenericInterface = typeInterface.GetGenericTypeDefinition();
var implementedGenericInterface = implementedInterface.GetGenericTypeDefinition();
if (typeGenericInterface == implementedGenericInterface)
{
classesImplementingInterface.Add(typeInTheAssembly);
}
}
}
}
}
}
else
{
foreach (var typeInTheAssembly in typesInTheAssembly)
{
if (typeInTheAssembly.IsClass)
{
// if the interface is a non-generic interface
if (implementedInterface.IsAssignableFrom(typeInTheAssembly))
{
classesImplementingInterface.Add(typeInTheAssembly);
}
}
}
}
return Tuple.Create(true, classesImplementingInterface);
}
}
public class DummyClass
{
}
public interface IHandleMessageG<T>
{
}
public interface IHandleMessage
{
}
public interface IMsgXX
{
}
public interface IMsgXY
{
}
public interface IMsgZZ
{
}
public class MessageHandlerXY : IHandleMessageG<IMsgXY>, IHandleMessage, IMsgXX
{
public string Handle(string a)
{
return "aaa";
}
}
public class MessageHandlerZZ : IHandleMessageG<IMsgZZ>, IHandleMessage
{
public string Handle(string a)
{
return "bbb";
}
}
You could try
openGenericType.IsAssignableFrom(myType.GetGenericTypeDefinition())
or
myType.GetInterfaces().Any(i => i.GetGenericTypeDefinition() = openGenericType)
You can use the following code to get all types that implement IRepository<> interface:
List<Type> typesImplementingIRepository = new List<Type>();
IEnumerable<Type> allTypesInThisAssembly = Assembly.GetExecutingAssembly().GetTypes();
foreach (Type type in allTypesInThisAssembly)
{
if (type.GetInterface(typeof(IRepository<>).Name.ToString()) != null)
{
typesImplementingIRepository.Add(type);
}
}
Below is my generic class , I need to access GetAll method for that I need to create instance. How can I create instance if this class to access GetAll method.
Below is my code
public class GenericRepository<TContext> : IGenericRepository
where TContext : DbContext, new()
{
private TContext _entities;
public TContext Context
{
get { return _entities; }
set { _entities = value; }
}
public virtual List<TEntity> GetAll<TEntity>() where TEntity : class
{
try
{
_entities = new TContext();
return _entities.Set<TEntity>().ToList();
}
catch (Exception)
{
return null;
}
}
}
public interface IGenericRepository
{
List<TEntity> GetAll<TEntity>() where TEntity : class;
}
Because the class is a Generic class it needs to have a type passed to it when creating it like this.
IGenericRepository referenceToRepostoryObject = new GenericRepository<MyDbContextClassName>();
Is this what you need?
var d1 = typeof(GenericRepository<>);
Type[] typeArgs = { typeof(Item) };
var makeme = d1.MakeGenericType(typeArgs);
var repository = (IGenericRepository)Activator.CreateInstance(makeme);
repository.GetAll<Item>();
I'm overriding the ValidateEntity method to check for unique validation and I've hit a stumbling block.
protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
var result = new DbEntityValidationResult(entityEntry, new List<DbValidationError>());
if (entityEntry.Entity is ReferenceType && entityEntry.State == EntityState.Added)
{
var entity = entityEntry.Entity as ReferenceType;
var pluralService = PluralizationService.CreateService(CultureInfo.GetCultureInfo("en-gb"));
var pluralEntity = pluralService.Pluralize(entity.GetType().Name);
// I would like Courses to be replaced with the property name of pluralEntity
if (Courses.Where(x => x.Title == entity.Title).Count() > 0)
{
result.ValidationErrors.Add(new DbValidationError(nameof(entity.Title), nameof(entity.Title) + " must be unique."));
}
}
if (result.ValidationErrors.Count > 0)
{
return result;
}
else
{
return base.ValidateEntity(entityEntry, items);
}
}
In my SchoolContext class I have the property DbSet<Course> Courses which is a ReferenceType (a custom abstract class type).
The value of pluralEntity is Courses, but I want to put in the if-statement something similar to:
if (Property(pluralEntity).Where(x => x.Title == entity.Title).Count() > 0)
{
// validate
}
Is there a way to do this?
Update
I've got this:
var prop = (DbSet<ReferenceType>) GetType().GetProperty(pluralEntity).GetValue(this, null);
if (prop.Where(x => x.Title == entity.Title).Count() > 0)
{
result.ValidationErrors.Add(new DbValidationError(nameof(entity.Title), nameof(entity.Title) + " must be unique."));
}
But because ReferenceType is an abstract class it cannot cast it at runtime.
I'd like to do something like this
var prop = (DbSet<typeof(entityEntry.Entity.GetType().Name)>)
But of course that's a variable and can't be passed in as a generic type
The only thing I can think of at the moment is writing a custom validation method, using the repository pattern.
First, create an interface which all your entities will implement
public interface IEntity
{
public string Title {get; set; }
}
Then create the repository:
public class Repository<TEntity> where TEntity: class, IEntity
{
private YourContext context = new YourContext();
private DbSet<TEntity> AppDbSet;
public Repository()
{
AppDbSet = context.Set<TEntity>();
}
//a couple of method to retrieve data...
public List<TEntity> GetAll()
{
return AppDbSet.ToList();
}
public IEnumerable<TEntity> Find(Func<TEntity, bool> predicate)
{
return AppDbSet.Where<TEntity>(predicate);
}
public TEntity Single(Func<TEntity, bool> predicate)
{
return AppDbSet.FirstOrDefault(predicate);
}
//Lastly, implement a validation method
public bool IsValid(TEntity entity)
{
if (AppDbSet.SingleOrDefault(x => x.Title == entity.Title) != null)
return false;
else
return true;
}
}
Use the repository as follow:
Repository<Course> courseRepository = new Repository<Course>();
Course course = new Course();
course.Title = "Boring course";
Console.WriteLine(courseRepository.IsValid(course));
Hope it helps.
I wrote this function to save data to EF4 using POCOs classes:
public void Guardar(Pedidos myPedido)
{
using (var context = new OhmioEntities())
{
if (myPedido.ID_Pedido == 0)
{
context.Pedidos.AddObject(myPedido);
}
else
{
context.Pedidos.Attach(myPedido);
context.ObjectStateManager.ChangeObjectState(myPedido, System.Data.EntityState.Modified);
}
context.SaveChanges();
}
}
Now i want to write this in a generic way on a base class. Is there a way to decide if i need to do UPDATE or INSERT without using the ID? (ID_Pedido in this case), because the name on key field change on every object type. The rest of the code is generic. I'm traing to know if i need to use AddObject (new) or Attach(exist).
Thanks you!
look to the method InsertOrUpdate! You can make this repository more generic; For example you can create an Entity base class and use it in a generic Approach.
public class Employee
{
public int Id { get; set; }
public string FullName { get; set; }
}
Now using this we will have a simple context class
public class HRContext : DbContext
{
public DbSet<DomainClasses.Employee> Employees { get; set; }
}
After that, define the repository interface IEmployeeRepository
public interface IEmployeeRepository : IDisposable
{
IQueryable<Employee> All { get; }
IQueryable<Employee> AllIncluding(params Expression<Func<Employee, object>>[] includeProperties);
Employee Find(int id);
void InsertOrUpdate(Employee employee);
void Delete(int id);
void Save();
}
Then the Repository class called EmployeeRepository
public class EmployeeRepository : IEmployeeRepository
{
HRContext context = new HRContext();
public IQueryable<Employee> All
{
get { return context.Employees; }
}
public IQueryable<Employee> AllIncluding(params Expression<Func<Employee, object>>[] includeProperties)
{
IQueryable<Employee> query = context.Employees;
foreach (var includeProperty in includeProperties) {
query = query.Include(includeProperty);
}
return query;
}
public Employee Find(int id)
{
return context.Employees.Find(id);
}
public void InsertOrUpdate(Employee employee)
{
if (employee.Id == default(int)) {
// New entity
context.Employees.Add(employee);
} else {
// Existing entity
context.Entry(employee).State = EntityState.Modified;
}
}
public void Delete(int id)
{
var employee = context.Employees.Find(id);
context.Employees.Remove(employee);
}
public void Save()
{
context.SaveChanges();
}
public void Dispose()
{
context.Dispose();
}
}
I get the soruce code from :
http://blogs.msdn.com/b/wriju/archive/2013/08/23/using-repository-pattern-in-entity-framework.aspx
for example for a generic repository:
public interface IGenericRepository<T> where T : class {
IQueryable<T> GetAll();
IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
void Add(T entity);
void Delete(T entity);
void Edit(T entity);
void Save();
}
Where T is the base entity for all your entities.
here is the complete generic example:
http://www.tugberkugurlu.com/archive/generic-repository-pattern-entity-framework-asp-net-mvc-and-unit-testing-triangle
I've Found it myself! In case anyone face the same problem, here is the solution. I wrote this method:
public string getKey<T>() where T :new()
{
T _obj = new T();
return context.CreateEntityKey(_obj.ToString().Split('.')[2], _obj).EntityKeyValues[0].Key;
}
Wich return the first Primary key of the object (in my case that's enough)
And use it like this:
string sKey = getKey<GruposComerciales>();
Now i can write a generic saveorupdate method on my repository. Thank you!!!
You can query all parts of the primary key via Metadataworkspace
IDictionary<string, ICollection<EdmMember>> dict = // create instance ...
MetadataWorkspace.GetItems<EntityContainer>(DataSpace.CSpace)
.First()
.BaseEntitySets
.ToList()
.ForEach(s => dict.Add(s.ElementType.Name, s.ElementType.KeyMembers));
With this I put the defined propertys of the primary key into a dictionary for later use.
Is there any way to move the Parse method into the abstract class ? I tried multiple ways (links at the bottom), but I am still hitting one or another roadblock.
public class AnimalEntityId : EntityId<AnimalEntityId>
{
public AnimalEntityId()
: base()
{
}
private AnimalEntityId(string value)
: base(value)
{
}
public static AnimalEntityId Parse(string value)
{
return new AnimalEntityId(value);
}
}
public abstract class EntityId<TEntityId>
{
private readonly System.Guid value;
protected EntityId(string value)
{
this.value = System.Guid.Parse(value);
}
protected EntityId()
{
this.value = System.Guid.NewGuid();
}
}
Tried these suggestions with no luck:
Passing arguments to C# generic new() of templated type
Is there a generic constructor with parameter constraint in C#?
https://social.msdn.microsoft.com/Forums/en-US/fd43d184-0503-4d4a-850c-999ca58e1444/creating-generic-t-with-new-constraint-that-has-parameters?forum=csharplanguage
http://www.gamedev.net/topic/577668-c-new-constraint--is-it-possible-to-add-parameters/
Thanks in advance!
If you don't mind using reflection, you can move Parse into the abstract type like this:
public static TEntityId Parse(string val) {
var constr = typeof(TEntityId).GetConstructor(
// Since the constructor is private, you need binding flags
BindingFlags.Instance | BindingFlags.NonPublic
, null
, new[]{ typeof(string) }
, null);
if (constr == null) {
throw new InvalidOperationException("No constructor");
}
return (TEntityId)constr.Invoke(new object[] {val});
}
Demo.
No, you cannot write a template constraint such as new(string) instead of simply new(). You'll have to leverage reflection to get it to work:
public abstract class EntityId<TEntityId>
where TEntityId : EntityId<TEntityId>
{
private readonly System.Guid value;
protected EntityId(string value)
{
this.value = System.Guid.Parse(value);
}
protected EntityId()
{
this.value = System.Guid.NewGuid();
}
public static TEntityId Parse(string value)
{
return (TEntityId)Activator.CreateInstance(typeof(TEntityId), new object[] { value });
}
}
Assuming you make the constructor accessible (instead of it currently being private). Note the constraint where TEntityId : EntityId<TEntityId> - which will ensure we'll only return subclasses of EntityId
How about making value a private mutable field/property and actually setting it from the Parse method?
(Curiously recurring generic parameter removed from EntityId for simplicity)
public class SimpleAnimalEntityId : EntityId
{
// Implicit parameterless constructor.
}
public class ParametrizedAnimalEntityId : EntityId
{
// Parametrized constructor only.
public ParametrizedAnimalEntityId(int ignored)
{
}
}
public abstract class EntityId
{
// Simple scenario: derived type has a parameterless constructor.
public static TEntity Parse<TEntity>(string value)
where TEntity : EntityId, new()
{
Guid id = Guid.Parse(value);
return new TEntity { value = id };
}
// Advanced scenario: derived type constructor needs parameters injected.
public static TEntity Parse<TEntity>(string value, Func<TEntity> constructor)
where TEntity : EntityId
{
Guid id = Guid.Parse(value);
TEntity entity = constructor();
entity.value = id;
return entity;
}
private Guid value;
protected EntityId()
{
value = Guid.NewGuid();
}
}
Now you can handle any constructor from your Parse method:
string id = Guid.NewGuid().ToString();
SimpleAnimalEntityId simple = EntityId.Parse<SimpleAnimalEntityId>(id);
ParametrizedAnimalEntityId parametrized = EntityId.Parse(id, () => new ParametrizedAnimalEntityId(42));