I want to write a common method which returns any model like product, sales,etc. Something like this (.net 3.5; I'm not using entity framework)
public class ProductRepository<TEntity> : IProduct<TEntity>
where TEntity : class
{
public IEnumerable<TEntity> GetProductList(string Type)
{
IEnumerable<Product> fLit = from p in ProductList
select p;
return fLit;
}
}
But I'm getting the following error
Cannot implicitly convert type System.Collections.Generic.IEnumerable<Product>' to
System.Collections.Generic.IEnumerable<TEntity>. An explicit conversion exists (are you missing a cast?)
Any help appreciated. Thanks in advance.
I'm afraid you have to change design of your Domain, well this is not how Repository Pattern going to implement. First of all You have to have a base class for your Domain Models something simple like below (Of course this is not necessary):
public class EntityBase {
public virtual int Id { get; set; }
}
then you must have a generic IRepository interface :
public interface IRepository<TEntity> where TEntity : EntityBase {
TEntity FindOne(int id);
}
after you implement generic IRepository interface you need to have a concrete Repository class which is inherited from you generic interface, like this :
public class Repository<TEntity> : IRepository<TEntity> where TEntity : EntityBase {
private readonly DbContext _dbContext;
private readonly DbSet<TEntity> _dbSet;
public Repository(DbContext dbContext) {
_dbContext = dbContext;
_dbSet = _dbContext.Set<TEntity>();
}
public IQueryable<TEntity> Entities {
get { return _dbSet; }
}
public TEntity FindOne(int id) {
return Entities.FirstOrDefault(t => t.Id == id);
}
}
this is neat, so as you can see here we expect DbContext parameter for Repository class constructor. Also we take the advantage of our entity base's Id property to find what exactly we want.
Well till now you implement a basics of Repository pattern, from now on, you need to create a Repository class for each Domain Entity. let's implement what you've asked here :
public class ProductRepository : Repository<Product> {
public ProductRepository(DbContext dbContext)
: base(dbContext) {
}
public IEnumerable<Product> GetProductList(string Type) {
IEnumerable<Product> fLit = from p in Entities select p;
return fLit;
}
}
Hope this help.
The error is pretty clear: a TEntity is not a Product. In .Net 4.0 you could use covariance to fix this, but in .Net 3.5 you may do the following:
Change your type constraint from where TEntity : class to where TEntity : Product. You are already assuming this in the method, so do this so the compiler can enforce it.
Use LINQ to explicitly cast the results to TEntities: return fLit.Cast<TEntity>();
In .NET 3.5 you cannot cast a generic type like that. So just keep your enumerable as a generic type. Change the line to:
IEnumberable<TEntity> flit = from p in PRODUCT LIST select p;
Related
I have a solution which contains several UI projects and a bunch of common (shared) DLL projects. There also is a "DB access project" based on EF which contains the models and DbContext and is referenced by the UI projects.
The problem now is, that for each UI project that uses the DbContext from the "DB access project" I have to install the Entity Framework NuGet package. The problem gets worse when I have to update EF. Then I always have to take care that all projects using EF have installed the same EF version.
Is there a better solution? The correct way for me seems to only have to install EF in one place, the "DB access project".
I also thought about something like hiding EF and the DbSets behind proxy objects only to avoid references to EF.
When I omit the EF reference I get errors like
Error CS0012 The type 'DbContext' is defined in an assembly that is not referenced. You must add a reference to assembly 'EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
and
Error CS0012 The type 'DbSet<>' is defined in an assembly that is not referenced. You must add a reference to assembly 'EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.
Why do you want to expose the DbContext from the DAL Layer ? The DAL layer should ideally expose a class which uses the Dbcontext or Dbset. There shoudn't be any EF depdendencies on your UI Layer. There are multiple ways to achieve this. One commonly used pattern is called Repository Pattern.
You can see the E,g here
https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
Your problem is typically an example that shows why it isn't wise to expose the insides of your database to the outer world.
You should expose the sequences of data on which your uses want to be able to perform Linq statements. Usually it is enough to expose the classes of your DbSets of your DbContext as IQueryable collections. This is quite often called the Repository pattern.
If you hadn't a repository, then users would create an object of your DbContext class. Then they would use the DbSets of the DbContext to access your tables as IQueryable. Use these IQueryables to create and execute the queries, possibley use SaveChanges. Finally they would Dispose the DbContext,.
The repository is used similarly. Users of your Repository would create a Repository object. Access IQueryables that represent your tables. Perform the linq, possibley SaveChanges and Dispose the Repository.
If you only want to allow queries, expose IQueryable objects, if you want to be able To Add / Update / Remove fetched items, also add Add / Remove / SaveChanges functions.
If you expose these functions as interfaces, you can unit test your code by simply replacing your DbContext by an in-memory unit-test database, or sometimes just by using Lists.
For example a School context with Students, Teachers, Grades: Teachers have many Students; Students have many Teachers (many-to-many); Students have zero or more Grades, every Grade belongs to exactly one student (one-to-many).
All items in the School context are identified by an Id: they all implement IId. This is used to enable a generic class to fetch your items by Id
Queries only
Your DbContext with the interfaces:
interface IId
{
public int Id {get;}
}
class Teacher : IId {...}
class Student: IId {...}
class Grade: IId {...}
interface ISchoolQuerier
{
IQueryable<Teacher> Teachers {get; }
IQueryable<Student> Students {get;}
IQueryable<Grade> Grades {get;}
}
class SchoolDbContext : DbContext
{
public DbSet<Teacher> Teachers {get; set;}
public DbSet<Student> Students {get; set;}
public DbSet<Grades> {get; set;}
// explicit interface implementation
IQueryable<Teacher> ISchoolQuerier.Teachers {get{return this.Teachers;}}
IQueryable<Student> ISchoolQuerier.Students {get{return this.Students;}}
IQueryable<Grade> ISchoolQuerier.Grades{get{return this.Grades;}}
}
The SchoolRepository that uses this SchoolDbContext
class SchoolRepository : IDisposable, ISchoolQuerier
{
private SchoolRepository() : this (ISchoolQuerier)(new SchoolDbContext())
{ // default constructor used default SchoolDbContext
}
private SchoolRepository(ISchoolQuerier querier)
{ // use the provided querier
// this constructor is useful for unit tests
this.querier = querier;
}
private bool isDisposed = false;
private readonly ISchoolQuerier;
#region IDisposable
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(bool disposing)
{
if (disposing && !this.isDisposed)
{
IDisposable disposableSchool = this.SchoolQuerier as IDisposable;
if (disposableSchool != null)
{
this.schoolDbContext.Dispose();
}
this.isDisposed = true;
}
}
#endregion IDispose
#region ISchoolQuerier
public IQueryable<Teacher> Teachers {get{return this.SchoolDbContext.Teachers;}}
public IQueryable<Student> Students {get{return this.SchoolDbContext.Students;}}
public IQueryable Grades {get{return this.SchoolDbContext.Grades;}}
#endregion ISchoolQuerier
}
Users would use it similar to how they would use the SchoolContext:
using (var schoolRepository = new SchoolRepository())
{
var youngStudents = schoolRepository.Students
.Where(student => student.Birthday > ...)
.Select(student => new {...});
}
The SchoolRepository has two constructors. The default constructor uses the default DbContext. The other constructor uses the provided ISchoolQuerier. This other constructor can be useful for unit tests, where you can provide a class with three collections to test simple queries
If you only expose the interface ISchoolQuerier to users, then they don't have to install entity framework. You are also free to change internally how you access your database. Don't fancy entity framework anymore? Go ahead, use Dapper from now on, users won't notice the difference!
Change the database
If you want to add / update / delete items it consider creating a separate interface for it. Only those who have that interface can update / delete items.
Here I used the earlier defined IId
interface IChangeableSet<T> : IQueryable<T> : where T: IId
{
public T Get(int id)
public T Add(T item);
public T Remove(int Id);
public T Remove(T item);
}
class DbSet<T> : IChangeableSet<T> : where T: IId
{
public DbSet(System.Data.Entity.IDbset<T> dbSet)
{
this.dbset = dbSet
}
private readonly System.Data.Entity.IDbSet<T> dbSet;
public T Get(int id)
{
return this.DbSet.Where(item => item.Id == id).SingleOrDefault();
// here is where I uses that all items implement IId
}
public T Add(T item)
{
return this.dbSet.Add(item);
}
... // TODO: fill the other functions
}
The SchoolRepository implements this interface (preferrably explicit implementatin)
class ChangeableSchoolRepository : IDisposable, ISchoolQuerier
{
public ChangeableSchoolRepository(SchooldDbContext dbContext)
{
this.schoolDbContext = dbContext;
}
private readonly SchoolDbContext;
public IChangeableSet<Teacher> Teachers
{
get {return new DbSet(dbContext.Teachers);}
}
... // etc for Students / Grades
public void SaveChanges()
{
return this.SchoolDbcontext.SaveChanges();
}
}
I now use the following solution. In general, I encapsulated the functionality of EF and IQueryable that I require in three proxy objects.
Instead of using the EF DbContext directly, I now use a proxy object (the DataStore class) that offers me the DbSets (repositories).
The EF DbSets exposed by the DbContext I wrapped into a generic Repository, which exposes the functionality required to work with the data. The Repository itself is derived from DbQueryProxy which proxies the EF DbQuery.
Now I can comfortably work with the data in a familiar way:
using (var db = new DataStore())
{
var students = db.Students.DoSomeLinq(...);
var teachers = db.Teachers.SomeLinq(...);
// do some work ...
// note: The entities in students and teachers are still bound to the DbContext internal to DataStore!
// optional
db.SaveChanges();
}
DataStore:
public class DataStore : IDisposable
{
private readonly DbContext _dbContext;
#region constructors
public DataStore()
{
_dbContext = new DbContext();
}
#endregion constructors
internal DbContext Context => _dbContext;
public void RemoveRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : EntityBase => _dbContext.RemoveRange(entities);
#region db sets
public Repository<Teacher> Teachers => _dbContext.Teachers; // implicit type conversion from DbSet<TEntity> to Repository<TEntity>!
public Repository<Student> Students => _dbContext.Students;
public Repository<Grade> Grades => _dbContext.Grade;
#endregion db sets
#region DbContext
public void Dispose() => _dbContext.Dispose();
public int SaveChanges() => _dbContext.SaveChanges();
public Repository<TEntity> Set<TEntity>() where TEntity : EntityBase => _dbContext.Set<TEntity>();
public override bool Equals(object obj) => _dbContext.Equals(obj);
public override int GetHashCode() => _dbContext.GetHashCode();
public override string ToString() => _dbContext.ToString();
#endregion DbContext
}
Repository<TEntity>:
public class Repository<TEntity> : DbQueryProxy<TEntity> where TEntity : class
{
private readonly DbSet<TEntity> _DbSet;
internal Repository(DbSet<TEntity> dbSet) : base(dbSet)
{
_DbSet = dbSet;
}
private Repository()
{
}
public static implicit operator DbSet<TEntity>(Repository<TEntity> entry) => entry._DbSet;
public static implicit operator Repository<TEntity>(DbSet<TEntity> entry) => new Repository<TEntity>(entry);
#region DbSet<TEntity>
public TEntity Add(TEntity entity) => _DbSet.Add(entity);
public IEnumerable<TEntity> AddRange(IEnumerable<TEntity> entities) => _DbSet.AddRange(entities);
public TEntity Attach(TEntity entity) => _DbSet.Attach(entity);
public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, TEntity => _DbSet.Create<TDerivedEntity>();
public override bool Equals(object obj) => _DbSet.Equals(obj);
public override int GetHashCode() => _DbSet.GetHashCode();
public TEntity Find(params object[] keyValues) => _DbSet.Find(keyValues);
public Task<TEntity> FindAsync(params object[] keyValues) => _DbSet.FindAsync(keyValues);
public Task<TEntity> FindAsync(CancellationToken cancellationToken, params object[] keyValues) => _DbSet.FindAsync(cancellationToken, keyValues);
public TEntity Remove(TEntity entity) => _DbSet.Remove(entity);
public IEnumerable<TEntity> RemoveRange(IEnumerable<TEntity> entities) => _DbSet.RemoveRange(entities);
#endregion DbSet<TEntity>
}
DbQueryProxy<TEntity>:
public class DbQueryProxy<TResult> : IOrderedQueryable<TResult>, IListSource
{
private readonly DbQuery<TResult> _DbQuery;
internal DbQueryProxy(DbQuery<TResult> entry)
{
_DbQuery = entry;
}
protected DbQueryProxy()
{
}
public static implicit operator DbQuery<TResult>(DbQueryProxy<TResult> entry) => entry._DbQuery;
public static implicit operator DbQueryProxy<TResult>(DbQuery<TResult> entry) => new DbQueryProxy<TResult>(entry);
#region Interfaces
Type IQueryable.ElementType => ((IQueryable)_DbQuery).ElementType;
Expression IQueryable.Expression => ((IQueryable)_DbQuery).Expression;
IQueryProvider IQueryable.Provider => ((IQueryable)_DbQuery).Provider;
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)_DbQuery).GetEnumerator();
IEnumerator<TResult> IEnumerable<TResult>.GetEnumerator() => ((IEnumerable<TResult>)_DbQuery).GetEnumerator();
bool IListSource.ContainsListCollection => ((IListSource)_DbQuery).ContainsListCollection;
IList IListSource.GetList() => ((IListSource)_DbQuery).GetList();
#endregion Interfaces
#region DbQuery<TResult>
public string Sql => _DbQuery.Sql;
public DbQueryProxy<TResult> AsNoTracking() => _DbQuery.AsNoTracking();
public override bool Equals(object obj) => _DbQuery.Equals(obj);
public override int GetHashCode() => _DbQuery.GetHashCode();
public DbQueryProxy<TResult> Include(string path) => _DbQuery.Include(path);
public override string ToString() => _DbQuery.ToString();
#endregion DbQuery<TResult>
}
Upadate:
I have prepared a generic function for capturing database rows for generation the listing. My method is exactly like this.
public class GenericDataAccess<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
internal ELearningDBContext ELearningDBContext;
internal DbSet<TEntity> ELearningDBSet;
public GenericDataAccess(ELearningDBContext context)
{
this.ELearningDBContext = context;
this.ELearningDBSet = context.Set<TEntity>();
}
public virtual PagingModel<TEntity> GetAllPaged(int pagesize, NameValueCollection queryString)
{
return ELearningDBSet.AsQueryable().ToList();
}
}
Here I am facing a problem to sort this list. Because the TEntity is changing time to time. However, I have a common field in every Entity named Id. So I want to sort each and every model by Id in descending order.
I have tried this way. But it was not effective, moreover, it's generating an exception.
ELearningDBSet.AsQueryable()..OrderByDescending(i=>typeof(TEntity).GetProperty("Id")).ToList();
I have gone through these questions Q1, Q2, Q3, Q4. However, these were not effective. I prefer C# code. Any kind of perfect solution is highly appreciated. Thank you.
Wow!! I have just got a wonderful solution. I have just used it.
ELearningDBSet.AsQueryable().SortBy("Id" + " Desc").ToList();
Here the SortBy() function I used, got it from the .Net Library named System.Web.UI.WebControls.QueryExtensions. For more details see here. Really it was a wonderful and outstanding solution for me.
Create an interface for all of your data entities to implement
public interface IEntityBase
{
long Id { get; set; }
}
Then in your data class, in your case TEntity, Implement the IEntityBase interface.
public partial class MyTEntity: IEntityBase
{
public long Id { get; set; }
//Other attributes as needed.
}
In your generic class GenericDataAccess<TEntity> add a constraint to make TEntity implement the new interface IEntityBase. Then in the GetAllPaged method add the OrderByDescending() method call using the IEntityBase.Id property.
public class GenericDataAccess<TEntity> : IGenericRepository<TEntity> where TEntity : class, IEntityBase
{
internal ELearningDBContext ELearningDBContext;
internal DbSet<TEntity> ELearningDBSet;
public GenericDataAccess(ELearningDBContext context)
{
this.ELearningDBContext = context;
this.ELearningDBSet = context.Set<TEntity>();
}
public virtual PagingModel<TEntity> GetAllPaged(int pagesize, NameValueCollection queryString)
{
return ELearningDBSet.AsQueryable().OrderByDescending(o => o.Id).ToList();
}
}
UPDATED: Added the partial descriptor for the MyEntityclass.
You can build an Expression tree yourself e.g.
public static IQueryable<T> Sort<T>(this IQueryable<T> source, string field)
{
var p = Expression.Parameter(typeof(T));
var exp = Expression.Property(p, field);
return source.OrderBy(Expression.Lambda<Func<T, object>>(exp, p));
}
Using StructureMap I want to bind IQueryable<T> to ApplicationDbContext.Set<T>().AsQueryable().
So I can inject like this.
public class Foo
{
public Foo(IQueryable<MyEntity> query)
{
}
}
I've gotten this far
config.For(typeof(IQueryable<>))
.Use(x =>
x.GetInstance<ApplicationDbContext>().Set<TNotWorking>().AsQueryable());
But I can't figure out how to get the TNotWorking working :) I guess reflection isn't an option here, also I would avoid it if possible.
As pointed out in the comments, it's not possible w/o reflection or implementing each entity as a type in your project.
The last approach could look like this. When you create a base class which implements IQueryable<T>, then the code required for adding new entities is minimal and will also work when there is more than a single DbContext available for injection, which is almost always the case in any non-trivial demo application.
public abstract class QueryableEntityBase<TEntity> : IQueryable<TEntity> where TEntity : class
{
protected QueryableEntityBase(DbContext context)
{
Context = context ?? throw new ArgumentNullException(nameof(context));
Queryable = context.Set<TEntity>().AsQueryable();
}
public Type ElementType => Queryable.ElementType;
public Expression Expression => Queryable.Expression;
public IQueryProvider Provider => Queryable.Provider;
protected IQueryable<TEntity> Queryable { get; }
private DbContext Context { get; }
public IEnumerator<TEntity> GetEnumerator() => Queryable.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => Queryable.GetEnumerator();
}
// You need to create this per entity you want to be injectable
public class MyQueryableEntity : QueryableEntityBase<MyEntity>
{
public MyQueryableEntity(ApplicationDbContext context) : base(context) { }
}
This has the additional advantage that you can change the underlying query/persistence provider by entity, by changing QueryableEntityBase<T> base with MonogoDbQueryableEntityBase<T> which uses an IMongoClient instead of DbContext.
The registration should be something along the line of (not familiar with StructureMap, you'd need to register all of it's types though or scan the assembly).
config.For(typeof(IQueryable<>)).Use(typeof(QueryableEntityBase<>));
In a simple case where you only have a single database, you could also make the base class non-abstract and just resolve QueryableEntity<T>, but like I said you'll hit the limitation of one single DbContext per app sooner or later, so it's best to be done explicitly.
Or alternatively extend the implementation so you can define the context too
public class QueryableEntityBase<TContext, TEntity> : IContextSetQueryable<TDbContext, TEntity>, IQueryable<TEntity> where TEntity : class, TContext : DbContext
{
private TContext Context { get; }
protected QueryableEntityBase(TContext context)
{
Context = context ?? throw new ArgumentNullException(nameof(context));
Queryable = context.Set<TEntity>().AsQueryable();
}
}
But then you need an additional interface:
public interface IContextSetQueryable<TContext, TContext> : IQueryable<TEntity> where TContext : DbContext { }
Then inject IContextSetQueryable<TContext, TContext> entity into your services.
But then you lose the ability to have persistence agnostic domain, since you'll need the ability to reference DbContext and it's subtypes, so it's not really elegant and you could as well use database specific interfaces, such as
public interface ISomethingDatabase
{
IQueryable<User> Users { get; }
IQueryable<Customer> Customers { get; }
...
}
I have a base class Repository which contains base functionality for a set of classes that inherit from it such as UserRepository or DepartmentRepository.
I'm using automapper to map between my entity framework objects and my domain objects.
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class {
protected readonly DbContext Context;
public Repository(DbContext context) {
Context = context;
}
public TEntity Get(int id) {
return Context.Set<TEntity>().Find(id);
}
}
public class UserRepository : Repository<User>, IUserRepository {
public UserRepository(DbContext context) : base(context) {
}
public User GetUserByNTId(string NTId) {
return Mapper.Map<User>(DbContext.Users.SingleOrDefault(u => u.NTId == NTId));
}
}
GetUserByNTId will work because I can use automapper in the return statement. But Get will not work because it deals with TEntity and I don't know how you can tell automapper to examine the type of TEntity and look for a matching mapping.
How can I change the return statement of the Get function so that it will work for all my derived repositories and still use automapper? Or do I just have to take all my general functions and push them down into the derived classes and eliminate the Repository base class?
It's a very bad idea to for mixing AutoMapper and other responsibilities when querying from a Repository pattern. It's also of course a violation of the Single Responsibility Principle.
A naive non tested implementation will be:
public class ExampleOfGenericRepository<TEntity> : Repository<TEntity>
where TEntity : class
{
public ExampleOfGenericRepository(DbContext context)
: base(context)
{
}
public TEntity GetById(int id)
{
return Mapper.Map<TEntity>(Get(id));
}
}
I have a generic repository that I use for common things such as FetchAllData, GetbyID and so on... Anyway, I want to include a Deactivate(T Entity) method so that instead of deleting data I will just turn their status off so the user will not see the data, but I can see it whenever I need. Basically, something similar to:
public interface IGenericRepository<T> where T : class {
...somecode
}
public class GenericRepository<T> : IGenericRepository<T> where T : class {
public T GetbyID(int id) { ... }
public void Deactivate(T entity) {
entity.stat = 0; // I know that this stat is common in all tables. However,
// my problem is that I don't know how to make appear stat
// in IntelliSense.
}
}
I know that this can be done, but I how do I do it?
Declare a interface:
public interface IDeactivatable {
int stats { get; set; }
}
Then your entities must derive from IDeactivatable.
Tip: You can add a generic type constraint too:
[...] IGenericRepository<T> where T : class, IDeactivatable [...]