Entity Framework Core Repository Pattern Eager Load - c#

I am trying to build out a web application using a repository pattern. In doing so, I have noticed that my navigation properties are not loading. I was able to get my properties to load using EF Proxies, but when I went to use my DTOs, it really didn't like that. I have since removed all the Proxy bit of code and decided that I just need to provide a way to Eager Load the data I care about.
In my repository, I have an interface similar to this. Using DBSet, I am able to use the Find method to generically find the value and return it from the repo. Doing this, I am not able to pass in Includes to resolve the navigation properties.
public async Task<T> GetById(int Id)
{
var query = await this.Context.Set<T>().FindAsync(Id);
// Left split like this for clarity
return query;
}
The only way I got this working was by removing the generic piece and adding a new method called GetUserById into the UserRepository.
public class UserRepository : RepositoryBase<User>, IUserRepository
{
private readonly Context _context;
public UserRepository(Context context) : base(context) {
_context = context;
}
public User GetUserById(int Id)
{
var query = _context.Users.Include("Department").Where(x => x.Id == Id).FirstOrDefault();
return query;
}
}
Please tell me there is a better way.

Try this GetUserById implementation:
public User GetUserById(int Id, params Expression<Func<User, object>>[] includes)
{
var query = _context.Users.Where(x => x.Id == Id);
foreach (var include in includes)
{
query.Include(include);
}
return query.FirstOrDefault();
}
And this is how you would call it:
var repo = new UserRepository();
var user = repo.GetUserById(100, u => u.Department);

Related

C# Entity Framework Moq exception

