Using DBContext in repository class - c#

I want to use my DBcontext in my repository class, but I don't know how at the moment. I've tried looking at other questions, but they don't provide the answer needed specific for my case.
I'll start with my controller:
public class MovieController : ControllerBase
{
IMovieService _movieService;
public MovieController(IMovieService movieService)
{
_movieService = movieService;
}
Service class:
public interface IMovieService
{
Movie RetrieveMovie(long id);
IEnumerable<Movie> RetrieveMovies();
}
public class MovieService : IMovieService
{
IMovieRepository _movieRepository;
public MovieService(IMovieRepository movieRepository)
{
_movieRepository = movieRepository;
}
Repository class:
public interface IMovieRepository
{
Movie GetMovieById(long id);
IEnumerable<Movie> GetMovies();
}
public class MovieRepository : IMovieRepository
{
private readonly MusicContext _db;
public Movie GetMovieById(long id)
{
return _db.Movie.Find(id);
}
DBContext:
public class MovieContext : DbContext
{
public MovieContext(DbContextOptions<MovieContext> options)
: base(options)
{
}
public DbSet<Movie> Movies { get; set; }
}
And last my startup function:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MovieContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MovieContext")));
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Movie.Api", Version = "v1" });
});
}
At the moment Visual studio warns me # MusicContext _db that this field is never assigned to, which I get. How can I get this to work?

you have to add a constructor to a MovieRepository and use DI
public class MovieRepository : IMovieRepository
{
private readonly MovieContext _db;
public MovieRepository (MovieContext db)
{
_db=db;
}
public Movie GetMovieById(long id)
{
return _db.Movie.Find(id);
}
.....
}

Related

https://localhost:5001/API/users/ can't be found

