Generic class repository with parameter for sorting - c#

I have a generic repository like this:
public class EntityMethods1<TModel> where TModel : class
{
public static List<TModel> GetAll()
{
using (DataContext context = new DataContext())
{
var propertyInfo = typeof(TModel).GetProperty("");
return context.Set<TModel>().OrderBy(x => propertyInfo.GetValue(x, null)).ToList();
}
}
}
And then I can inherit like this to access the methods.
public class DBTABLENAME : Generic<DBTABLENAME>
{
}
I've been searching the entire day how to pass a parameter to the generic repository from the Entity that inherits from him, tried with constructors on both and still no luck, it seems like the constructor won't execute and I get a null.
I want to inherit like this:
public class DBTABLENAME : Generic<DBTABLENAME>("defaultSortField")
{
}
I'm not good at generics and still trying to learn. Any help will be very appreciated.

Related

Generic class with non-generic method constraint?

I have this class working as my repository:
public class Repository<T> where T : class, new()
{
public T GetByID(int id)
{
//Code...
}
}
But there a few cases where I don't want to leave a class' default public constructor (such as some specific model properties that require some logic), like this:
public class Person
{
public CPersonID PersonID { get; private set; }
//This shouldn't exist outside Person, and only Person knows the rules how to handle this
public class CPersonID
{
internal CPersonID() { }
}
}
This makes the Repository template class invalid because of the new() constraint.
I'd like to make something like this:
public class Repository<T> where T : class
{
//This function should be created only when the T has new()
public GetByID(int id) where T : new()
{
}
//And this could be the alternative if it doesn't have new()
public GetByID(T element, int id)
{
}
}
Is there any way I can accomplish this?
Edit: Example of a Get method:
public IList<T> GetAll()
{
IList<T> list = new List<T>();
using(IConnection cn = ConnectionFactory.GetConnection())
{
ICommand cm = cn.GetCommand();
cm.CommandText = "Query";
using (IDataReader dr = cm.ExecuteReader())
{
while(dr.Read())
{
T obj = new T(); //because of this line the class won't compile if I don't have the new() constraint
//a mapping function I made to fill it's properties
LoadObj(obj, dr);
list.Add(obj);
}
}
}
return list;
}
As Lasse V. Karlsen already answered, this is not directly possible. However, you can get very close, close enough for practical purposes.
Given public class Repository<T> where T : class, you cannot define instance methods that only exist when T has a parameterless constructor. You don't need that. You just need repository.GetByID(3) to work. That can work if GetByID is an instance method, but also if it is an extension method, and extension methods can add requirements to T.
public static class RepositoryExtensions
{
public T GetByID(this Repository<T> repo, int id) where T : class, new()
{
...
}
}
Note that extension methods don't work if an instance method of the same name already exists, so if you go with this, you need both overloads of GetByID to be extension methods, not just this one.
The actual logic belongs in the Repository class, but you can forward to that:
public class Repository<T> where T : class
{
internal T GetByIDImpl(int id, Func<T> factory)
{
...
}
}
public static class RepositoryExtensions
{
public T GetByID(this Repository<T> repo, int id) where T : class, new()
{
return repo.GetByIDImpl(id, () => new T());
}
public T GetByID(this Repository<T> repo, T element, int id) where T : class
{
return repo.GetByIDImpl(id, () => element);
}
}
No, you can't do it this way.
All constraints have to be specified the place where you introduce the generic parameter, in this case at the class level.
As such you have two options:
Add , new() as a constraint, limiting the use of the repository class to use types that have a public parameterless constructor
Not add it as a constraint, and use reflection to try to construct the object at runtime
Note that point 2 there may fail (at runtime) if the type does not have a valid constructor.
There is no way you can ask the compiler to create a class where the ability to call a specific method is conditional, ie. "Only let me call GetByID if the type has a constructor".
If you want it as a compile-time constraint, you can do
public class Class<T> where T : class
{
public void Method<U> where U : T, new()
{
// ...
}
}
but this has the disadvantage that you'd have to do
new Class<HasConstructor>().Method<HasConstructor>();
as the type won't be implicitly picked up. The advantage is that the following won't compile:
new Class<NoConstructor>().Method<NoConstructor>();