I have IDataService that contains generic crud operations
public interface IDataService<T>
{
Task<IEnumerable<T>> GetAll();
Task<IEnumerable<T>> GetAll(string[] includes = null);
Task<T> GetById(int id);
Task<T> Create(T entity);
Task<T> Update(int id, T entity);
Task<bool> Delete(int id);
}
and I have class GenericDataService<T> that implements the IDataService interface:
public class GenericDataService<T> : IDataService<T> where T : DomainObject
{
private readonly DeployToolDBContexFactory _contexFactory;
public GenericDataService(DeployToolDBContexFactory contexFactory)
{
_contexFactory = contexFactory;
}
public async Task<T> Create(T entity)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
EntityEntry<T> createdResult = await contex.Set<T>().AddAsync(entity);
await contex.SaveChangesAsync();
return createdResult.Entity;
}
}
public async Task<bool> Delete(int id)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
T entity = await contex.Set<T>().FirstOrDefaultAsync((e) => e.Id == id);
contex.Set<T>().Remove(entity);
await contex.SaveChangesAsync();
return true;
}
}
public async Task<IEnumerable<T>> GetAll()
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
IEnumerable<T> entities = await contex.Set<T>().ToListAsync();
return entities;
}
}
public async Task<T> GetById(int id)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
T entity = await contex.Set<T>().FirstOrDefaultAsync((e) => e.Id == id);
return entity;
}
}
public async Task<T> Update(int id, T entity)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
entity.Id = id;
contex.Set<T>().Update(entity);
await contex.SaveChangesAsync();
return entity;
}
}
public async Task<IEnumerable<T>> GetAll(string[] includes = null)
{
using (DeployToolDBContex contex = _contexFactory.CreateDbContext())
{
var query = contex.Set<T>().AsQueryable();
foreach (var include in includes)
query = query.Include(include);
return query.ToList();
}
}
}
To create objects I'm using a data store class that executes operations with the DataService object:
public class DataStore
{
private static DataStore instance = null;
public static DataStore Instance
{
get
{
instance = instance == null ? new DataStore() : instance;
return instance;
}
}
public IDataService<User> userDataService;
public DataStore()
{
this.userDataService = new GenericDataService<User>(new DeployToolDBContexFactory());
}
}
For example, user creation :
private async void AddUser()
{
User user = new User()
{
UserName = UserNameTxt,
RoleId = RoleSelected.Id
};
await DataStore.Instance.userDataService.Create(user);
}
I'm new to Moq and I want to write unit tests, for example, test of user creation
[Test]
public async Task CreateUser()
{
Mock<DeployToolDBContexFactory> dbContextFactory = new Mock<DeployToolDBContexFactory>();
Mock<DeployToolDBContex> dbContextMock = new Mock<DeployToolDBContex>(dbContextFactory.Object);
var user = new User()
{
UserName = "testName"
};
var mock = new Mock<GenericDataService<User>>(new DeployToolDBContexFactory());
mock.Setup(m => m.Create(user)).ReturnsAsync(new User() { UserName = user.UserName}).Verifiable();
var service = new GenericDataService<User>(dbContextFactory.Object);
User u = await service.Create(user);
Assert.AreEqual(u.UserName , user.UserName);
}
I'm getting this error :
System.NotSupportedException : Unsupported expression: m => m.Create(user)
Non-overridable members (here: GenericDataService.Create) may not be used in setup / verification expressions.
I tried to set DbSet as virtual.
Thanks for any help.
It looks like you are a bit mixed up with mocking and what exactly you should be testing. Your GenericDataService is essentially a Generic Repository pattern. This is actually an anti-pattern for EF, but there is plenty to read up on why you shouldn't use it with EF... Repository patterns in general are a good thing for facilitating unit testing, however this is because they serve as a boundary to avoid needing to try and mock a DbContext and its DbSets.
Firstly, your Test is testing the wrong thing. The test should be testing whatever method that will be calling your AddUser method. Whether this is in a Controller or a Service, etc. That controller would have a dependency of IDataService<User> declared which we would be mocking in order to test the controller:
For the sake of argument I've made AddUser() a public method. In your case you should have a public action or method that calls AddUser and would set up a test for that method. You should also structure your code to avoid being dependent on module-level state. For instance an AddUser method should not be reliant on private/protected state, it should ultimately be getting parameters to do things like perform actions or modify state. (kept to a minimum)
So let's assume we want to test a method that should call the DataService Create method and Create is expected to have added the item to the DBContext and assigned an ID. The purpose of the unit test is not to assert what EF actually does, but rather what the code under test should do with the results:
[Test]
public void EnsureAddUserCreatesUser()
{
const string userName = "New User";
const int roleId = 4;
const int UserId = 101;
var mockUserDataService = new Mock<IDataService<User>>();
mockUserDataService.Setup(m => m.Create(It.IsAny<User>())).Callback(m => {m.UserId = userId;});
var testController = new UserController(mockUserDataService.Object);
var user = await testController.AddUser(userName, roleId);
Assert.IsNotNull(user);
Assert.AreEqual(userId, user.UserId);
Assert.AreEqual(userName, user.UserName);
Assert.AreEqual(roleId, user.RoleId);
mockUserDataService.Verify(m => m.Create(It.IsAny<User>()), Times.Once);
}
What a test like this does is set up a mock of our repository/data service telling it to expect its input parameter. Since our AddUser method will be creating a new user based on some values, we tell it to expect It.IsAny<User>() which says "expect a user". From there we can have the mock perform some basic actions as if the DbContext added our user successfully such as populating a PK. This example populates the PK using the Callback method which accepts whatever User was passed in then sets our known PK. The value itself does not matter since we aren't actually inserting data, we just want a known value to get back that we can assert that the mock was actually called. Normally the AddUser might also expect to check the resulting data and return something like a success state with an ID. In this example I had the AddUser return the User. Other tests you might want to assert the behaviour if the AddUser attempts to add a duplicate user. In these cases the Moq might Throw an exception or otherwise return a different result.
From there we have returned the User, so we just assert the values are what were expected, including the PK, and that our mocked method was actually called.
Ultimately when it comes to unit testing, the key points are:
Your DataService/Repository serves as the boundary for the tests. (What you mock)
Your tests test business logic above this boundary.
Your tests mock the boundary, capturing all expected calls to that boundary, and either return known state, take action on passed in state, or throw exceptions based on what behaviour you want to test.
Your tests can then assert the mocks to verify that methods that were expected to be called were called, and any methods that were not expected to be called were not called. (Times.None)

