1

Given the following scenario...

I am concerned about two things...

1) Is it okay to inject a provider into a business model object? - like I did with the Folder implementation because I want to load Sub-folders on demand.

2) Since I am injecting the DbContext in the Sql implementation of IFolderDataProvider, the context could be disposed or it could live on forever, therefore should I instantiate the context in the constructor?

If this design is incorrect then someone please tell me how should business models be loaded.

//Business model.
interface IFolder
{
    int Id { get; }
    IEnumerable<IFolder> GetSubFolders();
}

class Folder : IFolder
{
    private readonly int id_;
    private readonly IFolderDataProvider provider_;

    public Folder(int id, IFolderDataProvider provider)
    {
        id_ = id;
        provider_ = provider;
    }

    public int Id { get; }

    public IEnumerable<IFolder> GetSubFolders()
    {
        return provider_.GetSubFoldersByParentFolderId(id_);
    }
}

interface IFolderDataProvider
{
    IFolder GetById(int id);

    IEnumerable<IFolder> GetSubFoldersByParentFolderId(int id);
}

class SqlFolderDataProvider : IFolderDataProvider
{
    private readonly DbContext context_;

    public SqlFolderDataProvider(DbContext context)
    {
        context_ = context;
    }

    public IFolder GetById(int id)
    {
        //uses the context to fetch the required folder entity and translates it to the business object.
        return new Folder(id, this);
    }

    public IEnumerable<IFolder> GetSubFoldersByParentFolderId(int id)
    {
        //uses the context to fetch the required subfolders entities and translates it to the business objects.
    }
}

2 Answers2

1

Is it okay to inject a provider into a business model object? - like I did with the Folder implementation because I want to load Sub-folders on demand.

Yes, how else would you be able to call the provider and get the data?

However, the suffix DataProvider is very confusing because it is used for the provider that you use to connect to the database. I recommend changing it to something else. Examples: Repository, Context.

Since I am injecting the DbContext in the Sql implementation of IFolderDataProvider, the context could be disposed or it could live on forever, therefore should I instantiate the context in the constructor?

It won't necessarily live on forever. You decide its life span in your ConfigureServices function when you're adding it as a service, so you can change its scope from Singleton to whatever you like. I personally set the scope of my DBContext service to Transient and I also initiate it there with the connection string:

services.AddTransient<IDbContext, DbContext>(options =>
                      new DbContext(Configuration.GetConnectionString("DefaultDB")));

I then open and close the database connection in every function in my data layer files (you call it provider). I open it inside a using() statement which then guarantees closing the connection under any condition (normal or exception). Something like this:

public async Task<Location> GetLocation(int id) {
  string sql = "SELECT * FROM locations WHERE id = @p_Id;";
  using (var con = _db.CreateConnection()) {
     //get results
  }
}
Racil Hilan
  • 24,690
  • 13
  • 50
  • 55
  • Thank you mate. Your answer is very useful. –  Jan 05 '18 at 20:59
  • Should the IFolderRepository interface be in the BLL project or DAL project? Because I have read articles that say BLL and DAL should not know about each other. –  Jan 08 '18 at 10:57
  • It seems you're using the 3-layers design. In that case I put all my repositories (e.g. `IFolderRepository` and its implementation `FolderRepository`) in the DL (Data Layer) along with the `DbContext`, and all my services (I call them services or facades) (e.g. `IFolderService` and `FolderService`) along with all domain or business models (e.g. `IFolder` and `Folder`) in my BL (Business Layer). I only put fields in my models, the logic goes into the services. – Racil Hilan Jan 08 '18 at 18:08
  • However, I only use separate projects for the layers in big enterprise solutions. This separation works well with big teams. For smaller apps, I just put all the layers in the same project. – Racil Hilan Jan 08 '18 at 18:18
1

Is it okay to inject a provider into a business model object

Yes if you call it "business" provider :). Actually do not take too serious all this terminology "inject", "provider". Till you pass (to business model layer's method/constructor) interface that is declared on business model layer (and document abstraction leaks) - you are ok.

should I instantiate the context in the constructor?

This could be observed as an abstraction leak that should be documented. Reused context can be corrupted or can be shared with another thread and etc -- all this can bring side effects. So developers tend to do create one "heavy" object like dbContext per "user request" (that usually means per service call using(var context = new DbContext()), but not always, e.g. Sometimes I share it with Authentication Service Call - to check is the next operation allowed for this user). BTW, DbContext is quite quick to create so do not reuse it just for "optimization".

Roman Pokrovskij
  • 9,449
  • 21
  • 87
  • 142
  • Yes I was under the impression that a db context is an expensive object to create but now I'll make a better use of it. Thank you. –  Jan 05 '18 at 21:01
  • You're probably confusing the concepts. The database connection itself is an expensive resource, but the db context is usually a lightweight class (mine has only a few lines). However, although the database connection is expensive and shouldn't be opened and closed frequently, it is managed for you with the connection pool. So make sure you close it right away once you're done with it (the `using()` statement will do that almost perfectly), and don't worry about it because it will be simply returned to the connection pool and not really closed immediately. – Racil Hilan Jan 05 '18 at 21:11