when I do API> dotnet run on console, it doesn't show the page of list of users. I'm using sql server express. Here is the github link: https://github.com/joshii22/DatingApp
Error I get.
These are the files I changed.
UserController.cs
[ApiController]
[Route("api/[controller]")]
public class UsersControllers : ControllerBase
{
private readonly DataContext _context;
public UsersControllers(DataContext context)
{
_context = context;
}
[HttpGet]
public ActionResult<IEnumerable<AppUser>> GetUsers()
{
return _context.Users.ToList();
}
[HttpGet("{id}")]
public ActionResult<AppUser> GetUser(int id)
{
return _context.Users.Find(id);
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataContext>(options =>
{
options.UseSqlServer(_config.GetConnectionString("DefaultConnection"));
});
services.AddControllers();
}
appsettings.Development.json
{"ConnectionStrings" : {
"DefaultConnection" : "server = DESKTOP-QUO7HU2\\SQLEXPRESS; database = datingapp.db; Trusted_Connection = True;" }
DataContext.cs
public class DataContext : DbContext
{
public DataContext(DbContextOptions options) : base(options)
{
}
public DbSet<AppUser> Users { get; set; }
}
When on run this must show.
You should better change the UsersControllers to UsersController.
Or it will not be detected as a controller:
Or,you can change the Route to [Route("api/users")], instead of [controller]
public class UsersController : ControllerBase
{
private readonly DataContext _context;
public UsersController(DataContext context)
{
_context = context;
}
}
The other code are the same as yours and I can get the result:

Object Reference Not Set to an Instance of an Object at Repository Layer

I am receiving null exception error on my framework. I have tried to apply Repository and Unit of Work design patterns in my application. What I am trying to do is simply retreiving user titles from my data base with GetAll() method.
Here is my repository class:
public class Repository<T> : IRepository<T> where T : class
{
protected readonly DbContext Context;
public Repository(DbContext context)
{
this.Context = context;
}
public T Get(int id)
{
return Context.Set<T>().Find(id);
}
public IEnumerable<T> GetAll()
{
return Context.Set<T>().ToList();
}
public IEnumerable<T> Find(Expression<Func<T, bool>> predicate)
{
return Context.Set<T>().Where(predicate);
}
public void Add(T entity)
{
Context.Set<T>().Add(entity);
}
public void AddRange(IEnumerable<T> entityList)
{
Context.Set<T>().AddRange(entityList);
}
public void Remove(T entity)
{
Context.Set<T>().Remove(entity);
}
public void RemoveRange(IEnumerable<T> entityList)
{
Context.Set<T>().RemoveRange(entityList);
}
}
This is IUserTitlesRepository:
public interface IUserTitlesRepository : IRepository<UserTitles>
{
}
And, the class where above interface implemented:
public UserTitlesRepository(XaPaDataContext context) : base(context)
{
}
public XaPaDataContext XaPaDataContext
{
get { return Context as XaPaDataContext; }
}
Before coming to Controller layer, I have two more layers, which are Operation and Manager layers. And, I think I have messed up on that part (on Base Manager class as shown below).
This is operation layer:
public class UserTitlesOperations
{
private readonly IUnitOfWork _uow;
public UserTitlesOperations(IUnitOfWork uow)
{
_uow = uow;
}
public List<UserTitles> GetAllUserTitles()
{
try
{
List<UserTitles> userTitleList = _uow.UserTitles.GetAll().ToList();
_uow.Complete();
return userTitleList;
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
}
}
Below is the BaseManager class which gives inheritance to all manager classes.
public abstract class BaseManager
{
private IUnitOfWork _iUow;
private readonly XaPaDataContext _context;
public IUnitOfWork IUOW
{
get
{
if (_iUow == null)
{
_iUow = new XaPaUnitOfWork(_context);
}
return _iUow;
}
}
}
This is the manager class:
public class UserTitlesManager : BaseManager
{
private readonly UserTitlesOperations _userTitlesOperations;
public UserTitlesManager()
{
_userTitlesOperations = new UserTitlesOperations(base.IUOW);
}
public List<UserTitlesWM> GetAllUserTitles()
{
try
{
return UserTitlesMapping.MaptoWM(_userTitlesOperations.GetAllUserTitles());
}
catch (Exception ex)
{
throw new Exception(ex.ToString());
}
}
}
Finally, this is my API Controller:
[Route("api/LoginRequest")]
public class TitlesController : BaseController
{
UserTitlesManager _userTitlesManager;
public LoginController()
{
_userTitlesManager = new UserTitlesManager();
}
[Route("RetreiveTitles")]
public HttpResponseMessage GetTitles()
{
try
{
return Request.CreateResponse(HttpStatusCode.OK, _userTitlesManager.GetAllUserTitles());
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex.ToString());
}
}
}
By the way BaseController is just another API controller which gives inheritance to all other API controllers, and houses a method which is used by all the other controllers.
So, I'm still trying to sharpen my self on this design patterns and would be glad if anyone could show my mistake on BaseManager class. As I said, I suppose the problem is caused by that private readonly XaPaDataContext _context; line. On the other hand,I can't figure out how to corrrect it as my operation classes' constructors are asking for IUnitOfWork.
Thank you in advance!
EDIT:
Just realized that I forgot to share my Unit of Work class:
public class XaPaUnitOfWork : IUnitOfWork
{
private readonly XaPaDataContext _context;
public XaPaUnitOfWork(XaPaDataContext context)
{
_context = context;
Categories = new CategoriesRepository(_context);
OrderDetails = new OrderDetailsRepository(_context);
Orders = new OrdersRepository(_context);
ProductImages = new ProductImagesRepository(_context);
Products = new ProductsRepository(_context);
Users = new UsersRepository(_context);
UserTitles = new UserTitlesRepository(_context);
UserTokens = new UserTokensRepository(_context);
}
public ICategoriesRepository Categories { get; private set; }
public IOrderDetailsRepository OrderDetails { get; private set; }
public IOrdersRepository Orders { get; private set; }
public IProductImagesRepository ProductImages { get; private set; }
public IProductsRepository Products { get; private set; }
public IUsersRepository Users { get; private set; }
public IUserTitlesRepository UserTitles { get; private set; }
public IUserTokensRepository UserTokens { get; private set; }
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
After I have changed my BaseManager class as below:
public abstract class BaseManager
{
private IUnitOfWork _iUow;
public IUnitOfWork IUOW
{
get
{
if (_iUow == null)
{
_iUow = new XaPaUnitOfWork(new XaPaDataContext());
}
return _iUow;
}
}
}
I have achived to receive HttpStatusCode.OK
But, honestly, I'm still unsure about the real reason. I make this correction mostly by heart.

Inject different implementations that have same interface in multiple constructor parameters

My goal is to create an object that contains different implementations of an interface and at runtime select the implementation to use. I'm using the Dependency injection in ASP.NET Core.
Code:
public interface IStateRepository : IDbReadRepository<IState> { }
public interface IDbReadRepository<T> : IBaseRepository
{
IReadOnlyList<T> GetAll();
}
public interface IBaseRepository
{
IUserContext UserContext { get; set; }
}
namespace MvcOpinionatedTemplate.Repositories.Dapper
{
public class StateRepository : BaseDbRepository, IStateRepository
{
public StateRepository(IUserContext userContext, IDbConnection dbConnection) : base(userContext, dbConnection) { }
public IReadOnlyList<IState> GetAll()
{
return _dbConnection.Query<State>("SELECT * FROM State").ToList();
}
}
}
namespace Template.Repositories.Local
{
public class StateRepository : BaseRepository, IStateRepository
{
public StateRepository(IUserContext userContext) : base(userContext) { }
public IReadOnlyList<IState> GetAll()
{
var filePath = Path.Combine(AppContext.BaseDirectory, #"Local\json\states.json");
return JsonConvert.DeserializeObject<List<State>>(File.ReadAllText(filePath));
}
}
namespace MvcOpinionatedTemplate.Repositories.Collections
{
public class StateRepositories
{
public IStateRepository Local { get; }
public IStateRepository SqlServer { get; }
public StateRepositories(IStateRepository local, IStateRepository sqlServer)
{
Local = local;
SqlServer = sqlServer;
}
}
}
What I'd like to do is set in the Startup.ConfigureServices():
services.AddTransient<StateRepositories, XXXXX>
I tried this:
services.AddTransient<StateRepositories>(s => new StateRepositories(new Repositories.Local.StateRepository(--UserContext--), new Repositories.Dapper.StateRepository(-UserContext--)));
The problem is how to have DI populate UserContext. I have it defined Startup.ConfigureServices():
services.AddScoped<IUserContext, UserContext>();
How do have DI populate UserContext for the StateRepositories implementations? Or is there a better approach to achieve my goal?
You can register your IStateRepository separately and then inject IEnumerable<IStateRepository> which injects all implementations of IStateRepository.
public interface IStateRepository
{
}
public class LocalRepository : IStateRepository
{
}
public class DapperRepository : IStateRepository
{
}
services.AddTransient<IStateRepository, LocalRepository>()
.AddTransient<IStateRepository, DapperRepository>()
.AddTransient<StateRepositories>();
public class StateRepositories
{
public IStateRepository Local { get; }
public IStateRepository SqlServer { get; }
public StateRepositories(IEnumerable<IStateRepository> repositories)
{
Local = repositories.OfType<LocalRepository>().FirstOrDefault();
SqlServer = repositories.OfType<DapperRepository>().FirstOrDefault();
}
}

How to pass service config to the Xunit project Test controller?

I have below setup currently.
Startup Class : Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AzureStorageConfig>(Configuration.GetSection("AzureStorageConfig"));
services.AddTransient<IAzureService, AzureService>();
}
//other config settings ...
}
Class: AzureStorageConfig
//store the azure account details etc...
public class AzureStorageConfig
{
public string AzureURL { get; set; }
public string StorageConnectionString { get; set; }
public string AccountName { get; set; }
}
Interface class: IAzureService
public interface IAzureService
{
Task<string> UploadFileAsync(AzureStorageConfig _storageConfig, string filename);
Task<string> DeleteFile(AzureStorageConfig _storageConfig, string filename);
}
Azure class : AzureService that use by interface above
public class AzureService : IAzureService, IDisposable
{
// Here implemented above service methods..
public Task<string> UploadFileAsync(AzureStorageConfig _storageConfig, string filename)
{
//...
}
Task<string> DeleteFile(AzureStorageConfig _storageConfig, string filename)
{
//...
}
}
Image controller: ImageController.cs
[Produces("application/json")]
[Route("api/Images")]
public class ImagesController : Controller
{
#region Private properties
private readonly ApiDbContext _context;
private readonly IMapper _mapper;
private AzureStorageConfig _storageConfig;
public readonly IAzureService _azureService;
#endregion
#region Constructor
public ImagesController(ApiDbContext context, IMapper mapper, IOptions<AzureStorageConfig> storageConfig, IAzureService azureService)
{
_context = context;
_mapper = mapper;
_storageConfig = storageConfig.Value;
_azureService = azureService;
}
// other POST and Delete methods written
public async Task<IActionResult> PostImage(Guid Id, [FromForm] ICollection<IFormFile> files)
{
// here used _storageConfig objects to use account key and names...
}
}
Main issue in below class ( TEST Library ) with xunit
Class: ImageControllerTest
[Collection("TestDb")]
public class ImageControllerTest : IClassFixture<InitializeAutoMap>
{
private ImagesController _controller;
private DatabaseFixture _fixture;
private InitializeAutoMap _initialize;
public ImageControllerTest(DatabaseFixture fixture, InitializeAutoMap initialize)
{
this._fixture = fixture;
this._initialize = initialize;
// How to pass service object and StorageConfig to the main
//ImageController from this TestController for testing.
_controller = new ImagesController(_context,
_initialize.InstanceMapper,
/*<here I want to pass storageConfig object>*/,
/*<here I want to pass service object>*/
);
}
// other [Fact] testing done.
// other codes..
}
How to it will possible to get injected to Xunit ImageControllerTest constructor.
storageConfig class object and
IAzureService object
into the ImageController from Xunit ImageControllerTest method.?
Share me if you have idea or solutions.

How to inject a parameter constructor into Repository constructor using Ninject?

I am trying to create DBContect object by passing connection string at run time.
Following is the structure of my NiNject Repository implementation.
public class HomeController : ApiController
{
MyService _service{ get; set; }
public HomeController(MyService service)
{
_service= service;
}
}
public class MyService
{
IRepository _repo { get; set; }
public MyService(IRepository repo)
{
_repo = repo;
}
}
Repository implementation is as follows :
public interface IRepository
{
TenantDbContext _db { get; set; }
void Add<T>(T entity) where T : class;
void Delete<T>(int id) where T : class;
T Find<T>(int id) where T : class;
IQueryable<T> Query<T>() where T : class;
void SaveChanges();
MasterDbContext _db_master { get; set; }
void Add_Master<T>(T entity) where T : class;
void Delete_Master<T>(int id) where T : class;
T Find_Master<T>(int id) where T : class;
IQueryable<T> Query_Master<T>() where T : class;
void SaveChanges_Master();
}
public class Repository : IRepository
{
public TenantDbContext _db { get; set; }
public MasterDbContext _db_master { get; set; }
public Repository(TenantDbContext db)
{
_db = db;
}
public Repository(MasterDbContext db_master)
{
_db_master = db_master;
}
public IQueryable<T> Query<T>() where T : class
{
return _db.Set<T>().AsQueryable();
}
public IQueryable<T> Query_Master<T>() where T : class
{
return _db_master.Set<T>().AsQueryable();
}
//.....Rest of the implemetation
}
Here goes my TenantDBContext class which takes an argument as a database string. No default constructor
public class TenantDbContext : DbContext
{
public TenantDbContext(string connString)
: base(connString)
{
//Configuration.AutoDetectChangesEnabled = true;
//Configuration.LazyLoadingEnabled = false;
//Configuration.ProxyCreationEnabled = false; //change tracking
}
public static TenantDbContext Create(string DbString)
{
// Some logic to get the tenant database string.
// Presently i am just passing it hard coded as follows.
return new TenantDbContext(DbString);
}
}
public class MasterDbContext : IdentityDbContext<ApplicationUser>
{
public MasterDbContext() : base("MasterDBConnection", throwIfV1Schema: false)
{
// dbmigration.AutomaticMigrationsEnabled = true;
Configuration.ProxyCreationEnabled = false;
Configuration.LazyLoadingEnabled = false;
}
public static MasterDbContext Create()
{
return new MasterDbContext();
}
//public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<Tenant> Tenants { get; set; }
public DbSet<TenantUserMap> TenantUserMaps { get; set; } }
Finally, RegisterServices which i have in my NinjectWebCommons.cs looks as follows :
Each Tenant have its different database. We are fetching out the Tenant name from the access token on every request and caching that requested Tenant object so we can pass the correct Tenant Database string in order to do the operations on the requested Tenant Database.
Below snippet, We are fetching the Tenant object from the current request cache which will provide us the Tenant Database string of the requested client.
public Tenant Tenant
{
get
{
object multiTenant;
if (!HttpContext.Current.GetOwinContext().Environment.TryGetValue("MultiTenant", out multiTenant))
{
throw new ApplicationException("Could Not Find Tenant");
}
return (Tenant)multiTenant;
}
}
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IRepository>().To<Repository>();
kernel.Bind<TenantDbContext>().ToMethod(_ =>
TenantDbContext.Create(Tenant.DBString));
kernel.Bind<MasterDbContext>().ToMethod(__ => MasterDbContext.Create());
}
Problem : When i add second binding in my NinjectWebCommons.cs "kernel.Bind()" , i start getting exception saying that "No default constructor found". It is simply not taking two bindings with the kernal. Request you to please have a look at the code above and point me out where i am going wrong.
I will appreciate your help. Thanks in Advance.
You may add a binding for the database context and point Ninject to use your factory method:
kernel.Bind<TenantDbContext>().ToMethod(_ => TenantDbContext.Create());

Categories