Unit of Work - All Repositories need to be properties? - .net-core

I am trying to use Repository/UoW Patternin a .net core project . I have looked at many implementations across web . In all the implementations repositories are created as properties in IUnitOfWork.
In Future if we have 50 Repositories we need to have 50 properties in the Unit of Work . Could anyone suggest a better approach for implementing Repository/UoW.
Please Find below the code snippets of approach i have implemented currently.
IUnitOfWork.cs
IStudentRepository Student { get; set; }
IClassRepository Class { get; set; }
void Complete();
UnitOfWOrk.cs
public class unitofwork {
private readonly CollegeContext _context;
IStudentRepository Student { get; set; }
IClassRepository Class { get; set; }
public UnitOfWork(CollegeContext CollegeContext)
{
this._context = CollegeContext;
Student = new StudentRepository(_context);
Class = new ClassRepository(_context);
}
public void Complete()
{
return _context.SaveChanges();
}
}
Student and Class Repositories Inherit From a generic Repository class and IStudentRepository and IClassRepository respectively.
StudentRepository.cs
public class StudentRepository : Repository<Student> , IStudentRepository
{
private readonly CollegeContext context;
private DbSet<Student> entities;
public StudentRepository(CollegeContext context) : base(context)
{
this.context = context;
entities = context.Set<Student>();
}
}

Property-per-Repository is not convenient in some cases as you said. I generally use some sort of factory method in UoW class as below:
public class unitofwork
{
private readonly CollegeContext _context;
IStudentRepository Student { get; set; }
IClassRepository Class { get; set; }
public UnitOfWork(CollegeContext CollegeContext)
{
this._context = CollegeContext;
}
public T CreateRepository<T>() where T : IRepository
{
IRepository repository = null;
if(typeof(T) == typeof(IStudentRepository))
repository = new StudentRepository(_context);
......
......
else
throw new XyzException("Repository type is not handled.");
return (T)repository;
}
public void Complete()
{
return _context.SaveChanges();
}
}
public interface IRepository
{
Guid RepositoryId { get; }
}
My IRepository just hold a simple property. You may extend this interface as per your needs.

Related

My dbContext return null, when I want get list of user, which are from identity

My dbContext return null, when I want get list of user in Index View. This list are from my database AspNetUsers table, which has been generate by identity. I can get other my database table list.
There is my ApplicationDbContext
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<ProductCategory> ProductCategories { get; set; }
public DbSet<ProductBrand> ProductBrands { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<Address> Address { get; set; }
public DbSet<Recipe> Recipes { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<Order_detail> Order_Details { get; set; }
}
There is my UserController
[Area("Admin")]
public class UserController : Controller
{
UserManager<IdentityUser> _userManager;
private ApplicationDbContext _db;
public UserController(UserManager<IdentityUser> userManager, ApplicationDbContext db)
{
_userManager = userManager;
_db = db;
}
public IActionResult Index()
{
return View(_db.ApplicationUsers.ToList());
}
}
There is my ApplicationUser.Model, which inherit IdendityUser
public class ApplicationUser : IdentityUser
{
public string Name { get; set; }
public string Surname { get; set; }
public ICollection<Recipe> Products { get; set; }
public ICollection<Order> Orders { get; set; }
}
I don't know how you register ApplicationDbContext and Identity framework on your ASP.NET Core MVC application because you didn't show them on the question.
There are couple problems in your code.
First, if you have a custom IdentityUser, like the ApplicationUser you have, you would have to use the generic version of IdentityDbContext:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
...
}
You would need to use the matching generic version of IdentityDbContext if you have any of following:
Custom IdentityUser
Custom IdentityRole
Custom primary key
All 7 classes, user and role, plus IdentityUserRole, IdentityUserClaim, IdentityRoleClaim, IdentityUserLogin, and IdentityUserToken
After you register your custom class with IdentityDbContext, you don't need to put the class as one of the DbSet<> there:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
...
public DbSet<ProductCategory> ProductCategories { get; set; }
public DbSet<ProductBrand> ProductBrands { get; set; }
public DbSet<Product> Products { get; set; }
// public DbSet<ApplicationUser> ApplicationUsers { get; set; }
public DbSet<Address> Address { get; set; }
public DbSet<Recipe> Recipes { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<Order_detail> Order_Details { get; set; }
}
You would also need to use the generic version of AddIdentity<TUser>, AddDefaultIdentity<TUser>, or AddIdentityCore<TUser> in your Startup.cs, depending on what you need:
AddDefaultIdentity = AddIdentity + AddDefaultTokens + AddDefaultUI
You didn't specify what version of ASP.NET Core Identity you're using so I don't exactly know which one you use, but the following is how I registered it:
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.User.RequireUniqueEmail = ...;
...
})
.AddEntityFrameworkStores<ApplicationDbContext>();
I have all 7 classes customized as well as change the primary key from string to Guid.
Lastly, to use the dependency injected UserManager and SignInManager, you would need to correct generic version of them as well:
[Area("Admin")]
public class UserController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public UserController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public IActionResult Index()
{
// Get the user list
var users = _userManager.Users.ToList();
// Build your view model to define what your UI only needs, not just passing
// everything to it
var vm = new UserManagementListViewModel
{
Users = users.Select(x => new UserViewModel
{
UserId = x.Id,
UserSurname = x.Surname,
ProductCount = x.Products.Count(),
OrderCount = x.Orders.Count()
})
};
return View(vm);
}
}