Hot Chocolate (GraphQL) interceptor/middleware to get IQueryable before data fetch

I need to do something extra with the IQueryable generated, but im not able to create an interceptor in order to get the IQueryable (e.g log the query created by the GraphQL request).
Im still diving into the great material that is Hot chocolate, but for starters i have this:
Straight forward right? But now want an interceptor(or something like that) that gives me the rest of the generated IQueryable before the result to the body response.
Thank you,
You can do this by using a middleware.
public class Query
{
[UseYourCustom]
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<Person> GetPersons() => //...
}
public class UseYourCustomAttribute : ObjectFieldDescriptorAttribute
{
public override void OnConfigure(
IDescriptorContext context,
IObjectFieldDescriptor descriptor,
MemberInfo member)
{
descriptor.Use(next => async context =>
{
// before the resolver pipeline
await next(context);
// after the resolver pipeline
if (context.Result is IQueryable<Person> query)
{
// all middleware are applied to `query`
}
});
}
}
In Version 12 you will also be able to do the following:
public class Query
{
[UseProjection]
[UseFiltering]
[UseSorting]
public IQueryable<Person> GetPersons(IResolverContext context)
{
IQueryable<Person> person = //...
var allMiddlewareApplied = persons
.Sort(context)
.Filter(context)
.Project(context);
return allMiddlewareApplied
}
}

Automatically Infer DbContext Type

I've created a base service which runs CRUD commands on a database using EF Core that I want other services to inherit. I'm trying to figure out if there is a way to infer the type being queried by the DbContext based on the types being passed to the service.
public class DbServiceBase<TDatabaseModel, TBusinessDisplayModel>
{
private readonly DbContext _context;
private readonly Mapper _mapper;
public DbServiceBase(DbContext context)
{
_context = context;
_mapper = new Mapper(MapperConfigurationGenerator.Invoke());
}
public async Task<List<TBusinessDisplayModel>> GetAll()
{
var dbResults = await _context.<TDatabaseModel>
.ToListAsync()
.ConfigureAwait(false);
return _mapper.Map<List<TBusinessDisplayModel>>(dbResults);
}
public async Task<TBusinessDisplayModel> GetById(long id)
{
var dbResult = await _context.<TDbModel>
.SingleAsync(x => x.Id == id)
.ConfigureAwait(false);
return _mapper.Map<TBusinessDisplayModel>(dbResult);
}
}
DbContext has a method called Set, that you can use to get a non-generic DbSet, such as:
dbSet = this.Set<SomeType>();
You should save this dbset in a private readonly field of you service and use this to query data.
The properties on the database context that are used to access the models in the database are usually implemented like this:
public DbSet<Post> Posts { get; set; }
EF will automatically populate those properties for your with database set objects for those entity types but you can also just create a database set from the context directly by calling DbContext.Set<T>. Since that method is already generic, you can use it to create the database set for the type you need in your implementation:
public async Task<List<TBusinessDisplayModel>> GetAll()
{
var dbResults = await _context.Set<TDatabaseModel>()
.ToListAsync()
.ConfigureAwait(false);
return _mapper.Map<List<TBusinessDisplayModel>>(dbResults);
}

Login process within Onion Architecture

