public class GenericRepository<TEntity> where TEntity : class
{
internal DbContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(DbContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
//snip
}
public class MyRepository<TEntity> where TEntity : GenericRepository<TEntity>
{
public MyRepository(DbContext context) : base(context){ }
//snip
}
I extended the GenericRepository class, and to use base's member variables I need to call Base's constructor in child's constructor. But I got an error that says:
'object' does not contain a constructor that takes 1 arguments
Even though the GenericRepository has constructor.
What am I doing wrong?
Because your "base class" is object, not GenericRepository<TEntity>. You added a constraint on TEntity, you did not inherit from GenericRepository<TEntity>. Maybe you meant this:
public class MyRepository<TEntity> : GenericRepository<TEntity> where TEntity : class
{
public MyRepository(DbContext context) : base(context){ }
You have to change MyRepository's base class to GenericRepository<TEntity>
Also you need to leave where TEntity : class restriction
public class MyRepository<TEntity> : GenericRepository<TEntity> where TEntity : class
{
public MyRepository(object context)
:base(context)
{
}
}
Related
I have a project broken into separate classes for an MVC project using Entity Framework 6. One class has a Generic Interface and then it is inherited
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll();
}
Inherited as below
public class Repository<T> : IRepository<T> where T : class
{
protected readonly DbContext _context = null;
private readonly DbSet<T> _entities;
public GenericRepository(DbContext context)
{
_context = context;
_entities = _context.Set<T>();
}
public IEnumerable<T> GetAll()
{
return _entities;
}
}
This works fine and i then use this in a customer class as below
public class CustomerRepository : Repository<Customer>, ICustomerRepository
{
public CustomerRepository(DataContext context) : base(context)
{
}
public List<Customer> GetPremiumCustomers()
{
return GetAll().Where(p => p.Premium).ToList();
}
}
So far so good and everything returns as expected.
I need to Include a couple of additional tables that are linked to the customers.
When i go to the Repository class and against _entities i press the . key i see Include in the menu.
I then go into the CustomerRepository and do the same with GetAll(). and along other methods along that line but Include isnt shown?
I tried adding using System.Data.Entity to the top of the Customer class but that didnt bring the option either but it is available in the top most class? What am i missing here?
I was trying to achieve something along the lines of
GetAll().Include("Address").Where(p => p.Premium).ToList()
In Entity Framework 6, the Include method is defined on the DbQuery<T> class (DbSet<T> is derived from DbQuery<T>). Your GetAll method on the other hand returns an IEnumerable<T>. The compiler does not know that you return a DbSet<T> in the form of the IEnumerable<T>, hence the method is not offered.
If you want to offer the caller of GetAll to use the Include method, you can change the return type, e.g.:
public interface IRepository<T> where T : class
{
DbQuery<T> GetAll();
}
Please note by using DbQuery<T> as your return type, the interface shows that you are using Entity Framework and you do not hide this detail from the user of the interface. In order to hide this, you can offer another method that accepts a parameter for the include and still returns an IEnumerable<T>:
public interface IRepository<T> where T : class
{
IEnumerable<T> GetAll();
IEnumerable<T> GetAllWithInclude(string include);
}
public class Repository<T> : IRepository<T> where T : class
{
protected readonly DbContext _context = null;
private readonly DbSet<T> _entities;
public GenericRepository(DbContext context)
{
_context = context;
_entities = _context.Set<T>();
}
public IEnumerable<T> GetAll()
{
return _entities;
}
public IEnumerable<T> GetAllWithInclude(string include)
{
return _entities.Include(include);
}
}
I am developing an application in Asp.net MVC core in which i using a generic interface which is as below :
public interface IRepository<TEntity, TKeyType> where TEntity : class where TKeyType : struct
{
TEntity Add(TEntity entity);
}
And below is the implementation of it.
public class EntityRepository<TEntity, TKeyType> : IRepository<TEntity, TKeyType> where TEntity : class where TKeyType : struct
{
protected readonly DrinkDbContext Context;
protected readonly DbSet<TEntity> DbSet;
public EntityRepository(DrinkDbContext context)
{
Context = context;
DbSet = context.Set<TEntity>();
}
public virtual TEntity Add(TEntity entity)
{
Context.Add(entity);
return entity;
}
}
Futher, I am inheriting EntityRepository class in ShoppingCartRepository which is as below :
public class ShoppingCartRepository<ShoppingCartItem, Int32> : EntityRepository<ShoppingCartItem, Int32>
where ShoppingCartItem : class where Int32 : struct
{
public ShoppingCartRepository(DrinkDbContext context) : base(context) { }
public override ShoppingCartItem Add(ShoppingCartItem entity)
{
base.Add(entity);
return entity;
}
}
and able to see the populated properties of a class in a method at the runtime. Please find the screenshot as below :
But when try to use those properties at compile time, i am unable to see those properties. Please find the screenshot as below.
How can i access the properties of ShoppingCartItem which is passed in Add method as a parameter ?
The issue is your ShoppingCartRepository is viewed as a generic.
ShoppingCartRepository<ShoppingCartItem,int> -> In this scenario ShoppingCartItem is of type class which you have constrained it.
It is like writing ShoppingCartRepository<T,int>. So your ShoppingCartItem is not viewed as a type ShoppingCartItem but as a generic of type T where T : class.
So class does not have access to ShoppingCartItem's properties inside the Add method.
Update to this:
public class ShoppingCartRepository : EntityRepository<ShoppingCartItem, Int32>
This make your ShoppingCartRepository non generic.
I do not think you want the ShoppingCartRepository generic, since you are specifying implementation for ShoppingCartItem.
With the above change. You should have access to the ShoppingCartItem properties inside the Add method.
I have a Xamarin Forms project and I need to have two SQLite Connection. I'm using Autofac 3.5.2
I Have a global IRepository<TEntity> :
public interface IRepository<TEntity>
where TEntity : class, new()
{
Task<int> AddAsync(TEntity entity);
Task<int> DeleteAsync(TEntity entity);
}
I decide to create an abstract class to manage the IRepository<TEntity> interface commonly :
public abstract class BaseRepository <TEntity> : IRepository<TEntity>
where TEntity : class, new()
{
private SQLiteAsyncConnection Connection { get; }
public BaseRepository(SQLiteAsyncConnection connection)
{
Connection = connection;
}
public async Task<int> AddAsync(TEntity entity)
{
return await Connection.InsertAsync(entity);
}
public async Task<int> DeleteAsync(TEntity entity)
{
return await Connection.DeleteAsync(entity);
}
}
And after that I wan't to instantiate my two SQLiteConnections :
The first :
public class SQLiteRepositoryFirst<TEntity> : BaseRepository<TEntity>
where TEntity : class, new()
{
public SQLiteRepositoryFirst(SQLiteAsyncConnection connection) : base(connection)
{
}
}
The second :
public class SQLiteRepositorySecond<TEntity> : BaseRepository<TEntity>
where TEntity : class, new()
{
public SQLiteRepositorySecond(SQLiteAsyncConnection connection) : base(connection)
{
}
}
Using Autofac I have inject it like this :
builder.RegisterGeneric(typeof(BaseRepository<>))
.As(typeof(IRepository<>))
.SingleInstance();
builder.RegisterType<DatabaseConfiguration>()
.As<IDatabaseConfiguration>()
.SingleInstance();
builder.Register(connect => new SQLiteAsyncConnection(
connect.Resolve<IDatabaseConfiguration>().GetFirstConnectionPath()))
.As<SQLiteAsyncConnection>().SingleInstance();
builder.Register(connect => new SQLiteAsyncConnection(
connect.Resolve<IDatabaseConfiguration>(). GetSecondConnectionPath()))
.As<SQLiteAsyncConnection>().SingleInstance();
The DatabaseConfiguration class is where I get the path of my two string path databases.
Now I wan't to use my repository like this:
IRepository<HouseEntity> HouseRepository { get; }
But it's not working, the problem is that I don't know how to make the difference between the two db connections when I use a repository. So the application don't know in which database I need to take the data.
EDIT :
It looks like I also have a problem for the abstract class injection.
Maybe I'm doing it wrong ?
I'm refactoring my code and I started by removing a reference to Entity Framework in my service layer. This layer uses unit of work and repositories (through interfaces) located in my DAL layer.
Now I encountered a problem because my base repository class looks like this:
public interface IDatabaseFactory<C> : IDisposable
{
C Get();
void Set(string connectionString);
}
public abstract class Repository<C, T> : IRepository<T>
where C : DbContext, IBaseContext
where T : class, IEntity
{
protected readonly IDbSet<T> dbset;
private C dataContext;
protected Repository(IDatabaseFactory<C> databaseFactory)
{
this.DatabaseFactory = databaseFactory;
this.dbset = DataContext.Set<T>();
}
protected IDatabaseFactory<C> DatabaseFactory
{
get;
private set;
}
protected C DataContext
{
get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
}
public virtual void Add(T entity)
{
dbset.Add(entity);
}
//etc...
}
I obviously need the DbContext constraint on type C. However, if I do so, I get errors on dataContext because it cannot resolve C in DbContext.
How can I overcome this problem?
EDIT
A typical repository looks like this:
public interface ICustomerTypeRepository : IRepository<CustomerType> { }
public class CustomerTypeRepository : Repository<IBaseContext, CustomerType>, ICustomerTypeRepository
{
public CustomerTypeRepository(IDatabaseFactory<IBaseContext> databaseFactory)
: base(databaseFactory) { }
}
After the changes suggested below, I still get the same errors:
The type 'IBaseContext' cannot be used as type parameter 'TContext' in the generic type or method 'Repository'. There is no implicit reference conversion from 'IBaseContext' to 'System.Data.Entity.DbContext'.
To fix compilation error, you have to put appropriate constraints to your generic types (I've replaced C to TContext for readability):
interface IBaseContext { }
interface IDatabaseFactory<TContext> : IDisposable
where TContext : DbContext
{
TContext Get();
void Set(string connectionString);
}
interface IEntity { }
interface IRepository<T>
where T : class, IEntity
{ }
abstract class Repository<TContext, T> : IRepository<T>
where TContext : DbContext, IBaseContext
where T : class, IEntity
{
protected readonly IDbSet<T> dbset;
private TContext dataContext;
protected Repository(IDatabaseFactory<TContext> databaseFactory)
{
this.DatabaseFactory = databaseFactory;
this.dbset = DataContext.Set<T>();
}
protected IDatabaseFactory<TContext> DatabaseFactory { get; private set; }
protected TContext DataContext
{
get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
}
public virtual void Add(T entity)
{
dbset.Add(entity);
}
//etc...
}
But from the point of architecture, looks like that holding a reference to DbContext in repository is superfluous. Of course, it is hard to propose better solutiuon without knowledge about all of your types.
For remove DbContext from your code you need change IDatabaseFactory like this:
public interface IDatabaseFactory : IDisposable
{
T Set<T>() where T : IEntity;
}
Then you can change Repository
public abstract class Repository<T> : IRepository<T>
where T : class, IEntity
{
protected readonly IDbSet<T> dbset;
protected Repository(IDatabaseFactory databaseFactory)
{
this.dbset = databaseFactory.Set<T>();
}
// other code
}
Implementation of IDatabaseFactory:
public class DatabaseFactory : IDatabaseFactory
{
private readonly DbContext _context;
public DatabaseFactory(DbContext context)
{
_context = context;
}
public T Set<T>() where T : Entity
{
return _context.Set<T>();
}
}
Im not sure if what I'd like to do is possible since I haven't found anything on google and after about 30minutes of intensive search I decided to ask directly.
I have definded a simple interface for my repository
public interface IRepository<TEntity> : IDisposable
{
TEntity GetById(object id);
List<TEntity> GetAll();
}
Now I want to implement my first repository and it works like this
public class ContentRepository : IRepository<ContentPages>
{
private readonly Context _db = new Context();
public ContentPages GetById(object id)
{
var result = _db.ContentPages.Find(id);
return result;
}
public List<ContentPages> GetAll()
{
return _db.ContentPages.ToList();
}
public void Dispose()
{
_db.Dispose();
}
}
This works fine but when I inject my repository to my mvc Controller it takes an IRepository<ContentPages> as parameter type and I just want it to take an IRepository.
I tried to move the generic type to the functions itself like this
public interface IRepository : IDisposable
{
TEntity GetById<TEntity>(object id);
List<TEntity> GetAll<TEntity>();
}
}
When I do this I don't know how to define my generic type TEntity in the implementation
So in conclusion I want my use the interface without speficing a type so it gets the type from the actual object like this
public constructor1(IRepository ContentRepository){}
the next controller gets this constructor
public constructor2(IRepository BlogRepository){}
and so on
I hope I could describe my problem close enough for u guys to understand :)
Within the concrete implementation of IRepository Class you can define the type of the TEntity as follows.
public TEntity GetById<TEntity>(object id) where TEntity:class
{
// Implimetation
}
But in here according to repository pattern better to use as follows.
public interface IRepository<TEntity>: IDisposable where TEntity : class
try such variant:
public interface IRepository<TEntity> where TEntity : class
{
TEntity Find(params object[] keyValues);
// ...
}
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
private readonly IDbSet<TEntity> _dbSet;
public Repository(IDbContext context)
{
_dbSet = context.Set<TEntity>();
}
public virtual TEntity Find(params object[] keyValues)
{
return _dbSet.Find(keyValues);
}
// ...
}
Example of usage:
IRepository<ApplicationUser> repository = new Repository<ApplicationUser>(new ApplicationDbContext());
ApplicationUser applicationUser = repository.Find("key");
Also, there is a better solution - you can use pattern UnitOfWork. Check this implementation on codeplex. It is really cool.
Example:
public class DatabasesController : Controller
{
private UnitOfWork _unitOfWork;
private WebContext _context;
public DatabasesController()
{
_context = new WebContext();
_unitOfWork = new UnitOfWork(_context);
}
//
// GET: /Databases/
public ViewResult Index()
{
List<Database> databases =
_unitOfWork
.Repository<Database>()
.Query()
.Include(database => database.FileEntitiesInfo)
.Get()
.ToList();
_unitOfWork.Save();
return View(databases);
}
}