implement generic repository in ASP.NET MCV5 - c#

i am using ASP.NET mvc 5 in visual studio 2013. I have i process to implement generic repository and later on UnitOfWork. I have IGenericRepository which has IQueryable one function, because i want to learn and understand so i kept simple as possible. I have GenericRepository class where i am implementing this interface. I got FunctionContext which is inherited from baseContext. The reason i have baseContext so all the dbcontexts can use one path to hit database but same time keep number of table limited to business need.
I got error coming in GenericRepository class under GetAll function, i believe i am not extracting data properly.
many thanks in advanced....
IGernericRepository
public interface IGenericRepository<TEntity> : IDisposable
{
IQueryable<TEntity> GetAll { get; }
}
GenericRepository
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
private FunctionsContext _Context = new FunctionsContext();
public GenericRepository()
{
}
public IQueryable<TEntity> GetAll()
{
/* return _Context.Functions.Select(x => new Functions
{
Function_ID = x.Function_ID,
Title = x.Title,
Hierarchy_level = x.Hierarchy_level
});*/
????????????????????? need help here!
}
public void Dispose()
{
}
}
FunctionsContext
public class FunctionsContext : BaseContext<FunctionsContext>
{
public DbSet<App_Functions> Functions { get; set; }
}
BaseContext
public class BaseContext<TContext> : DbContext where TContext : DbContext
{
static BaseContext()
{
Database.SetInitializer<TContext>(null);
}
protected BaseContext()
: base("name = ApplicationDbConnection")
{ }
}
Functions Table (model)
[Table("Functions")]
public class App_Functions
{
public App_Functions()
{
// this.App_Controllers = new List<App_Controllers>();
}
[Key]
public int Function_ID { get; set; }
[StringLength(50)]
[Required]
public string Title { get; set; }
public int Hierarchy_level { get; set; }
}
Controller class
using (var repository = new GenericRepository<Functions>())
{
foreach(var functions in repository.GetAll)
{
var a7 = functions.Title;
??????
}
}

DbSet<TEntity> implements IQueryable<> so you should be able to just do this:
public IQueryable<TEntity> GetAll()
{
return _Context.Functions;
}
To make it generic, working for all types of TEntity:
public IQueryable<TEntity> GetAll()
{
return _Context.Set<TEntity>();
}
Give it a try and let me know if it works for you.

I don't think the other answer really addresses the ?????? bit.
This does:
public IQueryable<TEntity> GetAll()
{
return _Context.Set<TEntity>();
}
Although, I wouldn't do it as a method. Do it as a read-only property.
public IQueryable<TEntity> Entities
{
get { return _Context.Set<TEntity>(); }
}

// Generic Repository
public class Repository<T> where T : class
{
private readonly DbSet<T> _dbSet;
public Repository(DbSet<T> dbSet)
{
_dbSet = dbSet;
}
public IEnumerable<T> Where(Expression<Func<T, bool>> where)
{
return _dbSet.Where(where);
}
public T FirstOrDefault(Expression<Func<T, bool>> where)
{
return _dbSet.FirstOrDefault(where);
}
// other methods
}
// Unit of Work interface
public interface IUnitOfWork
{
Repository<Product> ProductsRepository { get; }
}
// Unit of Work implementation
public class MyContext : DbContext, IUnitOfWork
{
private readonly Repository<Product> _products = null;
public DbSet<Product> Products { get; set; }
public MyContext()
: base("MyContext")
{
_products = new Repository<Product>();
}
public Repository<Product> ProductsRepository
{
get { return _products; }
}
}

Related

return a list of generic C#