I want to implement a login process within the Onion Architecture. I can't get my head around how to go about it correctly. Below is my Repository class that will talk to the database. How would I go about checking that an email had not already been entered into the table. It seems the only parameters I can pass in are Model objects or entities from my BaseClass. My Base class contains only a string for Id.
public class RentalsRepository<T> : IRentalsRepository<T> where T : BaseClass
{
private readonly RentalsDBContext _Context;
private DbSet<T> entities;
string errorMessage = string.Empty;
public RentalsRepository(RentalsDBContext _Context)
{
this._Context = _Context;
entities = _Context.Set<T>();
}
public T Get(string Id)
{
return entities.SingleOrDefault(e => e.Id == Id);
}
Currently all I can think of is returning all the user entries and then searching the list but I imagine this is not very efficient. Thanks!
Basically you first of all would extend your Repository with the mentioned GetByPredicatemethod that is basically only a Wrapper for SingleOrDefaultor FirstOrDefault (or other LINQ methods that take a lambda expression / predicate). Your repo will then look similar to this:
public class RentalsRepository<T> : IRentalsRepository<T> where T : BaseClass
{
private readonly RentalsDBContext _Context;
private DbSet<T> entities;
string errorMessage = string.Empty;
public RentalsRepository(RentalsDBContext _Context)
{
this._Context = _Context;
entities = _Context.Set<T>();
}
public T Get(string Id)
{
return entities.SingleOrDefault(e => e.Id == Id);
}
public T GetByPredicate(Func<T, bool> predicate)
{
return entities.FirstOrDefault(predicate);
}
}
In your Businesslogic you would then call this method like so:
public void PerformLogin(string username, string hashedPassword)
{
var user = _repository.GetByPredicate(x => x.Username == username);
if(user != null)
{
if(user.HashedPassword == hashedPassword)
{
// Login succeeded do all you need to set usersession here
return;
}
}
// If we´ve come this far either there is no user or password was incorrect. We need to inform the user that login hasn´t succeeded
throw new LoginFailedException("Login failed. Username does not exist or password is incorrect.);
}
Basically as you can call this GetByPredicateanyway you want. Please consider that each call to GetByPredicatwill result in a SQL expression so do not use to complex conditions. Only use simple conditions like the one I´ve been showing above.

Join multiple tables when using a generic repository in C#

I have a .Net Solution with multiple projects for the layers which are as follows.
Business (solution folder)
Core (project)
Interfaces (project)
Data (solution folder)
Data (project)
Presentation (solution folder)
AdminPanel (project)
Shared (solution folder)
Common (project)
I have implemented the generic repository pattern with IoC layer using Unity i.e. Microsoft.Practices.Unity. Everything works great except when I want to do multi table simple or complex joins. I have tried many different ways. I have gone through every single existing thread here on SO regarding joins in repository but none of them are helpful in what I need.
Here's my repository class.
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class, IEntity
{
protected BuyatimeshareModel Context { get; private set; }
public Repository(IContextFactory contextFactory)
: this(contextFactory.Get())
{
}
protected Repository(BuyatimeshareModel context)
{
context.Database.Log = message => { Common.Logging.Log(message); };
Context = context;
}
IDbSet<TEntity> DbSet
{
get
{
return Context.Set<TEntity>();
}
}
public TEntity Add(TEntity instance)
{
DbSet.Add(instance);
Context.SaveChanges();
return instance;
}
public void Remove(TEntity instance)
{
DbSet.Remove(instance);
Context.SaveChanges();
}
public TEntity FindOne(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.AsQueryable().FirstOrDefault(predicate);
}
public IEnumerable<TEntity> All()
{
return DbSet.AsQueryable();
}
public IEnumerable<TEntity> Query() { IQueryable<TEntity> query = DbSet; return query.ToList(); }
public IEnumerable<TEntity> FindAll(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.AsQueryable().Where(predicate);
}
public int Count()
{
return DbSet.AsQueryable().Count();
}
public int Count(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.AsQueryable().Count(predicate);
}
public bool Exists(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.AsQueryable().Any(predicate);
}
}
Here's my IoC layer.
public class UnityControllerFactory : DefaultControllerFactory
{
IUnityContainer container;
public UnityControllerFactory(IUnityContainer container)
{
this.container = container;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
try
{
if (controllerType == null)
throw new ArgumentNullException("controllerType");
if (!typeof(IController).IsAssignableFrom(controllerType))
throw new ArgumentException(string.Format(
"Type requested is not a controller: {0}", controllerType.Name),
"controllerType");
return container.Resolve(controllerType) as IController;
}
catch { return null; }
}
public static void Configure()
{
IUnityContainer container = new UnityContainer();
/*string connectionString = ConfigurationManager.ConnectionStrings["Connection"].ConnectionString;*/
container.RegisterType<IContextFactory, ContextFactory>(new ContainerControlledLifetimeManager())//, new InjectionConstructor(connectionString))
.RegisterType<IUnitOfWork, UnitOfWork>(new ContainerControlledLifetimeManager())
.RegisterType<IAdminService, AdminService>()
.RegisterType(typeof(IRepository<>), typeof(Repository<>));
ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory(container));
}
}
And finally here's my service layer where I'm trying to query some data from the repository layer.
public class AdminService : IAdminService
{
private readonly IRepository<press_releases> pressReleasesRepo;
private readonly IRepository<tblads> adsRepo;
private readonly IRepository<tblresorts> resortsRepo;
public AdminService(IRepository<press_releases> _pressReleasesRepo, IRepository<tblads> _adsRepo, IRepository<tblresorts> _resortsRepo)
{
pressReleasesRepo = _pressReleasesRepo;
adsRepo = _adsRepo;
resortsRepo = _resortsRepo;
}
public List<press_releases> Test()
{
var data = pressReleasesRepo.FindAll(p => p.pr_id > 0);
var data1 =
(from a in adsRepo.Query()
join r in resortsRepo.Query() on a.resort_id equals r.resort_id
where a.ad_id == 413
select new
{
OwnerId = a.owner_id,
ResortName = r.name,
AdId = a.ad_id,
AskingPrice = a.askingPriceInt
}).ToList();
var model = data.ToList();
return model;
}
}
Now here we have two queries. data and data1
data is simply querying one single table with a criteria and its returning results as expected. All good here.
But, in data1, it is also technically returning exactly the end result that I want BUT I also have a raw SQL logger in place to see what's happening behind the scenes and what it's doing is to query both ads table and resorts table separately using a select * equivalent statement and then when the results return from both tables, it's applying the joins within memory and returning the result after filtering through the where clause. So basically its scanning through around 100000 rows even though I have join and where clause in place and in the end, it returns one single row with ad id 413.
One thing to note down is that I'm calling Query method of the repository class while doing the join for data1 which is returning an IEnumerable. I was not able to change it to IQueryable because then an exception was being thrown having some message like the linq query has references to different contexts.
Can anyone guide me to modify this code so that I can apply real sql joins between the repositories or if there's a way to modify this code by adding a middle layer to act as a bridge. I need something to go forward from this point. I've been looking everywhere but no avail. I'm sure that I'm not the first one ever in this world who could have this problem. There definitely would be others having similar issue and may be someone who have found a real way.
My tools and technologies are as follows.
Visual Studio 2013
.Net framework 4.0
MySQL Database
MySQL for Visual Studio 1.2.4
MySQL Connector 6.9.6
Entity Framework 6
Windows 10 Pro x64
Please let me know if there's anything that I might have missed.
Any help would be greatly appreciated.
Edit:
Here's the ContextFactory class
public class ContextFactory : IContextFactory
{
private bool _isDisposed;
private SomeModel _context;
public ContextFactory()
{ }
public SomeModel Get()
{
_context = new SomeModel();
return _context;
}
~ContextFactory()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed && disposing && (_context != null))
_context.Dispose();
_isDisposed = true;
}
}
Here is the method you have in your repository which you are using in your data1.
public IEnumerable<TEntity> Query() { IQueryable<TEntity> query = DbSet; return query.ToList(); }
Whenever you hit '.ToList()' it will hit the database.
If you want to create the query first and then execute then use 'DbSet' property of your repository.

Categories

Resources