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 */ }
}
Related
In my project previously I was having individual repository for each entity but I am now refactoring it to a common repository while taking advantage of Base Entity.
BaseEntity.cs :
public abstract class BaseEntity<T> : IEntity<T>
{
[NotMapped]
public abstract T Id { get; set; }
}
EFRepository.cs:
public class EFRepository<TEntity, TId> : IRepository<TEntity, TId> where
TEntity : BaseEntity<TId>, new()
{
private readonly IDbContext _context;
private DbSet<TEntity> _entities;
public EFRepository(IDbContext context)
{
_context = context;
}
private DbSet<TEntity> Entities
{
get { return _entities ?? (_entities = _context.Set<TEntity>()); }
}
public async Task<TEntity> GetByIdAsync(TId id)
{
return await Entities.FindAsync(id);
}
public void Insert(TEntity entity)
{
Entities.Add(entity);
_context.SaveChanges();
}
public async Task UpdateAsync(TEntity entity)
{
await _context.SaveChangesAsync();
}
public void Delete(TId id)
{
var entity = new TEntity
{
Id = id
};
// Attach the entity to the context and call the delete method.
Entities.Attach(entity);
Delete(entity);
}
public void Delete(TEntity entity)
{
Entities.Remove(entity);
_context.SaveChanges();
}
public IList<TEntity> Table
{
get { return Entities.ToList(); }
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_context != null)
{
_context.Dispose();
}
}
}
The above implementation works perfect in most of the cases, but there's on scenario in which I have a customer entity and that customer entity contains field SearchTerms so in case if I have to perform filter base on the SearchTerms I am not able to achieve it with above approach. Also I don't want to add that field in Base Entity as it's just specific to Customer Entity.
Any help on how I can filter the result base on SearchTerms?
Create a separate CustomerRepository class and implement your own filter logic as like below.
public class CustomerRepository : EFRepository<Customer, Guid>, ICustomerRepository
{
public CustomerRepository(IDbContext context): base(context) {}
public async Task<List<Customer>> GetCustomerBySearchTerms(string[] searchTerms)
{
//implement your logic here
}
}
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
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.
I'm writing a simple logging mechanism for my app.
I have generic repository:
public class GenericRepository<TEntity>:IRepository<TEntity> where TEntity : class
{
internal Equipment_TestEntities context;
internal DbSet<TEntity> dbSet;
internal Log entry;
public GenericRepository(Equipment_TestEntities context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
this entry= new Log();
}
public virtual void Insert(TEntity entity)
{
dbSet.Add(entity);
AddLog("insert "+typeof(TEntity)+" "+entity.ToString());
}
private void AddLog(string action)
{
entry.Action = action;
entry.Date = DateTime.Now;
string username = HttpContext.Current.User.Identity.Name;
username = username.Substring(username.LastIndexOf("\\") + 1);
entry.Who = 1;
context.Logs.Add(entry);
}
}
In entry.Action I want to keep:
Action eg. Insert
Which entityUsed eg. User or Role
Something to identify entity
In 1) I can easily hardcone action
2) I can use TypeOf and get entity class name
But in 3rd I have a bit problem.
In case of insert I can ask db for the newest record but what should I do in Edit/remove cases?
Is there any ways to get properties value from those entities?
#Update:
sample part from unitOfWork:
public IRepository<Storage> storageRepository
{
get
{
if (this.StorageRepository == null)
{
this.StorageRepository = new GenericRepository<Storage>(context);
}
return StorageRepository;
}
}
IUnitOfWork:
public interface IUnitOfWork : IDisposable
{
IRepository storageRepository { get; }
}
I'd create an interface for the entities:
public interface IEntity
{
int? Id { get; set; }
}
Then change the generic constraint:
public class GenericRepository<TEntity>:IRepository<TEntity>
where TEntity : class, IEntity
{
...
}
Now you can simply use entity.Id to identify your entities.:
public virtual void Remove(TEntity entity)
{
if (!entity.Id.HasValue) {
throw new Exception("Cannot remove " + entity + ", it has no ID");
}
...
}
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; }
}
}