i try to write Generic Repository in EF 4. But my codes is not looking good. Because ExpressionEntities is not GENERIC.
I want to convert this codes:
public class DataRepository<T> : IRepository<T> where T : class
{
private ExpressionsEntities _context;
public DataRepository()
{
}
public DataRepository(ExpressionsEntities context)
{
_context = context;
}
}
to the following:
public class DataRepository<T> : IRepository<T> where T : class
{
private GetGenericEntityCONTEXT _context;
public DataRepository()
{
}
public DataRepository(GetGenericEntityCONTEXT context)
{
_context = context;
}
}
because ExpressionsEntities not global my entities : ExpressionsEntities1, ExpressionsEntities 2, ExpressionsEntities 3 etx... i need to write get take entity for example:
public class DataRepository<T> : IRepository<T> where T : class
{
private Entity _context;
public DataRepository()
{
}
public DataRepository(Entity context)
{
_context = context;
}
public class Main
{
main()
{
new DataRepository(ExpressionEntities)
}
}
}
Not 100% sure what your asking - from what i can understand, you wan't a way to dynamically create the entity set based on T.
Well that's easy enough:
public class DataRepository<T> : IRepository<T> where T : class
{
private ObjectContext _ctx;
public DataRepository<T>(ObjectContext ctx)
{
this._ctx = ctx;
}
public IObjectSet<T> CurrentEntitySet<T>()
{
get
{
var entityName = _plularizer.Pluralize(typeof(T).Name);
string entitySetName = string.Format("{0}.{1}", EntityContainerName, entityName);
return _ctx.CreateObjectSet<T>(entitySetName );
}
}
}
Then your specific Repository could do this (for example):
public class AppleRepository : DataRepository<Apple>
{
public AppleRepository(IObjectContext ctx) : base(ctx) {}
public ICollection<Apple> FindApples(Func<Apple,bool> predicate)
{
return CurrentEntitySet.Where(predicate).ToList();
}
}
And when your creating your repository, pass through the object context - preferably by DI:
var repository = new AppleRepository(new ExpressionEntities()); // should be DI'ed
Basically, we're making use of Pluralization (the same code used by Entity Framework to pluralize entity set names), and CreateObjectSet<T>.
So if you created a DataRepository<Apple>, that would translate to an entity set name of Apples (which should match the entity set on your model), and we create an entity set based on that.
The EntityContainerName property is what's on your EDMX - you should pass this through the ctor (via DI preferably).
Does that answer your question?
I prefer to get the entityName like this.
EntityContainer container = context.MetadataWorkspace.GetEntityContainer(context.DefaultContainerName, DataSpace.CSpace);
EntitySetBase entitySet = container.BaseEntitySets.Where(item => item.ElementType.Name.Equals(typeof(T).Name)).FirstOrDefault();
var entityName = entitySet.Name
This deals with some cases if you have some classes pluralized, and some classes not.
Related
I'm working on an Asp.net core 5 project targeted .Net 5, I try to create an Action filter that will receive a property name, and he will recieve a TEntity generic type (represent the table to select from) and catch the submitted model and get the property value from it (model), this Action Filter will look in database if a record already has the same value in the past property inside the table passed in TEntity.
My Action filter:
public class RecordShouldNotExistFilter<TEntity>:IActionFilter where TEntity : class
{
private readonly AppDbContext _dbContext;
public string PropertyToBeChecked { get; set; }
public RecordShouldNotExistFilter( AppDbContext dbContext )
{
_dbContext = dbContext;
}
public void OnActionExecuting( ActionExecutingContext context )
{
// Some logic here that uses the PropertyToBeChecked's value
}
}
public void OnActionExecuted( ActionExecutedContext context )
{
}
}
The problem:
When I try to apply the filter on my action I don't know how to pass the PropertyToBeChecked value.
I paused here :
[TypeFilter(typeof(RecordShouldNotExistFilter<PedagogicalSequence>))]
public async Task<IActionResult> Create( PedagogicalSequenceModel model )
{
}
The question :
How can I pass the PropertyToBeChecked value ? or how to achieve my object with another way ? except using Action parameters
You can get the property to be checked in the constructor of your filter, like so:
public RecordShouldNotExistFilter(AppDbContext dbContext, string propertyToBeChecked)
{
_dbContext = dbContext;
PropertyToBeChecked = propertyToBeChecked;
}
And then pass that value into the filter attribute:
[TypeFilter(typeof(RecordShouldNotExistFilter<PedagogicalSequence>), Arguments = new object[] { "PropertyName" })]
public async Task<IActionResult> Create(PedagogicalSequenceModel model)
{
}
Generic attributes aren't supported so your other option is to make a non-generic attribute following this answer and get the entity type through a Type parameter. You would then use reflection to get the generic implementation:
public class RecordShouldNotExistFilterAttribute : TypeFilterAttribute
{
public RecordShouldNotExistFilterAttribute(Type entityType, string propertyToBeChecked)
: base(typeof(RecordShouldNotExistFilter<>).MakeGenericType(entityType))
{
Arguments = new object[] { propertyToBeChecked };
}
}
public class RecordShouldNotExistFilter<TEntity> : IActionFilter where TEntity : class
{
readonly AppDbContext _dbContext;
public string PropertyToBeChecked { get; set; }
public RecordShouldNotExistFilter(AppDbContext dbContext, string propertyToBeChecked)
{
_dbContext = dbContext;
PropertyToBeChecked = propertyToBeChecked;
}
}
This would allow you to do this instead:
[RecordShouldNotExistFilter(typeof(PedagogicalSequenceModel), "PropertyName")]
public async Task<IActionResult> Create(PedagogicalSequenceModel model)
Situation
Here I am, trying to write some unit tests for my GroupService with the use of MOQ.
To create an instance of my GroupService, I mocked 4 interfaces that needed to be passed through the constructor. Now on one of the mocks (IGroupRepository) a property called Context is called and my idea was to SetupGet this property and just simply return a fake list of GroupUser. But I keep getting errors, whatever I try.
Code
public class GroupServiceTests
{
private readonly GroupService _groupService;
private readonly Mock<AppDbContext> _dbContext;
private readonly Mock<IGroupRepository> _groupRepository;
private readonly Mock<IComponentService> _componentService;
private readonly Mock<IUserContextService> _userContextService;
private readonly Mock<IModelEntityMapper<Group, Core.DbContexts.Entities.Group>> _mapper;
public GroupServiceTests()
{
var groupUsersMock = CreateDbSetMock(GetFakeListOfGroupUsers());
_dbContext = new Mock<AppDbContext>(new DbContextOptions<AppDbContext>());
_dbContext.SetupGet(x => x.GroupUser).Returns(groupUsersMock.Object);
_groupRepository = new Mock<IGroupRepository>();
_groupRepository.SetupGet(repo => repo.Context).Returns(_dbContext.Object);
_componentService = new Mock<IComponentService>();
_userContextService = new Mock<IUserContextService>();
_mapper = new Mock<IModelEntityMapper<Group, Core.DbContexts.Entities.Group>>();
_groupService = new GroupService(_groupRepository.Object, _componentService.Object, _userContextService.Object, _mapper.Object);
}
}
In the GroupService this line is called:
// _repository reffers to IGroupRepository
userIdsForContextReset.AddRange(_repository.Context.GroupUser.Where(x => groupIds.Contains(x.GroupId)).Select(x => x.UserId));
And the GroupRepository and EntityRepository look like this:
public interface IGroupRepository : IEntityRepository<AppDbContext, Group>
{
List<GroupPermission> GetInheritedGroupPermissions(int groupId);
}
public class GroupRepository : EntityRepository<AppDbContext, Group>, IGroupRepository
{
public GroupRepository(AppDbContext dbContext) : base(dbContext)
{
}
public List<GroupPermission> GetInheritedGroupPermissions(int groupId)
{
// Removed for brevity
}
}
public class EntityRepository<TDbContext, TEntity> : EntityRepository<TDbContext, TEntity, int>, IEntityRepository<TDbContext, TEntity>
where TDbContext : DbContext
where TEntity : class, IEntity<int>
{
public EntityRepository(TDbContext dbContext) : base(dbContext)
{
}
}
public class EntityRepository<TDbContext, TEntity, TId> : IEntityRepository<TDbContext, TEntity, TId>
where TDbContext : DbContext
where TEntity : class, IEntity<TId>
where TId : IComparable
{
public EntityRepository(TDbContext context)
{
Context = context;
}
public TDbContext Context { get; }
}
And last but not least, the AppDbContext and SqlDbContext:
public class AppDbContext : Shared.DbContexts.SqlDbContext
{
public virtual DbSet<GroupUser> GroupUser { get; set; }
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
}
public class SqlDbContext : DbContext
{
public SqlDbContext(DbContextOptions options) : base(options)
{
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
ChangeTracker.StateChanged += ChangeTracker_StateChanged;
}
}
Error
The error that I am getting is inside the SqlDbContext on the 1st line inside the constructor and says the following:
System.InvalidOperationException: 'No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext.'
What am I doing wrong?
When you mock an implementation it creates the object using the constructor matching the parameters provided; it runs that code.
Additionally, anything not able to be mocked (not virtual or abstract) will run as is. In this case, you're passing in DbContextOptions and you haven't specified a provider, and something needs that.
This can be an opinionated topic, however to solve your problem there are a number of ways you could do it:
Add a parameterless constructor to your DbContext for testing. I wouldn't recommend this as I follow the mantra of not changing your SUT for a test.
Use an in-memory provider; EF Core In-Memory Database Provider or SQLite EF Core Database Provider are two that I have used. They do have limitations but for the OP usage would probably be fine and addresses Microsofts notes about how you shouldn't mock the DbContext.
Use an existing library such as EntityFrameworkCore.Testing (disclaimer, I am the author) which will extend in-memory providers to address their limitations.
I want to create an abstraction layer between Entity Framework and the rest of my application. But I am having a few problems with Entity Framework.
Basically (I don't show you all the interface layers that I've created too), I've split my application into several projects like this :
Domain
Contains my domain object, an abstraction of my datastorage object
DAL
Creates a link between my datastorage and my business layer. Contains two types of elements :
Private ones : my EDMX, my database object, and some other generated objects providing me some useful methods like ToDomain/ToEntity
Public ones : my Data Access Object, providing CRUD methods
Business
Contains the logic of my application. Only knows about the public elements of the DAL and the Domain Layer.
Presentation
Presents the domain objects for the user. Only knows about the business layer.
As I said, I want to create an abstraction of my datastorage objects (in my case Database object, but I want a solution that works also for file or WCF storage for example) so that my business layer don't know anything about my DAL implementation.
Here is a glimpse of what I've done in my DAL :
public abstract class GenericDao<TEntity, TDomain, TDbContext> : IGenericDao<TDomain>
where TDbContext : DbContext, new()
where TEntity : class
where TDomain : class
{
protected TDbContext _context;
protected DbSet<TEntity> _dbSet;
public GenericDao(TDbContext dbContext)
{
this._context = dbContext;
this._dbSet = dbContext.Set<TEntity>();
}
public TDomain Create()
{
return this.ToDomain(this._dbSet.Create());
}
public IList<TDomain> GetAll()
{
return this._dbSet.ToList().Select(entity => this.ToDomain(entity)).ToList();
}
public void Update(TDomain domain)
{
var entity = this.ToEntity(domain);
var entry = this._context.Entry(entity);
entry.State = EntityState.Modified;
}
public void Remove(TDomain domain)
{
_dbSet.Remove(this.ToEntity(domain));
}
protected abstract TDomain ToDomain(TEntity entity);
protected abstract TEntity ToEntity(TDomain domain);
}
You will probably see what's wrong with my code by reading it: when I try to delete or update an entity, I am not manipulating an entity attached to Entity Framework. If I try to attach my entity to the dbContext, it fails because there is already an entity in the context with the same id.
I already thought about several solutions, but none of them please me.
Maybe am I doing something wrong in my approach? I am a little bit confused about the Repository and DAO pattern (I read anything and the very opposite about that difference on the internet).
You have two options:
initialize new dbcontext for each operation and dispose it when operation is ended:
public abstract class GenericDao<TEntity, TDomain, TDbContext> : IGenericDao<TDomain>
where TDbContext : DbContext, new()
where TEntity : class
where TDomain : class
{
protected Func<TDbContext> _contextFactory;
public GenericDao(Func<TDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public TDomain Create()
{
using(var context = _contextFactory())
{
return context.Set<TEntity>().Create();
}
}
public IList<TDomain> GetAll()
{
using(var context = _contextFactory())
{
return context.Set<TEntity>().ToList()
.Select(entity => this.ToDomain(entity)).ToList();
}
}
public void Update(TDomain domain)
{
using(var context = _contextFactory())
{
var entity = this.ToEntity(domain);
context.Attach(entity);
var entry = this._context.Entry(entity);
entry.State = EntityState.Modified;
context.SaveChanges();
}
}
public void Remove(TDomain domain)
{
using(var context = _contextFactory())
{
var entity = this.ToEntity(domain);
context.Attach(entity);
context.Set<TEntity>.Remove(entity);
context.SaveChanges();
}
}
protected abstract TDomain ToDomain(TEntity entity);
protected abstract TEntity ToEntity(TDomain domain);
}
or you can try to find entity in your instance of dbcontext using property Local of DbSet:
var contextEntity = context.Set<TEntity>().Local
.Where(c=>c.Id == entity.Id).FirstOrDefault();
You seem to be getting stuck coding to an implementation within your abstraction. If you injected an interface to your generic rather than a concrete type (like EF) then your GenericDao becomes much more flexible. You can inject whatever implementation you choose providing it implements the required interface. In your case, WCF, File, Whatever. For example;
protected IDbContext _context;
public GenericDao(IDbContext dbContext)
{
this._context = dbContext;
}
public void Remove(TDomain domain)
{
_context.Remove(this.ToEntity(domain));
}
//abstraction
public interface IDbContext
{
void Remove(Entity entity);
}
//EF Implementation
public MyEfClass : IDbContext
{
public void Remove(Entity entity)
{
//code to remove for EF example
context.Attach(entity);
context.State = EntityState.Modified;
context.Set<TEntity>.Remove(entity);
context.SaveChanges();
}
}
//WCF Implementation
public MyWCFClass : IDbContext
{
public void Remove(Entity entity)
{
//Wcf implementation here
}
}
//File example
public FileWriter : IDbContext
{
public void Remove(Entity entity)
{
LoadFile();
FindEntry(entity);
WriteFile(entity);
SaveFile();
}
public void LoadFile()
{
//use app settings for file directory
}
}
I am using a generic repository pattern Repository<TEntity> where repositories access the entities through a context. Then I have a service layer that accepts a context in the constructor. Now I can have multiple repositories in a service, accessing the entities through the same context. Pretty standard. This is perfect for tables/views that map to entities, but I cannot unit test data coming through stored procedures.
This is my current setup:
IDbContext:
public interface IDbContext : IDisposable
{
IDbSet<T> Set<T>() where T : class;
DbEntityEntry<T> Entry<T>(T entity) where T : class;
void SetModified(object entity);
int SaveChanges();
// Added to be able to execute stored procedures
System.Data.Entity.Database Database { get; }
}
Context:
public class AppDataContext : DbContext, IDbContext
{
public AppDataContext()
: base("Name=CONNECTIONSTRING")
{
base.Configuration.ProxyCreationEnabled = false;
}
public new IDbSet<T> Set<T>() where T : class
{
return base.Set<T>();
}
public void SetModified(object entity)
{
Entry(entity).State = EntityState.Modified;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new BookingMap());
}
// Added to be able to execute stored procedures
System.Data.Entity.Database Database { get { return base.Database; } }
}
Generic Repository:
public class Repository<T> : IRepository<T> where T : class
{
private readonly IDbContext context;
public Repository(IDbContext context)
{
this.context = context;
}
public IQueryable<T> GetAll()
{
return this.context.Set<T>().AsQueryable();
}
public void Add(T entity)
{
this.context.Set<T>().Add(entity);
}
public void Delete(T entity)
{
this.context.Set<T>().Remove(entity);
}
public void DeleteAll(IEnumerable<T> entities)
{
foreach (var e in entities.ToList())
{
this.context.Set<T>().Remove(e);
}
}
public void Update(T entity)
{
this.context.Set<T>().Attach(entity);
this.context.SetModified(entity);
}
public void SaveChanges()
{
this.context.SaveChanges();
}
public void Dispose()
{
if (this.context != null)
{
this.context.Dispose();
}
}
}
Service:
public class BookingService
{
IDbContext _context;
IRepository<Booking> _bookingRepository;
public BookingService(IDbContext context)
{
_context = context;
_bookingRepository = new Repository<Booking>(context);
}
public IEnumerable<Booking> GetAllBookingsForName(string name)
{
return (from b in _bookingRepository.GetAll()
where b.Name == name
select b);
}
}
Test:
[TestClass]
public class BookingServiceTest
{
[TestMethod]
public void Test_Get_All_Bookings_For_Name()
{
var mock = new Mock<IDbContext>();
mock.Setup(x => x.Set<Booking>())
.Returns(new FakeDbSet<Booking>
{
new Booking { Name = "Foo" },
new Booking { Name = "Bar" }
});
BookingService _bookingService = new BookingService(mock.Object);
var bookings = _bookingService.GetAllBookingsForName(name);
Assert.AreEqual(2, bookings.Count(), "Booking count is not correct");
}
}
This is perfect for tables/views that map to entities, but I cannot unit test data coming through stored procedures.
I looked up on the internet and found DbContext.Database property and I am able to execute stored procedures with the .SqlQuery() function and map them to an entity type.
This is what I added to the Repository<T> class:
public IEnumerable<T> SqlQuery(string storedProc, params object[] paramList)
{
return this.context.Database.SqlQuery<T>(storedProc, paramList);
}
And call the .SqlQuery() function in my service class:
public IEnumerable<Booking> GetAllBookings(string name)
{
return _bookingRepository.SqlQuery("EXEC GetAllBookings #name = {0}", name);
}
This works great (I am able to get some data), but my question is how can I mock and unit test this?
I just encountered a need to do this, and my googling led me to this question. I didn't like the answer from Sriram Sakthivel, I didn't want to have to introduce yet another abstraction when I already had one in place:
I already had an interface which I had extracted from my DbContext, and implemented in a test double.
I simply added int ExecuteSqlCommand(string sql, params object[] parameters) to my interface, and in the actual context I implemented it like this:
public int ExecuteSqlCommand(string sql, params object[] parameters)
{
return Database.ExecuteSqlCommand(sql, parameters);
}
Which obviously just delegates to the actual EF Database property to do the work.
And in my test double I implemented it like this:
public int ExecuteSqlCommand(string sql, params object[] parameters)
{
return 0;
}
Which doesn't really do anything, which is the point: You aren't unit testing the actual stored procedure, you just need a way to get it to return something useful.
I imagine at some point I 'might' need it to return something other than 0 in a unit test, at which point I'll probably introduce something like a Func<int> executeSqlCommandResultFactory to test double constructor so that I can control it, but at the moment YAGNI applies.
You can abstract away the Database property with some interface say IDatabase with SqlQuery method.
interface IDatabase
{
public IEnumerable<T> SqlQuery<T>(string sql, params Object[] parameters);
}
class DatabaseWrapper : IDatabase
{
private readonly Database database;
public DatabaseWrapper(Database database)
{
this.database = database;
}
public IEnumerable<T> SqlQuery<T>(string sql, params Object[] parameters)
{
return database.SqlQuery<T>(storedProc, paramList);
}
}
Modify your IDbContext interface to use IDatabase instead of concrete instance so that we can mock it.
public interface IDbContext : IDisposable
{
...
// Added to be able to execute stored procedures
IDatabase Database { get; }
}
and your implementation this way
public class AppDataContext : DbContext, IDbContext
{
private readonly IDatabase database;
public AppDataContext()
: base("Name=CONNECTIONSTRING")
{
base.Configuration.ProxyCreationEnabled = false;
this.database = new DatabaseWrapper(base.Database);
}
...
// Added to be able to execute stored procedures
IDatabase Database { get { return database; } }
}
At this point I believe you know how to mock the IDatabase to return the test data.
I realize this is an old question, but to anyone having a similar issue, here's my take.
Why not just use AutoFixture to create an object of the data you usually return from the stored procedure and mock your repository to return it?
public class FooBar
{
private Fixture fixture;
private Mock<BookingRepository> bookingRepository;
public FooBar()
{
fixture = new Fixture();
bookingRepository= new Mock<BookingRepository>();
}
public void TestInitialize()
{
var sprocObject = fixture.Create<DbObject>();
bookingRepository.Setup(x => x.GetAllBookings(It.IsAny<string>())).Returns(sprocObject);
}
// Unit tests
}
As Gert Arnold said, you should do integration tests if you want to test the actual stored procedure. If you're testing the service/repository logic you just need to return some data.
I am still struggling to make good data access layer for asp mvc application and as always something is missing :)
I have created separate assembly for DAL and I am using repository pattern and ninject for IOC.
Problem is that now I don't know how to write custom methods (methods out of generic CRUD methods).
This is implementation:
Context class:
public class MainContext : IdentityDbContext<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>, IMainContext
{
public MainContext()
: base("DefaultConnection")
{
}
...
public DbSet<Country> Countries { get; set; }
...
public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
{
return base.Set<TEntity>();
}
Repository:
public interface ICountryRepository : IGenericRepository<Country>...
Generic Repository:
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
private readonly IMainContext _context;
private readonly IDbSet<TEntity> _dbSet;
public GenericRepository(IMainContext context)
{
this._context = context;
this._dbSet = context.Set<TEntity>();
}
...
Generic Repository Interface:
public interface IGenericRepository<TEntity> where TEntity : class
{
IEnumerable<TEntity> Get(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "");
TEntity GetByID(object id);
void Insert(TEntity entity);
void Delete(object id);
void Delete(TEntity entityToDelete);
void Update(TEntity entityToUpdate);
}
And this is try to add custom method in repository class:
public virtual IEnumerable<Country> GetByLocation(float location)
{
var data = from c in _context...
return data;
}
But I don't have a context.
I don't know how to implement getting data now.
Should I inject it somehow or make instance by new keyword (but I guess this is wrong)
How to implement custom method now?
Your implementation of ICountryRepository should inherit the GenericRepository. The GenericRepository has a reference to the db context which you can use for your custom queries. For your Generic Repository constructor is will be much easier to take MainContext instead of an IMainContext which is ok if you keep injecting it down through you layers. With Ninject you will want to bind your MainContext like this:
kernel.Bind<MainContext>().ToSelf().InRequestScope();
and the rest of your interfaces to the concrete implementation. So each of your repositories will have the context through the GenericRepository which has a reference to your db context which you can make custom queries off of in the repository. If you had a service layer, inject the interface of the repository:
private readonly ICountryRepository _repository;
public SomeServie(ICountryRepository repository){
_repository = repository;
}
public void DoSomething(float locationId){
_repository.GetByLocation(locationId);
}
HERE IS THE OTHER CODE YOU NEED:
public class CountryRepository : GenericRepository<Country>, ICountryRepository
{
public CountryRepository(MainContext mainContext) : base(mainContext) { }
public IEnumerable<Country> GetByLocation(float location)
{
return this.Context.Countries.ToList();
}
.....
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
public MainContext Context { get; set; }
public IDbSet<TEntity> DbSet { get; set; }
public GenericRepository(MainContext context)
{
Context = context;
DbSet = context.Set<TEntity>();
}
.......
Also, your GetCountries does not need to be virtual.
The main changes here are that you need to pass the context through the country repository constructor to base, and the context and countries db set need to be public, even when inherited. See this article: Are private members inherited in C#?