I have a singleton class, holding several list of data. I want to have a function to return one of the list based on the requested data type
public interface IRentable
{
long Id { get; set; }
}
public class Book : IRentable
{
private long _id;
public long Id
{
get { return _id; }
set { _id = value; }
}
public string Name { get; set; }
public int Pages { get; set; }
}
public class DVD : IRentable
{
private long _id;
public long Id
{
get { return _id; }
set { _id = value; }
}
public string Name { get; set; }
public TimeSpan Length { get; set; }
}
public class DBReferenceSingleton
{
private List<Book> _bookList;
private List<DVD> _dvdList;
public IEnumerable<Entity> GetEntities<Entity>() where Entity : IRentable
{
switch(typeof(Entity).Name)
{
case nameof(Book):
return _bookList;
case nameof(DVD):
return _dvdList;
}
return null;
}
This is not working, since a conversion is needed. I wonder if there is any elegant solution (without serialize all elements or implement IConvertible)?
Background: I was looking into the implementation of Repository pattern implementation from Microsoft document website and a couple of YouTube videos. Their base class implementations are bounded with DBContext,
[from Microsoft doc]
public class GenericRepository<TEntity> where TEntity : class
{
internal SchoolContext context;
internal DbSet<TEntity> dbSet;
[from youtube]
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext Context;
public Repository(DbContext context)
{
Context = context;
}
public IEnumerable<TEntity> GetAll()
{
return Context.Set<TEntity>().ToList();
}
So, this means this base class cannot be used for databases without EntityFramework. I'm trying to remove DBContext in my base class
public class InMemoryRepository<TEntity> : IRepository<TEntity> where TEntity : IRentable
{
private IEnumerable<TEntity> _entities;
public IEnumerable<TEntity> GetAll()
{
if (_entities == null)
{
_entities = DBReferenceSingleton.GetInstance.GetEntities<TEntity>();
}
return _entities;
}
public TEntity Get(long id)
{
GetAll();
if (_entities == null)
{
return default(TEntity);
}
return _entities.FirstOrDefault(t => t.Id == id);
}
(I was wrong in my previous code, as I was using "class" instead of "IRentable" as I thought it will be generic for all classes. After implementing the Get(long id) function. I found I have to have a IRentable interface so I made the changes. And the answer from The Lemon work perfectly.
Thanks
It doesn't look like generics seem the right tool here; not least because Book and DVD don't have a common base class.
I think you should have two methods:
public List<Book> GetBooks() => _bookList;
public List<DVD> GetDvds() => _dvdList;
Have you tried doing a Cast?
public class Book
{
public string Name { get; set; }
public int Pages { get; set; }
}
public class DVD
{
public string Name { get; set; }
public TimeSpan Length { get; set; }
}
public class DBReferenceSingleton
{
private List<Book> _bookList;
private List<DVD> _dvdList;
public IEnumerable<Entity> GetEntities<Entity>() where Entity : class
{
switch(typeof(Entity).Name)
{
case nameof(Book):
return _bookList as List<Entity>;
case nameof(DVD):
return _dvdList as List<Entity>;
}
return null;
}
}
the "as List< Entity >" will return null if the object is the wrong type, or the object as the type if it's the right type. The A = B as C pattern works for inherited types as well, just remember to check for null returned values for cases where your type isn't as well known as in this situation
A side comment on the usefulness of having a generic method in this case: In this method you're forced to set the type of entity each time explicitly, which means your method is functionally non-generic - so you might as well make two explicit methods.
One case where a generic method like the one you have might be more useful, is if book and dvd both inherited from a base class, and you had some follow up methods that needed to operate off a list of . For example, you might end up wanting to do something like this instead in your code:
public class Book : Rentable
{
public int Pages { get; set; }
}
public class DVD : Rentable
{
public TimeSpan Length { get; set; }
}
public class Rentable
{
public string Name { get; set; }
public string borrowedBy { get; set; }
}
public class DBReferenceSingleton
{
private List<Book> _bookList;
private List<DVD> _dvdList;
public enum RentableType { Book, DVD }
public IEnumerable<Rentable> GetEntities(RentableType entityType)
{
switch (entityType)
{
case RentableType.Book:
return _bookList.ToList<Rentable>();
case RentableType.DVD:
return _dvdList.ToList<Rentable>();
default:
throw new NotImplementedException($"Entity {entityType} not supported");
}
return null;
}
}

Type 'TEntity' doesn't match expected type '???'

I've built a generic repository interface off which hang a number of entity specific repository interfaces. Mirroring this structure are a number of concrete repository classes.
I'm getting the following error in my base Repository class:
Type 'TEntity' doesn't match expected type '???'.
Method 'GetAll' cannot implement method from interface '...IRepository<TEntity, TIdentity>'. Return type should be 'System.Collections.Generic.List<???>'.
The minified interface/class structure is as follows:
IRepository:
public interface IRepository<TEntity, in TIdentity> where TEntity : IEntity<TIdentity>
{
...
List<TEntity> GetAll();
...
}
Repository:
internal class Repository<TEntity, TIdentity> : IRepository<TEntity, TIdentity>
where TEntity : class, IEntity<TIdentity>
{
...
protected DbSet<TEntity> Set => _set ?? (_set = _context.Set<TEntity>());
public List<TEntity> GetAll()
{
return Set.ToList();
}
...
}
IRoleRepository:
public interface IRoleRepository : IRepository<Role, Guid>
{
...
Role FindByName(string roleName);
...
}
RoleRepository:
internal class RoleRepository : Repository<Role, Guid>, IRoleRepository
{
...
public Role FindByName(string roleName)
{
return Set.FirstOrDefault(x => x.Name == roleName);
}
...
}
This has a knock on effect in my consuming classes where doing a RoleRepository.GetAll() returns a List<???> rather than a List<Role> as I expected.
Edit - Entity definitions...
IEntity:
public interface IEntity<T>
{
T Id { get; set; }
byte[] Version { get; set; }
}
Entity:
public abstract class Entity<T> : IEntity<T>
{
public T Id { get; set; }
public byte[] Version { get; set; }
}
Role:
public class Role : Entity<Guid>
{
private ICollection<User> _users;
public string Name { get; set; }
public ICollection<User> Users
{
get { return _users ?? (_users = new List<User>()); }
set { _users = value; }
}
}
It looks like your TEntity class is not implementing that interface: IEntity<TIdentity>
Depends if your are using CodeFirst at EntityFramework or ModelFirst you have other options to go with:
ModelFirst: you can use Model.tt file to specify implementation of interfaces you need.
CodeFirst: just implement that interface in the model class.
Third option is just get rid of that where TEntity : IEntity<TIdentity> only if your are not using anything from that interface of course.

Controller with same structure not working using an interface

I have created 8 models and controllers respectively for my Project using the same structure, however now this structure is not working. It is giving me this error:
The current type, HelpDesk.Contracts.Repositories.IRepositoryBase`1[HelpDesk.Model.Knowledgebase], is an interface and cannot be constructed. Are you missing a type mapping?
Here is my interface:
namespace HelpDesk.Contracts.Repositories
{
public interface IRepositoryBase<TEntity>
where TEntity : class
{
void Commit();
void Delete(object id);
void Delete(TEntity entity);
System.Linq.IQueryable<TEntity> GetAll();
System.Linq.IQueryable<TEntity> GetAll(object filter);
TEntity GetById(object id);
TEntity GetFullObject(object id);
System.Linq.IQueryable<TEntity> GetPaged(int top = 20, int skip = 0, object orderBy = null, object filter = null);
void Insert(TEntity entity);
void Update(TEntity entity);
}
}
Here is my controller:
namespace HelpDesk.WebUI.Controllers
{
public class KnowledgebaseController : Controller
{
private DataContext db = new DataContext();
IRepositoryBase<Knowledgebase> knowledgebases;
public KnowledgebaseController(IRepositoryBase<Knowledgebase> knowledgebases)
{
this.knowledgebases = knowledgebases;
}
public ActionResult ChooseSearchView()
{
string r = Session["LoggedUserRole"].ToString();
if (r == "Technician")
{
return RedirectToAction("TechKnowledgebaseSearch");
}
else
{
return RedirectToAction("UserKnowledgebaseSearch");
};
}
My DataContext:
namespace HelpDesk.Contracts.Data
{
public class DataContext : DbContext
{
/// <summary>
/// you can either pass the NAME of a connection string (e.g. from a web.
/// ), and explicitly i
/// </summary>
public DataContext() : base("HelpDesk")
{
}
/// <summary>
/// any entity to be persisted must be declared here.
/// </summary>
///
public DbSet<Category> Categories { get; set; }
public DbSet<Knowledgebase> Knowledgebases { get; set; }
public DbSet<Problem> Problems { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<Ticket> Tickets { get; set; }
public DbSet<TicketNote> TicketNotes { get; set; }
public DbSet<TicketStatus> TicketStatuses { get; set; }
public DbSet<TicketSubscription> TicketSubscriptions { get; set; }
public DbSet<User> Users { get; set; }
}
}
Here is the repositorybase:
namespace HelpDesk.Contracts.Repositories
{
public abstract class RepositoryBase<TEntity> : HelpDesk.Contracts.Repositories.IRepositoryBase<TEntity> where TEntity : class
{
internal DataContext context;
internal DbSet<TEntity> dbSet;
public RepositoryBase(DataContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual TEntity GetById(object id)
{
return dbSet.Find(id);
}
public virtual IQueryable<TEntity> GetAll()
{
return dbSet;
}
public IQueryable<TEntity> GetPaged(int top = 20, int skip = 0, object orderBy = null, object filter = null)
{
return null; // need to override in order to implement specific filtering and ordering
}
public virtual IQueryable<TEntity> GetAll(object filter)
{
return null; //need to override in order to implement specific filtering
}
public virtual TEntity GetFullObject(object id)
{
return null; //need to override in order to implement specific obect graph.
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
}
public virtual void Update(TEntity entity)
{
dbSet.Attach(entity);
context.Entry(entity).State = EntityState.Modified;
}
public virtual void Delete(TEntity entity)
{
if (context.Entry(entity).State == EntityState.Detached)
dbSet.Attach(entity);
dbSet.Remove(entity);
}
public virtual void Delete(object id)
{
TEntity entity = dbSet.Find(id);
}
public virtual void Commit()
{
context.SaveChanges();
}
public virtual void Dispose()
{
context.Dispose();
}
}
}
Here I have a similar structured Controller that works just fine and another 7 like that:
namespace HelpDesk.WebUI.Controllers
{
public class RoleController : Controller
{
private DataContext db = new DataContext();
IRepositoryBase<Role> roles;
public RoleController(IRepositoryBase<Role> roles)
{
this.roles = roles;
}
The Exception is thrown when I click on this link:
<li>#Html.ActionLink("Knowledgebase", "ChooseSearchView", "Knowledgebase")</li>
Update your KnowledgebaseController constructor to this:
public KnowledgebaseController()
{
this.knowledgebases = new RepositoryBase<Knowledgebase>(db);
}
and remove abstract from the RepositoryBase<TEntity> class in order to make it instantiable.
Please note: This is just a quick fix. To implement a much solid architecture which facilitates easy unit testing and loosely coupled classes, I would suggest you implement a unit of work. Follow this link Repository Pattern and Unit of Work with Entity Framework in ASP.NET MVC
You can't instantiate an interface, you have to instantiate a class that implements that interface.
public class RepositoryBase<T> : IRepositoryBase
{
public RepositoryBase<T>()
{
}
...
}
Then instantiate your concrete class with that type
IRepositoryBase<Knowledgebase> knowledgebases = new RepositoryBase<Knowledgebase>();
Read more about interfaces and instantiation here: Using Interfaces

Passing value to FindById method using Generic types doesn't work

I have a repository for all my entities like this :
public abstract class GenericRepository<C, T> :
IGenericRepository<T>
where T : class
where C : DbContext, new()
{
private C _entities = new C();
public C Context
{
get { return _entities; }
set { _entities = value; }
}
public virtual IQueryable<T> GetAll()
{
IQueryable<T> query = _entities.Set<T>();
return query;
}
public IQueryable<T> FindBy(System.Linq.Expressions.Expression<Func<T, bool>> predicate)
{
IQueryable<T> query = _entities.Set<T>().Where(predicate);
return query;
}
public virtual void Add(T entity)
{
_entities.Set<T>().Add(entity);
}
public virtual void Delete(T entity)
{
_entities.Set<T>().Remove(entity);
}
public virtual void Edit(T entity)
{
_entities.Entry(entity).State = System.Data.Entity.EntityState.Modified;
}
public virtual void Save()
{
_entities.SaveChanges();
}
}
So i create a User repository that inherences from the above class as you can see here :
public class userRepository:GenericRepository<InModelContainer,User>
{
}
My problem is ,how can i call FindBy() method in my class ,My User entity is like this :
public partial class User
{
public User()
{
this.Ideas = new HashSet<Idea>();
this.Companies = new HashSet<Company>();
}
public int Id { get; set; }
[DisplayName("نام")]
[Required(ErrorMessage = "نام را وارد کنید")]
public string Name { get; set; }
}
I need to find a User By id,so i have to use findBy method but i don't know how can i call this method .
Example:
userRepository objRepository= new userRepository();
public ActionResult Edit(int id)
{
return View(objRepository.FindBy());
}
What should i pass to the above function ?
Best regards.
I am using MVC4 and EF6.
here you go
userRepository objRepository= new userRepository();
public ActionResult Edit(int id)
{
return View(objRepository.FindBy(user => user.Id == id).FirstOrDefault());
}
this will filter users based on the id and will take the first one or null if there is no match. You can choose to use .First() also if you want an exception instead.

Generic access to DbContext

ObjectContext allows generic access to the generated Entities. DbContext appears to have no such support. Accessing EF5 with a generic repository is challenging. Let's say I want a generic mechanism to read any given entity, call it TEntity:
public class DataRepositoryEF5<T> where T: DbContext
{
private ObjectContext _context;
public DataRepositoryEF5(DbContext context)
{
_context = ((IObjectContextAdapter)context).ObjectContext;
}
public IEnumerable<TEntity> ReadAll<TEntity>() where TEntity : class,new()
{
return GetObjectSet<TEntity>().AsEnumerable();
}
protected ObjectSet<TEntity> GetObjectSet<TEntity>() where TEntity : class,new()
{
ObjectSet<TEntity> result;
result = _context.CreateObjectSet<TEntity>();
return result;
}
}
Usage
var context = new MyContext();
var repository = new DataRepositoryEF5<MyContext>(context);
IEnumerable<Document> results = repository.GetAll<Document>();
foreach (var item in results)
{
Console.WriteLine("{0} {1} {2} {3}", item.Description, item.Id, item.Property, item.Whatever);
}
EF used to generate classes with a common base type EntityObject. Since that is no longer the case the best constraint I can have is class...
Second, because there is no equivalent of ObjectContext.CreateObjectSet<>() I am forced to cast from DbSet to ObjectSet.
With this pattern in place I have no need for DbContext. Without generics I am forced to hand code all CRUD operations. Did I miss something? If not, is it possible to tell EF5 to generate code with ObjectContext instead?
have a look at this tip that I posted a while ago
Anyway the trick is by using context.Set<>
public interface IEntity
{
int Id { get; set; }
}
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
{
private IDbContext _context;
public Repository(IDbContext context)
{
_context = context;
}
private IDbSet<TEntity> DbSet
{
get
{
return _context.Set<TEntity>();
}
}
public IQueryable<TEntity> GetAll()
{
return DbSet.AsQueryable();
}
public void Delete(TEntity entity)
{
DbSet.Remove(entity);
}
.....
This is a more up to date and complete answer.
using Library.Models;
using Library.EF;
namespace Abstractions
{
public interface IKeyedModel
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
}
public class GenericContext<T> : DbContext where T : class, IKeyedModel
{
public DbSet<T> Items { get; set; } = default!;
}
public interface IRepository<T> where T : class, IKeyedModel
{
public GenericContext<T> Context() => new GenericContext<T>();
public T Select(int id) => Context().Items.Single(x => x.Id == id);
public IQueryable<T> Select() => Context().Items;
public void Insert(T item)
{
var context = Context();
context.Items.Add(item);
context.SaveChanges();
}
public void Update(T item)
{
var context = Context();
context.Items.Attach(item);
context.SaveChanges();
}
public void Delete(int id)
{
var context = Context();
var item = context.Items.Single(x => x.Id == id);
context.Items.Remove(item);
context.SaveChanges();
}
}
}
namespace Models
{
[Table(name: "User")]
public class User : IKeyedModel
{
public int Id { get; set; }
public string Name { get; set; } = default!;
}
}
namespace App
{
public class UserRepository : IRepository<User> { /* empty */ }
}

Categories

Resources