Lazy Initialization - Invalid arguments - c#

I am coding a MVC 5 internet application, and am having some trouble with initializing a Lazy object.
Here is my code:
public Lazy<IGenericRepository<Account>> accounts;
public IGenericRepository<Account> accountsNotLazy;
I am wanting to initialize these two variables in the constructor call to the class.
Here is the constructor code;
public GenericMultipleRepository(DbContext dbContext)
{
this.dbContext = dbContext;
accounts = new Lazy<GenericRepository<Account>>(dbContext);
accountsNotLazy = new GenericRepository<Account>(dbContext);
}
Can I please have some help with this code?
Thanks in advance.
EDIT
I am wanting the exact same initialization as the accountsNotLazy variable, but using Lazy loading. The accountsNotLazy variable is initializing correctly, how come the accounts is not? The only difference is the Lazy keyword.
These are the errors:
The best overloaded method match for
'System.Lazy>.Lazy(System.Func>)'
has some invalid arguments
As well as:
cannot convert from 'System.Data.Entity.DbContext' to
'System.Func>'
Here is the GenericRepository class:
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
{
protected DbSet<TEntity> DbSet;
private readonly DbContext _dbContext;
public GenericRepository(DbContext dbContext)
{
_dbContext = dbContext;
DbSet = _dbContext.Set<TEntity>();
}
public GenericRepository()
{
}
public IQueryable<TEntity> GetAll()
{
return DbSet;
}
public async Task<TEntity> GetByIdAsync(int id)
{
return await DbSet.FindAsync(id);
}
public IQueryable<TEntity> SearchFor(Expression<Func<TEntity, bool>> predicate)
{
return DbSet.Where(predicate);
}
public async Task EditAsync(TEntity entity)
{
_dbContext.Entry(entity).State = EntityState.Modified;
await _dbContext.SaveChangesAsync();
}
public async Task InsertAsync(TEntity entity)
{
DbSet.Add(entity);
await _dbContext.SaveChangesAsync();
}
public async Task DeleteAsync(TEntity entity)
{
DbSet.Remove(entity);
await _dbContext.SaveChangesAsync();
}
}

As many have mentioned that you need to pass a Func<T>, but the expression suggested as the answer is incorrect.
Initialize your accounts variable as follows -
accounts = new Lazy<IGenericRepository<Account>>(() => new GenericRepository<Account>(dbContext));
Do note that when you want to access your instance of GenericRepository lazily, you would need to access Value property of Lazy class.. like so - accounts.Value, which will be of type GenericRepository<Account>

The constructor for Lazy<T> requires a parameter of type Func<T>. You may want to try this instead:
accounts = new Lazy<GenericRepository<Account>>(() => { return dbContext; });

The constructor of the Lazy<T> class is
Lazy<T>(Func<T>)
You can change your GenericMultipleRepository constructor to as follow:
public GenericMultipleRepository(DbContext dbContext)
{
this.dbContext = dbContext;
accounts = new Lazy<GenericRepository<Account>>(() => { return dbContext; });
accountsNotLazy = new GenericRepository<Account>(dbContext);
}

Related

Implementing repositories with EF Core without creating multiples methods

