4

I have a class defined as follows;

public abstract class Repository<TEntity, TDataContext> : DisposableBaseClass
   where TEntity : class
   where TDataContext : DataContext, new()
{...contains Linq to SQL related functionality

In the concrete sub-class I define the types as so;

public class ConcreteRepo : Repository<LSTableClass, LSDataContext>

At the next tier up, I have business objects which hold the Repository object as a private variable.

This was fine;

private ConcreteRepo _repository;

However, I then refactored this out into a parent class for all the business objects - this parent class holds the repository/ implements Dispose pattern to dispose of repository etc.

My problem is that I just can't get the syntax right for the declaration of the variable.

The closest I have come is;

protected Repository<Object, DataContext> _repository;

but this gives the compile error:

"Error 1 'System.Data.Linq.DataContext' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TDataContext' in the generic type or method '....Repository'..."

I've tried various other things but hit other problems.

In the business layer object inheriting this abstract class I am creating and using the _repository variable with a cast;

(Repository<LSTableClass, LSDataContext>)_repository = new ConcreteRepo();
  • and I think this will be fine, assuming I can get this declaration right in the parent.

If I can't get this to work I have to declare the _repository in each business object, with the full/ concrete type details, and implement the dispose pattern in each one to clear up. Not the end of the world, but I'd like to not have to.

4

2 回答 2

3

If I understand your problem correctly, you need to add a third generic parameter, the repository type, which is constrained to be a descendant of Repository with the appropriate argument types.

To outline it some more:

public abstract class Repository<TEntity,TDataContext>
    where TEntity : class
    where TDataContext : DataContext, new() {}

public abstract class BusinessObject<TEntity,TDataContext,TRepository>
    where TEntity : class
    where TDataContext : DataContext, new()
    where TRepository : Repository<TEntity,TDataContext>
{
    TRepository _repository;
}

public class ConcreteObject : BusinessObject<LSTableClass,LSDataContext,ConcreteRepo>
{ // ...

I know it's probably not as compact as you would like, but what's needed to reduce this down effectively yet still retain the strong typing, is higher order generic types (called type classes in Haskell): a way of indicating that type parameters are themselves generic, and can take type parameters.

于 2009-05-04T22:40:47.663 回答
3

Your declaration

protected Repository<Object, DataContext> _repository;

Didn't work because of the constraint you placed upon it:

...    
where TDataContext : DataContext, new()

Specifically the new() part.

I'm guessing that you want to 'inject' an object satisfying your generic interface.

Two things:

  • You can't convert between Repository<Object, DataContext> and Repository<LSTableClass, LSDataContext>. Not with C# 3. This is called Contravariance/Covariance and won't be available until C# 4. In C# 3 these are two completely different types.

  • If you want to store a generic member inside of a class, then the type arguments either have to be concrete types, or they must be declared as type parameters in the parent class declaration (making that generic).

Options:

  • Use generic type arguments on your parent class.
  • Remove the new() constraint from your class declaration.
于 2009-05-04T22:54:56.133 回答