0

I have used IdentityDbContext in my project. My database has some tables which are connected to each other (relational). I want to use Repository pattern. I declared all my Interfaces. Then, I tried to Implement them. The problem is that I cannot create an instance of IdentityAppContext, because the constructor needs an input parameter 'option'. How can I implement them?

IdentityAppContext.cs :

public class IdentityAppContext: IdentityDbContext<AppUser, AppRole, int>
{
    public IdentityAppContext(DbContextOptions<IdentityAppContext> options) : base(options)
    {

    }
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    }
    public DbSet<AppUser> Users { get; set; }
    public DbSet<Message> Messages { get; set; }
    public DbSet<PM> PMs { get; set; }
    public DbSet<Notification> Notifications { get; set; }
    public DbSet<FileRepository> Files { get; set; }
}

IPmRepository.cs :

public interface IPmRepository
{
    IEnumerable<PM> GetAllPMs();
    PM GetPmById(int pmId);
    bool InsertPM(PM pm);
    bool UpdatePM(PM pm);
    bool DeletePM(int pmId);
    bool DeletePM(PM pm);
    void Save();
}

PmRepository.cs :

public class PmRepository : IPmRepository
{
    IdentityAppContext db = new IdentityAppContext();
    public IEnumerable<PM> GetAllPMs()
    {
        
    }
    public PM GetPmById(int pmId)
    {
        throw new NotImplementedException();
    }
    public bool InsertPM(PM pm)
    {
        throw new NotImplementedException();
    }
    public bool UpdatePM(PM pm)
    {
        throw new NotImplementedException();
    }
    public bool DeletePM(int pmId)
    {
        throw new NotImplementedException();
    }

    public bool DeletePM(PM pm)
    {
        throw new NotImplementedException();
    }
    public void Save()
    {
        throw new NotImplementedException();
    }
}
  • 3
    Why do you assume you need to use the repository pattern? A DbSet is already a single-entity repository. A DbContext is already a multi-entity Unit-of-Work. That `GetAllPMs` method is seldom useful - there's seldom any reason to load all rows in a table unless you want to fill a lookup table. Using a low-level "repository" interface over a high level abstraction like an ORM is actually an *anti*pattern – Panagiotis Kanavos Dec 03 '21 at 16:45
  • In agreement with @PanagiotisKanavos, avoid the antipattern. This will help you setup when dealing with IdentityDbContext: https://stackoverflow.com/questions/50377705/asp-net-core-add-data-to-identitydbcontext-or-use-dbcontext, Possible duplicate: https://stackoverflow.com/questions/23226140/asp-net-identity-with-repository-and-unit-of-work – David Vogel Dec 03 '21 at 17:20
  • If you want to use DDD, be aware that DDD uses a Repository for the *aggregate root* - that's the root of the object graph you need to load to handle a specific use case (bounded context in DDD terms). *NOT* individual entities. In that case, Yes, a *specialized* Repository makes sense. At that level there's definitely no need for classes with CRUD methods, no need to load all objects in a table. – Panagiotis Kanavos Dec 03 '21 at 17:35
  • 1
    As for why you can't use `IdentityDbContext` with new, it's because it's meant to be used with dependency injection. Your classes that use that DbContext need to work the same way - instead of trying to manually create their dependencies, accept them as constructor parameters. That makes it easy to change the underlying storage from SQL Server to MySQL to Oracle to NoSQL databases just by changing the options registered in `AddDbContext`, or passed to the constructor. This allows easy unit testing - you can use the in-memory provider without modifying your DbContext or any code that uses it – Panagiotis Kanavos Dec 03 '21 at 17:38
  • Assuming you really ned a `PmRepository`, it needs a constructor that accepts an `IdentityAppContext` parameter. – Panagiotis Kanavos Dec 03 '21 at 17:41
  • I updated IdentityAppContext.cs in the question explanation. –  Dec 03 '21 at 18:07
  • @PanagiotisKanavos Can I create an another constructor without any input parameter in IdentityAppContext (constructor overloading)? –  Dec 03 '21 at 18:10
  • Fix the bug instead of trying to cover it up. If you try to cover it up like this you'll have to hard code the provider and connection inside the DbContext. The original bug though is that `PmRepository` class. Consider: What are you trying to gain by using this class? Or are you using it because it's a "best practice"? It's not – Panagiotis Kanavos Dec 03 '21 at 18:14

1 Answers1

0

Presuming that the DbContext is registered with a DI Container in your Startup.cs file, using something like:

services.AddDbContext<IdentityAppContext>(opt => 
{
   opts.UseSqlServer(myConnectionString);
})

then adding the following in the same method as the above will register your repository in the same DI Container:

services.AddScoped<IPmRepository, PmRepository>();

Then in your repository:

public class PmRepository : IPmRepository
{
    readonly IdentityAppContext _context;
 
    // 'context' parameter is automagically provided by the container.
    public PmRepository(IdentityAppContect context)
    {
        _context = context;
    }

    public bool InsertPM(PM pm)
    {
        _context.PMs.Add(pm);
    }
}

Then, in your controller (which is also provided by the container), you should be able to request from the container an IPmRepository in the constructor:

public class MyController : Controller
{
    readonly IdentityAppContext _context;
    readonly IPmRepository _pmRepository;

    // Both 'context' and 'pmRepository' are automagically provided by the container
    public MyController(
        IdentityAppContext context,
        IPmRepository pmRepository)
    {
        _context = context;
        _pmRepository = pmRepository;
    }

    [HttpPost]
    public async Task DoSomething(MyRequest request)
    {
        _pmRepository.InsertPM(new PM() { Id = request.Id });

        await _context.SaveChangesAsync();
    }
}

Note that because IdentityAppContext and IPmRepository are registered in the same container as MyController, then the framework can automatically provide the parameters for the MyController constructor.

Further, when the PmRepository is being created for the Controller the container can look at the constructor parameters of PmRepository and see that it wants an IdentityAppContext, which can be provided automatically because its registered in the same container.

Neil W
  • 7,670
  • 3
  • 28
  • 41