I've been using EF core in my project for years without repositories layer and now I decided to implement repositories pattern for one of my projects which became very big. We have more than 30 entity models and a huge list of API endpoints.
The thing is, each endpoint returns to the client the necessary data from DB formatted by the frontend needs. Some times we want just a list of an entity, other times the same list with some related data and sometimes use some SQL aggregate functions to do some calculations.
We just use the DBContext directly in each endpoint to perform the queries as we need, but when implementing the repositories, we faced an effort obstacle which is coding several methods to get the different data formatted to our needs. Not only basic CRUD and some more operations.
My question is, this really how thing are done (creating as much methods as needed) or is there any best practices to this? Is there some way "rewrite" the DBContext so that I can use expressions and turn it generic avoiding creating so mach methods?
Thank you very much!
Share my actual BaseRepo
public class BaseRepository<TEntity> : IBaseRepository<TEntity> where TEntity : class
{
internal ApplicationDbContext Context;
internal DbSet<TEntity> dbSet;
public BaseRepository(ApplicationDbContext context)
{
this.Context = context;
this.dbSet = context.Set<TEntity>();
}
public virtual async Task AddAsync(TEntity entity)
{
await dbSet.AddAsync(entity);
await SaveAsync();
}
public virtual async Task AddRangeAsync(IEnumerable<TEntity> entities)
{
await dbSet.AddRangeAsync(entities);
await SaveAsync();
}
public virtual async Task<IEnumerable<TEntity>> GetAllAsync()
{
return await dbSet.ToListAsync();
}
public virtual async Task<IEnumerable<TEntity>> GetAsync(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
query = query.Where(filter);
foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
return await orderBy(query).ToListAsync();
else
return await query.ToListAsync();
}
public virtual async Task<TEntity> GetByIdAsync(int? id)
{
return await dbSet.FindAsync(id);
}
public async Task Remove(TEntity entity)
{
dbSet.Remove(entity);
await SaveAsync();
}
public async Task RemoveRange(IEnumerable<TEntity> entities)
{
dbSet.RemoveRange(entities);
await SaveAsync();
}
public virtual async Task<TEntity> SingleOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
{
return await dbSet.SingleOrDefaultAsync(predicate);
}
public virtual async Task Update(TEntity entityToUpdate)
{
dbSet.Attach(entityToUpdate);
Context.Entry(entityToUpdate).State = EntityState.Modified;
await SaveAsync();
}
public virtual async Task UpdateRange(IEnumerable<TEntity> entitiesToUpdate)
{
dbSet.AttachRange(entitiesToUpdate);
Context.Entry(entitiesToUpdate).State = EntityState.Modified;
await SaveAsync();
}
public async Task SaveAsync()
{
await Context.SaveChangesAsync();
}
public virtual async Task AddUpdateOrDeleteRange(IEnumerable<TEntity> entitiesToAddOrUpdate)
{
await Context.BulkInsertOrUpdateOrDeleteAsync<TEntity>(entitiesToAddOrUpdate.ToList(), new BulkConfig { SetOutputIdentity = false });
await SaveAsync();
}
public virtual async Task AddOrUpdateRange(IEnumerable<TEntity> entitiesToAddOrUpdate)
{
await Context.BulkInsertOrUpdateAsync<TEntity>(entitiesToAddOrUpdate.ToList(), new BulkConfig { SetOutputIdentity = false });
await SaveAsync();
}
}
The bulk ones are extensions from EFCore.BulkExtensions;
Unit of Work
public class UnitOfWork : IUnitOfWork, IDisposable, IAsyncDisposable
{
private readonly ApplicationDbContext _context;
private ExampleRepository _exampleRepository;
IDisposable _disposableResource = new MemoryStream();
IAsyncDisposable _asyncDisposableResource = new MemoryStream();
public UnitOfWork(ApplicationDbContext context)
{
_context = context;
}
public IExampleRepository ExampleRepository=> _exampleRepository = _exampleRepository ?? new ExampleRepository(_context);
public async Task<int> CommitAsync()
{
return await _context.SaveChangesAsync();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public async ValueTask DisposeAsync()
{
await DisposeAsyncCore();
Dispose(disposing: false);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
_disposableResource?.Dispose();
(_asyncDisposableResource as IDisposable)?.Dispose();
}
_disposableResource = null;
_asyncDisposableResource = null;
}
protected virtual async ValueTask DisposeAsyncCore()
{
if (_asyncDisposableResource is not null)
{
await _asyncDisposableResource.DisposeAsync().ConfigureAwait(false);
}
if (_disposableResource is IAsyncDisposable disposable)
{
await disposable.DisposeAsync().ConfigureAwait(false);
}
else
{
_disposableResource?.Dispose();
}
_asyncDisposableResource = null;
_disposableResource = null;
}
}
ApplicationDbContext:
public class ApplicationDbContext : DbContext
{
public DbSet<Example> Examples { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
}
Hope it help's you!
Create a base Crud class, something like this:
public class BaseRepository<TEntity> where TEntity : class, new()
Then create a set of standard methods:
public virtual TEntity Get(Expression<Func<TEntity, bool>> predicate)
{
using (var scope = ScopeFactory.CreateScope())
{
var Context = scope.ServiceProvider.GetRequiredService<ubContext>();
var item = Context.Set<TEntity>().FirstOrDefault(predicate);
return item;
}
}
public List<TEntity> GetList(Expression<Func<TEntity, bool>> predicate)
{
using (var scope = ScopeFactory.CreateScope())
{
var Context = scope.ServiceProvider.GetRequiredService<ubContext>();
var item = Context.Set<TEntity>().Where(predicate).AsNoTracking().ToList();
return item;
}
}
public IQueryable GetListQueryable<TContext>(Expression<Func<TEntity, bool>> predicate)
{
using (var scope = ScopeFactory.CreateScope())
{
var Context = scope.ServiceProvider.GetRequiredService<ubContext>();
var item = Context.Set<TEntity>().Where(predicate);
return item;
}
}
You can also do an insert, or update:
public virtual void Update(TEntity input, Expression<Func<TEntity, bool>> predicate)
{
using (var scope = ScopeFactory.CreateScope())
{
var Context = scope.ServiceProvider.GetRequiredService<ubContext>();
if (input == null)
return;
var existing = Context.Set<TEntity>().FirstOrDefault(predicate);
if (existing != null)
{
Context.Entry(existing).CurrentValues.SetValues(input);
Context.SaveChanges();
}
}
}
public virtual void Insert(TEntity input)
{
using var scope = ScopeFactory.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ubContext>();
context.Set<TEntity>().Add(input);
context.SaveChanges();
}
Now, if you need to make something specific in terms of an overload, or some "wonky" handling, you create a class that inherits from this base class.
(But with the specific name of table as the TEntity)
And now you can employ polymorphism to change the behaviour or create new behavior however you like.
Also you may not be required to make a scoped approach to processing the requests, this is just "yoinked" out of an existing code base I already made that did have it as a requirement.
If this doesn't make sense, let me know, and I will give you more code, from the project.
public abstract class DBAccess
{
private readonly DbContext _db;
public DBAccess(DbContext db)
{
_db = db;
}
protected virtual IQueryable<T> Get<T>() where T : class
{
return _db.Set<T>().AsQueryable();
}
}
Then you can use it like this:
private ApplicationUser GetUser(int id)
{
return _dbAccess.Get<ApplicationUser>().Where(w => w.Id == id).FirstOrDefault();
}

Last inserted id from generic repository

My Generic repository class
public class Repository<TEntity, TId> : IRepository<TEntity, TId> where TEntity : class, IEntity<TId>
{
protected readonly CBSContext _context;
//private DbSet<TEntity> _entities;
public Repository(CBSContext context)
{
_context = context ?? throw new ArgumentNullException(nameof(context));
// _entities = _context.Set<TEntity>();
}
public async Task<TEntity> AddAsync(TEntity entity)
{
await Task.Run(() => _context.Add(entity));
return entity;
}
}
Am using ef core 5 Rc.. After insert id is not returning ? Any thing else need to be done..Thanks
EDIT:
After code change to not working..
public async Task<TEntity> AddAsync(TEntity entity)
{
await _context.AddAsync(entity);
return entity;
}
You need to use _context.SaveChangesAsync() after Add(). Then you can get id.

How to handle database transactions in Generic Repository?

I'm working on ASP.NET Core WebAPI with EFCore 3.
How can I handle transactions in Generic Repository approach? I'm aware of Repository patterns pros and cons, as well as its generic repository approach, but something that confuses me is how to handle transactions while using them??
Below is BaseRepository I use. I have a problem implementing transaction like this:
Add Employee
Get created EmployeeId
Add Employee's address with just created EmployeeId
The problem I have is that I must call SaveChanges to get autogenerated Employee's Id, to be able to insert Employee's address. SaveChanges commits the transaction (created by EF Core).
How can I do this in transaction, while using BaseRepository? Should I expose BeginTransaction and Commit methods in BaseRepository, so that Service class can create the transaction and close it?
public abstract class BaseRepository<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
private readonly DemoDb_context _context;
public BaseRepository(DemoDb_context context)
{
_context = context;
}
public async Task<TEntity> GetById(int id)
{
return await _context.Set<TEntity>().FindAsync(id);
}
public async Task<List<TEntity>> GetAll()
{
return await _context.Set<TEntity>().ToListAsync();
}
public async Task<TEntity> Add(TEntity entity)
{
_context.Set<TEntity>().Add(entity);
await _context.SaveChangesAsync();
return entity;
}
public async Task<TEntity> Delete(int id)
{
var entity = await _context.Set<TEntity>().FindAsync(id);
if (entity == null)
{
return entity;
}
_context.Set<TEntity>().Remove(entity);
await _context.SaveChangesAsync();
return entity;
}
public async Task<TEntity> Update(TEntity entity)
{
_context.Entry(entity).State = EntityState.Modified;
await _context.SaveChangesAsync();
return entity;
}
}
This is my service class, where I think, I should add transaction:
public class EmployeeService
{
private readonly IEmployeeRepository _employeeRepository;
private readonly IAddressRepository _addressRepository;
private readonly IMapper _mapper;
public EmployeeService(IEmployeeRepository employeeRepository, IAddressRepository addressRepository, IMapper mapper)
{
_employeeRepository = employeeRepository;
_addressRepository = addressRepository;
_mapper = mapper;
}
public async Task<EmployeeDto> Add(EmployeeDto employeeDto)
{
// TODO: Start transaction here ???
var employee = _mapper.Map<Employee>(employeeDto);
var addedEmployee = await _employeeRepository.Add(employee);
var employeeAddress = Generate_Employee_Address_Entity_With_EmployeeId(addedEmployee);
var addedAddress = await _addressRepository.Add(employeeAddress);
var output = Generate_Employee_Dto(addedEmployee, addedAddress);
// TODO: Commit transaction here ???
return outputs;
}
}
One of the advantages of entity framework is that you can avoid using transactions in base case scenarios like the one you showed us. I would suggest the following:
In BaseRepository remove the SaveChangesAsync from Add, Update and Delete methods and expose a method called SaveDbChangesAsync().
public abstract class BaseRepository<TEntity> : IRepository<TEntity>
where TEntity : class, IEntity
{
private readonly DemoDb_context _context;
public BaseRepository(DemoDb_context context)
{
_context = context;
}
public async Task<TEntity> GetById(int id)
{
return await _context.Set<TEntity>().FindAsync(id);
}
public async Task<List<TEntity>> GetAll()
{
return await _context.Set<TEntity>().ToListAsync();
}
public async Task<TEntity> Add(TEntity entity)
{
_context.Set<TEntity>().Add(entity);
return entity;
}
public async Task<TEntity> Delete(int id)
{
var entity = await _context.Set<TEntity>().FindAsync(id);
if (entity == null)
{
return entity;
}
_context.Set<TEntity>().Remove(entity);
return entity;
}
public async Task<TEntity> Update(TEntity entity)
{
_context.Entry(entity).State = EntityState.Modified;
return entity;
}
public async Task SaveDbChangesAsync()
{
await _context.SaveChangesAsync();
}
}
In this way you do not store changes to the database but changes are only tracked in memory in the context.
When you are done with all your logic you can call the SaveDbChangesAsync() method from your service class (in general the class that is using the repository) and persist the change.
public class EmployeeService
{
private readonly IEmployeeRepository _employeeRepository;
private readonly IAddressRepository _addressRepository;
private readonly IMapper _mapper;
public EmployeeService(IEmployeeRepository employeeRepository, IAddressRepository addressRepository, IMapper mapper)
{
_employeeRepository = employeeRepository;
_addressRepository = addressRepository;
_mapper = mapper;
}
public async Task<EmployeeDto> Add(EmployeeDto employeeDto)
{
// TODO: Start transaction here ???
var employee = _mapper.Map<Employee>(employeeDto);
var addedEmployee = await _employeeRepository.Add(employee);
var employeeAddress = Generate_Employee_Address_Entity_With_EmployeeId(addedEmployee);
var addedAddress = await _addressRepository.Add(employeeAddress);
var output = Generate_Employee_Dto(addedEmployee, addedAddress);
//// Here instead of commit transaction, we save changes to
//// the database. If anything goes wrong changes will be discarded
//// anyways when you context gets out of scope;
await SaveDbChangesAsync();
return outputs;
}
Benefits:
Avoid to use a performance heavy operation like Transactions
You use EF in the right way
However if you insist using transactions you can do the following:
using (EntitiesContext context = new EntitiesContext())
{
using (var transaction = context.Database.BeginTransaction())
{
}
}

Issues with DbContext getting disposed after multiple calls to service

I am working on an API and am having problems with making multiple calls to a service and it's different methods, I have each method creating and using new DBContext (or at least that's the intention), but after the first service call the others complain that the DBContext has been disposed, I was hoping you could point me in the right direction, because as far as I can see I am creating a new context for each of these calls - obviously I am doing something wrong here, any help would be much appreciated.
The actual error I am getting is "Cannot access a disposed object."
I know I can maybe pull the db interaction and context creation code out of the service and into the controller method here (it's a simplified example), but will need to use more services in other parts of the application and have encountered the problem there also, so would like to try and identify what is causing my problem in this example so that I can apply the fix elsewhere.
Here are the simplified classes involved.
public class UserController : Controller
{
private readonly IUserService userService;
public UserController(IUserService userService)
{
this.userService = userService;
}
[HttpPost]
[ActionName("PostUserDetails")]
public async Task<IActionResult> PostUserDetails([FromBody]UserDetailsContract userDetailsContract)
{
// this call is fine
var user = await userService.GetUserByCode(userDetailsContract.Code);
if (user == null)
{
return BadRequest("User not found");
}
// this call fails with the object disposed error
var userDetails = await userService.GetUserDetailsByCode(userDetailsContract.Code);
if (userDetails != null)
{
return BadRequest("UserDetails already exists");
}
// .. go on to save new entity
return Ok();
}
}
public class UserService : IUserService
{
private readonly IDatabaseFactory databaseFactory;
public UserService(IDatabaseFactory databaseFactory)
{
this.databaseFactory = databaseFactory;
}
public async Task<User> GetUserByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.Users.GetByCode(code);
}
}
public async Task<IEnumerable<UserDetail>> GetUserDetailsByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.UserDetails.GetByCode(code);
}
}
}
public class ApiDbContext : DbContext, IApiDbContext
{
public DbSet<User> Users { get; set; }
public DbSet<UserDetail> UserDetails { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=192.168.1.1;Database=dbname;User Id=user; Password=pwd; MultipleActiveResultSets=True;");
}
}
public class DatabaseFactory : IDatabaseFactory
{
public IApiDatabase Create()
{
return new ApiDatabase(new ApiDbContext());
}
}
public class ApiDatabase : RepositoriesBase, IApiDatabase
{
private IUserRepository userRepository;
private IUserDetailsRepository userDetailsRepository;
public ApiDatabase(ApiDbContext context) : base(context)
{
}
public IUserRepository Users => userRepository ?? (userRepository = new UserRepository(context));
public IUserDetailsRepository UserExchange => userDetailsRepository ?? (userDetailsRepository = new UserDetailsRepository(context));
}
public abstract class RepositoriesBase : IRepositories
{
internal readonly ApiDbContext context;
private bool isDisposing;
protected RepositoriesBase(ApiDbContext context)
{
}
public void Dispose()
{
if (!isDisposing)
{
isDisposing = true;
context?.Dispose();
}
}
public Task SaveChanges() => context.SaveChangesAsync();
}
public class UserRepository : Repository<User>, IUserRepository
{
public UserRepository(ApiDbContext context) : base(context)
{
}
public async Task<User> GetByCode(string code)
{
return Filter(x => x.code == code).Result.FirstOrDefault();
}
}
public class UserDetailsRepository : Repository<UserDetail>, IUserDetailRepository
{
public UserExchangeRepository(ApiDbContext context) : base(context)
{
}
public async Task<IEnumerable<UserDetail>> GetByUserId(int userId)
{
return await Filter(x => x.UserId == userId);
}
}
public class Repository<T> : IRepository<T> where T : class, IEntity
{
private readonly ApiDbContext context;
public Repository(ApiDbContext context) => this.context = context;
public async Task Add(T entity)
{
context.Set<T>().Add(entity);
}
public async Task Add(IEnumerable<T> entities)
{
foreach (var entity in entities)
{
context.Set<T>().Add(entity);
}
}
public async Task Delete(T entity)
{
context.Set<T>().Remove(entity);
}
public async Task Delete(IEnumerable<T> entities)
{
foreach (var entity in entities)
{
context.Set<T>().Remove(entity);
}
}
public async Task Delete(int id)
{
var entityToDelete = context.Set<T>().FirstOrDefault(e => e.Id == id);
if (entityToDelete != null)
{
context.Set<T>().Remove(entityToDelete);
}
}
public async Task Update(T entity)
{
context.Set<T>().Update(entity);
}
public async Task Edit(T entity)
{
var editedEntity = context.Set<T>().FirstOrDefault(e => e.Id == entity.Id);
editedEntity = entity;
}
public async Task<IEnumerable<T>> GetAll(Expression<Func<T, bool>> predicate = null)
{
var query = context.Set<T>().Include(context.GetIncludePaths(typeof(T)));
if (predicate != null)
{
query = query.Where(predicate);
}
return await query.ToListAsync();
}
public async Task<T> GetById(int id)
{
return context.Set<T>().FirstOrDefault(e => e.Id == id);
}
public async Task<IEnumerable<T>> Filter()
{
return context.Set<T>();
}
public virtual async Task<IEnumerable<T>> Filter(Func<T, bool> predicate)
{
return context.Set<T>().Where(predicate);
}
public async Task SaveChanges() => context.SaveChanges();
}
In my DI config I have DatabaseFactory and UserService defined as singletons.
Error: "Cannot access a disposed object."
More error details: " at
Microsoft.EntityFrameworkCore.DbContext.CheckDisposed() at
Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.get_Model() at
Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityType()
at
Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityQueryable()
at
Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.System.Collections.Generic.IEnumerable<TEntity>.GetEnumerator()
at System.Linq.Enumerable.WhereEnumerableIterator1.MoveNext() at
System.Linq.Enumerable.Any[TSource](IEnumerable1 source, Func2
predicate) at
App.Api.Controllers.UserController.PostUserDetail(UserDetailContract
userDetailContract) in
D:\Repositories\application\src\App\Api\Controllers\UserController.cs:line
89"
Thank you
I think you may be a victim of delayed execution. The following piece of code creates an instance of of ApiDatabase which in turn creates a new ApiDbContext:
public IApiDatabase Create() //in DatabaseFactory
{
return new ApiDatabase(new ApiDbContext());
}
I detect a code smell here, by the way, as ApiDbContext is disposable so you should be tracking this reference and disposing of it properly.
Anyways, ApiDatabase is disposable since it's wrapped in a using statement, so I think the the context is being disposed after the call to GetByUserId:
public async Task<IEnumerable<UserDetail>> GetByUserId(int userId)
{
return await Filter(x => x.UserId == userId);
}
Notice you are returning an enumeration. I think it may not be materialized by the time you use it, hence the error. Add a cast to an array to force materialization:
return await Filter(x => x.UserId == userId).ToArray();
Your problem is the signature of this method:
public async Task<IEnumerable<UserDetail>> GetUserDetailsByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.UserDetails.GetByCode(code);
}
}
IEnumerable<T> is an enumerable, which are generally lazy-evaluated. In the meantime, the Task<T> is considered complete once the enumerable is defined (not when it is completed). And the context is disposed once that enumerable is defined. You would have the same problem if the code was synchronous.
The fix is to "reify" (evaluate) the enumerable before the context is disposed:
public async Task<IReadOnlyCollection<UserDetail>> GetUserDetailsByCode(string code)
{
using (var db = databaseFactory.Create())
{
return await db.UserDetails.GetByCode(code).ToList();
}
}