Getting computed column filled in EF Core on every call to entity

I am using code first with Entity Framework in my ASP.NET Core application. I am using a generic repository for crud operations.
I have a base audit class which is inherited by all entities in my dbcontext.
public class Audit_base
{
public int AddedUserId { get; set; }
public int ModifiedUserId { get; set; }
// I'd like to have it filled every time I query
[NotMapped]
public String AddedUsername { get; set; }
[NotMapped]
public String EditedUserName { get; set; }
}
And I have a User class in the dbcontext
public class UserMaster
{
public int Id { get; set; }
public int FullName { get; set; }
}
All my tables (about 70) inherit from the Audit_base class.
public class Book : Audit_base
{
public int Id { get; set; }
public String BookName { get; set; }
public virtual UserMaster CreatedByUser { get; set; }
public virtual UserMaster ModifiedByUser { get; set; }
}
And this is the part of generic repository class where I query these tables
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
protected readonly ApplicationContext _context;
public GenericRepository(ApplicationContext context)
{
_context = context;
}
public IEnumerable<T> Find(Expression<Func<T, bool>> expression)
{
return _context.Set<T>().Where(expression);
}
public IEnumerable<T> GetAll()
{
return _context.Set<T>().ToList();
}
public T GetById(int id)
{
return _context.Set<T>().Find(id);
}
}
Is there a way that I can get the user name corresponding to the created and modified userid when I query it, like a computed column?

I can not create instace of DBContext

I have model:
public class Department
{
public int DepartmentID { get; set; }
[Required]
[UniqueDepartmentName]
public string Name { get; set; }
public List<Person> Persons { get; set; }
}
And DBcontext:
public class InstituteContext : DbContext
{
public InstituteContext (DbContextOptions<InstituteContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Department>().HasIndex(p => p.Name).IsUnique();
}
public DbSet<Institute.Models.Department> Department { get; set; }
}
As you see property "NAME" i make unique.
For validation i create My validation Attribute:
public class UniqueDepartmentName : ValidationAttribute
{
public override bool IsValid(object value)
{
var db = new InstituteContext();
}
}
But i can not create instance of InstituteContext, because constructor need parameters.
How i can create instance of InstituteContext? Or what should i pass to constructor in parameters?
Try this:
public class UniqueDepartmentName : ValidationAttribute
{
public override bool IsValid(object value)
{
var connectionString = "Your ConnectionString"
var options = new DbContextOptionsBuilder<InstituteContext>()
.UseSqlServer(new SqlConnection(connectionString)).Options;
using (var dbContext = new BloggingContext(options))
{
// Do necessary staffs here with dbContext
}
}
}
Your DbContextOptions method is in the wrong place, your constructor can be empty, and you need to add the method OnConfiguring, which receives the DbContextOptions.
Something like:
public DbSet<Department> Department { get; private set; }
protected override void OnConfiguring(DbContextOptionsBuilder options) {
// In my case I'm passing the connection string in this method below
options.UseSqlServer("Data Source=DATABASEIP;Initial Catalog=DATABASETABLE;" +
"User ID=USER;Password=PASSWORD");
}

Asp.Net Core: Add data to IdentityDbContext or use DbContext

