In a controller, is there any difference between declaring a database context instance like this:
public class MyController : Controller
{
private readonly MyDatabaseEntities _context = new MyDatabaseEntities();
public ActionResult Index()
{
...
}
}
Versus this:
public class MyController : Controller
{
private readonly MyDatabaseEntities _context;
public MyController()
{
_context = new MyDatabaseEntities();
}
public ActionResult Index()
{
...
}
}
I'm just beginning to realise that the way I've been handling database context in my ASP.NET apps may not have been correct (or at least ideal) for a while and I'm reading up on it and thinking of how I'm going to start using Dependency Injection, but for starters I was just wondering if I'm even instantiating database contexts in my controllers correctly.
Incidentally, I'm still working in .NET Framework 4.7.2. Hopefully we'll start working in .NET 5 soon!
Related
So I have a repository class that looks as following:
public class StationsRepository : IStationsRepository
{
private readonly AftermarketDbContext _db;
public StationsRepository(DbContext db)
{
_db = db;
}
and in my startup.cs class i have added it to services as following.
services.AddTransient<IStationsRepository, StationsRepository>();
And in my controller I have injected it as following:
public RpcApiController(AftermarketDbContext db,IStationsRepository stationsRepository)
{
_db = db;
_stationsRepository = stationsRepository;
}
But my issue is how do I use it when I need it in the controller because it needs a database to be sent to the constructor so where so I do that? What would a example of using the repositories "GetStation" method look like for example?
I am in the process of migrating a project from .Net Framework to .Net Core. In the existing project we have a utility class with a few functions like below:
public static class BudgetUtilities
{
public static decimal CalculateBudgetRemaining(string fiscalYear = null)
{
if (string.IsNullOrWhiteSpace(fiscalYear))
fiscalYear = DateTime.Now.GetFiscalYear().ToString();
using (AppContext _context = new AppContext())
{
FiscalYearBudget currentBudget = _context.FiscalYearBudgets.Find(fiscalYear);
return currentBudget.BudgetAllocation - currentBudget.ExpenditureToDate;
}
}
// other functions removed for brevity
}
I can then reference it anywhere else using BudgetUtilities.CalculateBudgetRemaining(). Very simple and straightforward.
When migrating this function to .Net Core I need to use Dependency Injection so I have amended the class by removing the static modifier (since static constructors cannot have parameters) and injecting the AppContext into the constructor:
public class BudgetUtilities
{
private readonly AppContext _context;
public BudgetUtilities(AppContext context)
{
_context = context;
}
public decimal CalculateBudgetRemaining(string financialYear = null)
{
if (string.IsNullOrWhiteSpace(fiscalYear))
fiscalYear = DateTime.Now.GetFiscalYear().ToString();
FiscalYearBudget currentBudget = _context.FiscalYearBudgets.Find(fiscalYear);
return currentBudget.BudgetAllocation - currentBudget.ExpenditureToDate;
}
}
I then tried to call my code by doing the following:
BudgetUtilities utils = new BudgetUtilities();
decimal remaining = utils.CalculateBudgetRemaining();
But I cannot make a new instance of BudgetUtilities without providing an AppContext in the constructor which makes sense. Every method in this application is at some point initiated by a controller action, and I know that DbContexts are supposed to be short lived, so I assume passing the context the whole way down to this BudgetUtilities class from the initial controller is a bad idea.
The only other option I can see is to keep going back up the call stack from where CalculateBudgetRemaining() is referenced and keep adding in constructor injections until I get to a controller but this is not the only class I will have to inject like this so my constructors further up the chain are going to be really bloated and this will make my ConfigureServices() method bloated too.
I'm sure there's a simple way to do this but I just can't see it.
Don't manually create a new BudgetUtilities instance, that type should also be registered with the DI Framework, preferably interfaced:
public interface IBudgetUtilities
{
decimal CalculateBudgetRemaining(string financialYear);
}
public class BudgetUtilities : IBudgetUtilities
Then in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddScoped<IBudgetUtilities, BudgetUtilities>();
}
Then it can be injected into any class that needs it, such as a controller:
public class YourController : Controller
{
private readonly IBudgetUtilities _utils;
public YourController(IBudgetUtilities utils)
{
_utils = utils;
}
public ActionResult YourMethod()
{
//...
decimal remaining = _utils.CalculateBudgetRemaining();
}
}
By default, registered DbContexts have a scoped lifetime, which means a single instance is used for the entirety of a HTTP request.
I am taking small lookup table values from Entity Framework database and storing in MemoryCache. Currently using both ways, regular memoryCache, and Singleton MemoryContainer seen here Asp.Net Core: Use memory cache outside controller.
In Home Controller, this saves ProductTypes in a MemoryCache, and we see values store correctly in debug window (ProductType, ProductName, etc).
public class HomeController : Controller
{
public IMemoryCache _memoryCache;
public readonly StoreContext _storeContext;
public MemoryContainer _memoryContainer;
public HomeController(StoreContext storeContext, IMemoryCache memoryCache, MemoryContainer memoryContainer)
{
_storeContext= storeContext;
_memoryCache = memoryCache;
_memoryContainer = memoryContainer;
}
public IActionResult Index()
{
var productTypes = storeContext.ProductTypes;
_memoryCache.Set("ProductTypesKey", productTypes );
_memoryContainer._memoryCache.Set("ProductTypesKey2", test);
return View(); //both values store correctly
}
Then when going to ProductController,
public class ProductsController : Controller
{
public StoreContext _storeContext;
public IMemoryCache _memoryCache;
public MemoryContainer _memoryContainer;
public ProductsController(StoreContext storeContext, IMemoryCache memoryCache, MemoryContainer memoryContainer)
{
_storeContext = storeContext;
_memoryCache = memoryCache;
_memoryContainer = memoryContainer;
}
public async Task<IActionResult> Details(int? id)
{
var test = _memoryCache.Get<DbSet<ProductTypes>>("ProductTypesKey");
var test2 = _memoryContainer._memoryCache.Get<DbSet<ProductTypes>>("ProductTypesKey2");
I see the following error result in MemoryCache for both, how can this be fixed?
How do to make sure MemoryCache will get/store correctly with DbContext, regardless of going from Controller to Controller?
"Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.\r\nObject name: 'StoreContext'."
Other Code:
public class MemoryContainer
{
public IMemoryCache _memoryCache { get; set; }
public MemoryContainer(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
}
Startup.cs
services.AddMemoryCache();
services.AddSingleton<MemoryContainer>();
Other Resources:
MVC Net Core Pass MemoryCache to All Controllers
DbSet<T> implements IQueryable so those ProductTypes aren't really inside your application until materialized.
A simple ToList() will do the job:
var productTypes = storeContext.ProductTypes.ToList();
Then, inside the other controller:
var test = _memoryCache.Get<List<ProductTypes>>("ProductTypesKey");
Calling ToList() on a DbSet without any filters isn't the best approach in all scenarios because it will load the whole sql table behind the DbSet. However, I guess this is kind of what you are trying to do, just make sure that you never do this on a table with a lot of data.
Note: you are seeing the data in debug window, because expanding IQueryable results actually performs sql queries.
If i have a controller that receives and processes the action selected by the user, and then want to use another controller to store all database related logic, what is the correct way to connect these controllers while allowing the 2nd controller to interact with the database context.
At the moment I have it working with creating a database context in the first controller and then parsing that to the database controller when I connect the two using DI, but hopefully someone could show me the correct way to do this.
public class TestController : Controller
{
private readonly DatabaseContext context;
private Database.UserController userDatabaseController;
public TestController(DatabaseContext db)
{
context = db;
userDatabaseController = new Database.UserController(context);
}
}
database controller
public class UserController : Controller
{
private readonly DatabaseContext context;
public UserController(DatabaseContext ctx)
{
context = ctx;
}
public bool RegisterUser(Models.DatabaseModels.UserModel model)
{
try
{
context.Users.Add(model);
context.SaveChanges();
return true;
}
catch (Exception e)
{
return false;
}
}
}
startup.cs
services.AddDbContext<DatabaseContext>
(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
databasecontext
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options)
: base(options)
{ }
public DbSet<DatabaseModels.UserModel> Users { get; set; }
}
The "correct" way is: you don't. A controller should never directly call into another controller. You can redirect to a new route that maps to a different controller, but that's it. If you have some common logic that needs to be shared, then that should be factored out into a completely different class that both controllers can utilize.
If you're finding that you need to call Controller methods from another Controller, you probably need to refactor your code. Controllers should have very little logic in them, which usually just involves calling a Service layer and then constructing a ViewModel from the data.
My advice would be to do some reading on the Service Layer pattern and the Repository pattern (sometimes called the Manager pattern).
I was following this tutorial:
http://blog.johanneshoppe.de/2010/10/walkthrough-ado-net-unit-testable-repository-generator/
And I had this issue:
MVC3 & EF. Interface for TDD
However, now I have my interfaces setup (I am not using ninject due to project restrictions)
I am getting a null error here;
`Public partial class MyEntitiesRepository : MyEntitiesRepository
{
public IEnumerable<userdetails> getAlluserDetails()
{
return this.Context.userDetails.ToList();
}`
Context is null. I am using the exact same structure as the tutorial.
The header in my MVC controller that calls this is:
`
[HandleError]
public class HomeController : Controller
{
private MyEntitiesRepository _repository;
...
...
public HomeController() : this(new externalEntities(), new MyEntitiesRepository ()){}
public HomeController(externalEntities external, MyEntitiesRepository repository)
{
_repository = repository;
_ContextExt = external;
}
`
EDIT:
context is from:
[System.CodeDom.Compiler.GeneratedCode("ADO.NET Unit Testable Repository Generator", "0.5")]
public partial class MyEntitiesRepository
{
/// <summary>
/// Gets or sets the specialised object context
/// </summary>
/// <value>object context</value>
#if !DO_NOT_USE_UNITY
[Dependency]
#endif
public IMyEntities Context { get; set; }
}
}
I am guessing that in the example they pass the Context in the constructor. They can do this because they are using dependency injection and it will create that instance for you. Since you are not using Ninject, you will more than likely need to construct this Context yourself.
If you are unable to use Ninject or any other IoC container then you need to do a better job convincing your bosses to let you. If they still don't let you then you can do poor man's dependency injection I suppose:
public class MyEntitiesRepository
{
private MyDbContext context;
public MyEntitiesRepository() : this(new MyDbContext())
{ }
public MyEntitiesRepository(MyDbContext context)
{
this.context = context;
}
}
It's better than nothing I suppose?
Seeing the edit (the Dependency attribute) I guess the project restrictions you are referring to are that instead of Ninject you are to use Microsoft's Unity.
Now you can solve your problem using or not using Unity. To start with the latter: Adjust your HomeController and MyEntitiesRepository classes a little:
public HomeController() :
this(new externalEntities(),
new MyEntitiesRepository (new MyEntities()))
{
}
public HomeController(externalEntities external, MyEntitiesRepository repository)
{
_repository = repository;
_ContextExt = external;
}
public partial class MyEntitiesRepository
{
public MyEntitiesRepository(IMyEntities context)
{
this.Context = context;
}
public IMyEntities Context { get; private set; }
}
Here I made the assumption that you have a class MyEntities implementing the interface IMyEntities.
You could also use Unity. To get to know that framework a little better you could start at MSDN. I don't have any experience with Unity, but some things I noticied are that you need to create MyEntityRepository using a UnityContainer object:
IUnityContainer container = new UnityContainer();
...
MyEntityRepository repository = container.Resolve<MyEntityRepository>();
Before that works you need to register a mapping of MyEntities to IMyEntities:
container.RegisterType<IMyEntities, MyEntities>();
If you choose to try Unity I suggest you give it a try and ask a new question if you get stuck.