Implementing Generic Repository and UnitOfWork

I have followed this tutorial.
I got to a stage of calling a repository using _unitOfWork.XYZRepository.Get(), now to take it further I want to write an interface for my UnitOfWork class and inject it to my controller.
I am not sure whether I need write interface for GenericRepository or UnitofWork class or both.
Can some one guide me in this as to what needs to be done to instantiate a repository with interface instead of private readonly UnitOfWork _unitOfWork = new UnitOfWork(); as shown in the link above.
Modify your repository constructor to accept a unit of work, via its interface:
public MyRepository(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
Then you instantiate your repository, passing the appropriate unit of work in via the constructor. Alternatively, wire-up your IoC container of choice and let it do the heavy lifting.
Here's a nice tutorial on using Castle Windsor with ASP.NET MVC.
I have used Autofac for this purpose. In my Global.asax.cs file
var builder = new ContainerBuilder();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerApiRequest();
builder.RegisterAssemblyTypes(typeof (LocationTypesRepository).Assembly).Where(
type => type.Name.EndsWith("Repository")).AsImplementedInterfaces();
and then in my controller
public class LocationTypesController : ApiController
{
private readonly ILocationRepository _locationRepository;
private readonly IUnitOfWork _unitOfWork;
private readonly IAuthenticatedUser _user;
public LocationTypesController(ILocationRepository locationRepository,
IUnitOfWork unitOfWork,
IAuthenticatedUser user)
{
if (locationRepository == null)
throw new ArgumentNullException("locationRepository");
if (unitOfWork == null)
throw new ArgumentNullException("unitOfWork");
if (user == null)
throw new ArgumentNullException("user");
_locationRepository = locationRepository;
_unitOfWork = unitOfWork;
_user = user;
}
public IEnumerable<LocationType> Get()
{
try
{
IEnumerable<Location> locations = _locationRepository.GetAllAuthorizedLocations(_user.UserName);
_unitOfWork.Commit();
return locations.Select(location => location.LocationType).Distinct().OrderBy(location => location.LocationTypeId);
}
catch (Exception)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.BadRequest));
}
}
Essentially leveraging a DI framework and placing the interfaces as parameters to your repositories (or in my case a WebApi controller)
Based on suggestions I have made following changes...
public interface IGenericRepository<T> where T : class
{
IQueryable<T> Get();
IQueryable<T> FindBy(Expression<Func<T, bool>> predicate);
void Insert(T entity);
void Delete(T entity);
void Update(T entity);
void Save();
T GetByID(Object id);
}
public class GenericRepository<C, T> : IGenericRepository<T>
where T : class
where C : EFDbContext, new()
{
private C _entities = new C();
public C Context
{
get { return _entities; }
set { _entities = value; }
}
public virtual IQueryable<T> Get()
{
IQueryable<T> query = _entities.Set<T>();
return query;
}
public virtual T GetByID(object id)
{
return Context.Set<T>().Find(id);
}
}
//NinjectControllerFactory
private void AddBindings()
{
_ninjectKernel.Bind<IGenericRepository<Product>>().To<GenericRepository<EFDbContext, Product>>();
}
//Controller
[Inject]
public IGenericRepository<Product> ProductRepo;
public ProductController(IGenericRepository<Product> ProductRepository )
{
ProductRepo= ProductRepository ;
}
//Inside Action
model.Products = ProductRepo.Get();
Everything works now... Thanks for the help...

Categories

Resources