Bind Two Generic Type

I have two parallel class hierarchy, where the first hierarchy is for an API while the second is used in the model layer.
The same type has one representation(class) in each hierarchy and I want to 'bind' (more later) this two classes in order to use generics.
API
/ \
ApiA ApiB
Model
/ \
ModelA ModelB
For instance, once this function
public string DoSomething<APIType> (APIType value) {
gets an APIType as argument (e.g. ApiB), I want to call the associated generic method that takes a ModelType as type argument (ModelB in this case).
I tried something similar to this:
public string DoSomething (ApiType value) where ModelType: Model where ApiType : API
But I then discover that C# can't do partial inference, so this:
class ApiB : Api<ModelB> {}
ApiB obj;
DoSomething(obj)
can't work (both type arguments are required)
I tried to implement something similar to C++ traits but it did not work.
It would be possible to use only Type, but I am doing this in order to get the additional compiler checking.
I guess this is not a big problem, however I would like to know if someone knows a solution.
It's very complex question. Check this code, I've replaced associated generic method call with generic constructor of List. Coment if there is difference between what you questioned and what I understood from question.
class Program
{
public class Model { }
public class ModelB : Model { }
public class Api<T> where T : Model
{
public List<T> CallGenericMethod()
{
return new List<T>();
}
}
public class ApiB: Api<ModelB> { }
public static string DoSomething<T>(Api<T> a) where T : Model
{
var b = a.CallGenericMethod();
return b.GetType().ToString();
}
static void Main(string[] args)
{
ApiB a = new ApiB();
Console.WriteLine(DoSomething(a));
}
}
Edit two types generic version
public class Api<TApi, TModel> where TApi: Api<TApi, TModel> where TModel : Model
{
public List<TModel> CallGenericMethod()
{
return new List<TModel>();
}
}
public class ApiB: Api<ApiB, ModelB> { }
public static string DoSomething<TApi, TModel>(Api<TApi, TModel> a) where TApi : Api<TApi, TModel> where TModel: Model
{
return new Dictionary<TApi, TModel>().GetType().ToString();
}

Create Generic Class C#

I'm working on a repository for a list of entities, and I should repeat thea same class more than once, the only difference is type type .. is there a way to make it generic?
It should quite easy, for sure I don't know how to make this generic:
private Namespace.DAL.UserProfileRepository _rep = new Namespace.DAL.UserProfileRepository();
The class I'm repeating it this:
public class UserProfileRepository : IEntityRepository<IUserProfile>
{
private Namespace.DAL.UserProfileRepository _rep = new Namespace.DAL.UserProfileRepository();
public IUserProfile[] GetAll()
{
return _rep.GetAll();
}
public IUserProfile GetById(int id)
{
return _rep.GetById(id);
}
public IQueryable<IUserProfile> Query(Expression<Func<IUserProfile, bool>> filter)
{
return _rep.Query(filter);
}
}
#NickBray hit the nail on the head. Regardless of how different or similar the actual concrete repository implementations are the DAL class in your example should expose the repository instance via an interface.
Ideally the exposed interface would be declared something like this.
interface IUserProfileRepository : IEntityRepository<IUserProfile>
{
}
This way you could add custom IUserProfile methods as necessary. While the IEntityRepository interface would define the common methods Add, Update, Remove and various QueryXXX methods.
I hope this example helpful for you. If I correctly understood your question, you want to make generizable your repository based on the interface "IEntityRepository".
Try something like this:
public class UserProfileRepository<TUserProfile> : IEntityRepository<TUserProfile> where TUserProfile : IUserProfile
{
private Namespace.DAL.UserProfileRepository _rep = new Namespace.DAL.UserProfileRepository();
public TUserProfile[] GetAll()
{
return _rep.GetAll();
}
public TUserProfile GetById(int id)
{
return _rep.GetById(id);
}
public IQueryable<TUserProfile> Query(Expression<Func<TUserProfile, bool>> filter)
{
return _rep.Query(filter);
}
}

Workaround for C# generic attribute limitation

As discussed here, C# doesn't support generic attribute declaration.
So, I'm not allowed to do something like:
[Audit<User> (UserAction.Update)]
public ActionResult SomeMethod(int id){ ...
that would fit like a charm in my attribute impl class, cause I need to call a method from a generic repository:
User fuuObj = (User) repository.LoadById<T>(_id);
I tried to use this solution without success. I can pass something like typeOf(User), but how can I call LoadById just with type or magic string?
*Both, T and User, extend a base class called Entity.
You could use reflection to load by id:
public class AuditAttribute : Attribute
{
public AuditAttribute(Type t)
{
this.Type = t;
}
public Type Type { get; set; }
public void DoSomething()
{
//type is not Entity
if (!typeof(Entity).IsAssignableFrom(Type))
throw new Exception();
int _id;
IRepository myRepository = new Repository();
MethodInfo loadByIdMethod = myRepository.GetType().GetMethod("LoadById");
MethodInfo methodWithTypeArgument = loadByIdMethod.MakeGenericMethod(this.Type);
Entity myEntity = (Entity)methodWithTypeArgument.Invoke(myRepository, new object[] { _id });
}
}
You have at least these three possibilities:
You could use reflection to call LoadById
You could create an expression tree that calls LoadById
You could provide a LoadById method in your repository that is not generic.
You could use reflection to invoke the LoadById method. The following msdn article should point you in the right direction:
http://msdn.microsoft.com/en-us/library/b8ytshk6(v=vs.100).aspx
Since C# 11 there is no need for workarounds because generic attributes are supported:
public class AuditAttribute<T> : Attribute where T : Entity
{
// ...
}

How to cast object to generic type?

I have many classes that implements IBuilder<> interface such the ones below
UPDATED:
each Model1, Model2... inherits from IModel
public class A : IBuilder<Model1>
{
public Model1 Create(string param)
{
return new Model1();
}
}
public class B : IBuilder<Model2>
{
public Model2 Create(string param)
{
return new Model2();
}
}
I'm using StructureMap to register all classes that inherit IBuilder<>
Scan(x =>
{
x.TheCallingAssembly();
x.AddAllTypesOf(typeof(IViewModelBuilder<>));
});
UPDATED
Now, every time I need to get model of some Module I call Do function
public IModel Do(Module module)
{
//ModelSettings is taken from web.config
var builderType = Type.GetType(string.Format("{0}.{1}ModelBuilder,{2}", ModelSettings.Namespace, module.CodeName, ModelSettings.Assembly));
var builder = ObjectFactory.GetInstance(t) as IViewModelBuilder<>;
return builder.Create("");
}
I get compilation error in the line ObjectFactory.GetInstance(t) as IViewModelBuilder<>.
Many posts suggest to create NOT generic interface(IViewModelBuilder) and let the generic one to inherit it. And then I could make the casting like
ObjectFactory.GetInstance(t) as IViewModelBuilder
Is this the only way?
Thank you
Your code for Do and GetInstance should be generic too. Basicly it could look something like this
public T Do<T> ()
{
return ObjectFactory.GetInstance<T>().Create();
}
Couldn't you make Do() generic?
var m = Do<B>();
public T Do<T>()
{
var builder = (IViewModelBuilder<T>)ObjectFactory.GetInstance(typeof(T));
return builder.Create("");
}
If you can't, using non-generic interface is probably your best bet, but there are other options like using reflection or C# 4's dynamic:
var m = Do(typeof(B));
public object Do(Type t)
{
dynamic builder = ObjectFactory.GetInstance(t);
return builder.Create("");
}
The only thing I can think of is that you make an interface or a base class that your viewmodel inherit from. I.e:
public class Model1 : ModelBase
{
}
public class ModelBase
{
}
public ModelBase Do(Type t)
{
var builder = ObjectFactory.GetInstance(t);
return t.GetMethod("Create").Invoke(builder, new object[] { "" }) as ModelBase;
}
You need to introduce a non-generic IViewModelBuilder interface if you want to call it in a non-generic scenario. The generic IViewModelBuilder<T> would implement IViewModelBuilder.
The only other alternative is to invoke the create method via reflection, but I don't see why that would be preferred:
var builder = ObjectFactory.GetInstance(builderType);
var method = builderType.GetMethod("Create");
return (IModel) method.Invoke(builder, new[]{""});

Categories

Resources