I work with Asp.Net Core WebApi project.
Can I add my tables to IdentityDbContext, like this:
public class ApplicationDbContext : IdentityDbContext<User>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{ }
public DbSet<ProgrammerRole> ProgrammerRoles { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<SubProject> SubProjects { get; set; }
public DbSet<Report> Reports { get; set; }
}
Or do I need to create a second DbContext. And if i create a second DbContext
how can I communicate wiht User in IdentityDbContext.
And my second question:
If i add data in IdentityDbContext, like above, How do I get the data from my tables in ApplicationDbContext?
Because i need to pass DbContextOptions object every time I create a new instance оf ApplicationDbContext. I do this in Startup.cs:
// ===== Add DbContext ========
var connectionString = Configuration.GetConnectionString("DbConnection");
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
I saw in older version of Asp.Net Core, that i can pass Connection String in IdentityDbContext constructor, but now only DbContextOptions.
And i can't do, for example this:
[AllowAnonymous]
[HttpGet]
public IEnumerable<Project> GetRoles()
{
using (var db = new ApplicationDbContext())
{
return db.Projects;
}
}
Can I add my tables to IdentityDbContext, like this:
Yes, it is how you create custom tables. You do not need to create another DbContext. E.g.
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Project> Projects { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Project>(entity =>
{
entity.Property(e => e.Name)
.IsRequired()
.HasMaxLength(50);
});
base.OnModelCreating(builder);
}
}
Note: you might need to run dotnet ef migrations add Initial and dotnet ef database update for database migration.
using (var db = new ApplicationDbContext()) {...}
You should not create or manage ApplicationDbContext inside controller. If you do so, they become tightly coupled, and you cannot implement unit tests.
Instead, you let dependency inversion (DI) container manage it for you. E.g.
public class UserController : Controller
{
private readonly ApplicationDbContext _context;
public UserController(ApplicationDbContext context)
{
_context = context;
}
[AllowAnonymous]
[HttpGet]
public IEnumerable<Project> GetRoles()
{
return _context.Projects;
}
}
I solve my problem, i just replaced code in my ApplicationDbContext, and get connection string from method:
public class ApplicationDbContext : IdentityDbContext<User>
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(GetConnectionString());
}
private static string GetConnectionString()
{
const string databaseName = "EmployeeReportsDb";
const string databasePass = "SuperPuper_Random_DB-key!";
return $"Server=localhost;" +
$"database={databaseName};" +
$"Trusted_Connection = True;" +
$"MultipleActiveResultSets = True;" +
$"pwd={databasePass};" +
$"pooling=true;";
}
public DbSet<ProgrammerRole> ProgrammerRoles { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<SubProject> SubProjects { get; set; }
public DbSet<Report> Reports { get; set; }
}
here is the resource: https://medium.com/#ozgurgul/asp-net-core-2-0-webapi-jwt-authentication-with-identity-mysql-3698eeba6ff8

SignalR multiple requests

I'm new to the forum and in the "world" of programming. I bumped into a problem while creating a game with SignalR Web technology and it is expressed in terms of access to the database (using EF) with multiple requests from UI-a. What is the best solution using the repository pattern? The decision to me at this stage is added Lock {} structure in each method, which accesses the database. How can I avoid blocking requests to a server?
public interface IRepository<T> where T : class
{
IQueryable<T> GetAll();
T GetById(object id);
void Add(T item);
void Update(T item);
void Delete(T item);
void Delete(object id);
}
public class DBRepository<T> : IRepository<T> where T : class
{
private DbContext DbContext;
private DbSet<T> Entities
{
get
{
return this.DbContext.Set<T>();
}
}
public DBRepository(DbContext context)
{
this.DbContext = context;
}
public IQueryable<T> GetAll()
{
return Entities.AsQueryable();
}
.....
public class TicTacToeContext : DbContext
{
public DbSet<Game> Games { get; set; }
public DbSet<Guess> Guesses { get; set; }
public DbSet<Message> Messages { get; set; }
public DbSet<MessageState> MessageStates { get; set; }
public DbSet<MessageType> MessageTypes { get; set; }
public DbSet<User> Users { get; set; }
public TicTacToeContext()
: base("TicTacToeDb")
{
}
public interface IGameService
{
void CreateGame(CreateGameModel gameModel);
void JoinGame(JoinGameModel gameModel);
...
public abstract class BaseService
{
public IRepository<User> UserRepository;
public IRepository<Game> GameRepository;
...
public class GameService : BaseService, IGameService
{
public GameService(IRepository<Game> gameRepositort, IRepository<User> userRepository, ISessionService sessionService)
{
this.UserRepository = userRepository;
this.GameRepository = gameRepositort;
}
public void CreateGame(CreateGameModel gameModel)
{
....
}
public class TicTacToeHub : Hub
{
IUserService UserServise;
IGameService GameServise;
private static object _syncRoot = new object();
public TicTacToeHub(IUserService userService, IGameService gameService)
{
this.UserServise = userService;
this.GameServise = gameService;
}
.....
public void ReturnOpenGamesToClient(string sessionKey)
{
IEnumerable<GameModel> openGames;
lock (_syncRoot)
{
openGames = GameServise.GetOpenGames(sessionKey).ToList();
}
Clients.Caller.updateOpenGamesList(openGames);
}
Why locks? You use a DB and only update one entity (No transaction scope needed).
Locks needs to be used for Inmemory types like IList or IDictionary otherwise it will crash when one request reads and another one writes. But SQL takes care of this for you

Resources