I have an application where multiple users can login. For that I have a kind of session object that stores a DBContext object. But the problem is, that the cached DBContext stores only the data of the logged user. When another user are also logged in, then the cached data is maybe older because it will be changed by the second user.
Is there a conceptional way to handle this. Instead of caching the DBContext object, I can create it every time I do an database request. Is this the correct way or is there a kind of event to catch to know that the database content is changed?
You should not be caching the DbContext object in any way. DbContext maintains state internally for multiple users automatically.
You create a new context when you open a controller to respond to a user request for data. In Entity Framework 6 this would look like
public class FeedItemController : ApiController
{
private LynxFeedAPI_Context db = new LynxFeedAPI_Context();
// GET api/FeedItem
public IQueryable<FeedItem> GetFeedItems()
{
return db.FeedItems;
}
It is done differently in EF 7 where Startup.cs is used to setup the Dependency Injection
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Uncomment the following line to add Web API services which makes it easier to port Web API 2 controllers.
// You will also need to add the Microsoft.AspNet.Mvc.WebApiCompatShim package to the 'dependencies' section of project.json.
// services.AddWebApiConventions();
services.Configure<AppSettings>(configuration.GetConfigurationSection("AppSettings"));
// Add EF services to the services container.
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"]));
services.AddSingleton<IApplicationDbContext, ApplicationDbContext>();
services.AddSingleton<IProposalDataRepository, ProposalDataRepository>();
services.AddSingleton<IPositionDataRepository, PositionDataRepository>();
services.AddSingleton<IMandatoryReqDataRepository, MandatoryReqDataRepository>();
services.AddSingleton<IRatedReqDataRepository, RatedReqDataRepository>();
}
and is used by the controller
public class ProposalController : Controller
{
private readonly IProposalDataRepository repProposal;
public ProposalController(IProposalDataRepository repository) {
repProposal = repository;
}
[HttpGet]
public IEnumerable<Proposal> GetAll()
{
return repProposal.SelectAll();
}
where it is not necessary to ever make a call to create a new DbContext
Related
I want to store my data in memory cache and access it from another controller ,what i have done is
MemoryCache myCache = new MemoryCache(new MemoryCacheOptions());
//responseQID is my data i have already seen it contains data
myCache.Set("QIDresponse", responseQID);
in another controller i want to get this data :
MemoryCache myCache = new MemoryCache(new MemoryCacheOptions());
var inmemory= myCache.Get("QIDresponse");
inmemory is null, where am I doing wrong?
A common memory cache is provided by ASP.NET Core. You just need to inject it into each controller or other service where you want to access it. See the docs.
You need to ensure memory caching is registered, note this from the docs:
For most apps, IMemoryCache is enabled. For example, calling AddMvc,
AddControllersWithViews, AddRazorPages,
AddMvcCore().AddRazorViewEngine, and many other Add{Service} methods
in ConfigureServices, enables IMemoryCache. For apps that are not
calling one of the preceding Add{Service} methods, it may be necessary
to call AddMemoryCache in ConfigureServices.
To ensure this is the case, you add the registration within Startup.cs:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
...
Once memory caching is registered in Startup.cs, you can inject into any controller like this:
public class HomeController : Controller
{
private IMemoryCache _cache;
public HomeController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
...
Then use this _cache instance rather than using new to create one.
I noticed the project template for MVC when using individual user accounts puts a few objects in the current Owin context (in App_Start/Startup.Auth.cs):
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
It looks like this is to access the database for Identity features. My understanding is that a single instance of ApplicationDbContext is created per request and re-used through the entire pipeline. Would it be beneficial to do the same with my own entity framework DbContexts?
For example I created a new file in App_Start/Startup.Data.cs:
public partial class Startup
{
public void ConfigureData(IAppBuilder app)
{
app.CreatePerOwinContext(CreateParkingEntities);
}
protected ParkingEntities CreateParkingEntities()
{
return new ParkingEntities();
}
}
Then in Startup.cs:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
ConfigureData(app);
}
}
Then I can use the context in my controllers:
private ParkingEntities _db;
public ParkingEntities DbContext
{
get
{
return _db ?? HttpContext.GetOwinContext().Get<ParkingEntities>();
}
private set
{
_db = value;
}
}
I would think if this was standard practice, the entity framework would have some scaffolding for this, but it just creates an instance at the controller level. Is it safe to assume that if DbContext is only accessed from that controller then it would be functionally equivalent to the above implementation and placing it in the Owin pipeline is overkill?
I suppose another use of this approach is a single initialization point for the DbContext, if additional setup is needed.
DbContext is designed to be lightweight, so there isn't much cost to creating a new instance every time you need one. The main costs are re-creating the connection and garbage-collecting all of the instances you are creating.
Note that if you use the Find() method on the DbSets, it will cache the result, so next time you ask for it, it doesn't need to go to the database. If you re-use the same context, you can take advantage of that cache, but if you create a new context, you lose the cache.
If you store the DbContext in an instance member of the controller, then it will only get used for one request, since a new instance of the controller will be created for each request. Just make sure you DON'T put it in a static member - I expect that you would get all kinds of race conditions if you do that.
The main approach to use EF db-context is simple: connect to the db-context, get your data and forget about this db-context object. EF context is IDisposable and closes the connection after dispose (don't forget about using(){}). It means, that you can create EF data-context anywhere in your application when it needs a db-connection. You can create new db-context right inside the controller-action if they are using your db. Everything depends on what is the architecture of your application. If you are sure, that all requests to your app will need many db-operations, I think your solution with db-context per owin-context can be useful.
I've just started work on an experimental project using ASP.net 5, MVC6 and Entity Framework 7. I have ASP.Net Identity working fine, but then tried to add some of my own data to the DbContext and hit this problem. EF7 reports:
An unhandled exception occurred while processing the request.
InvalidOperationException: No database providers are configured.
Configure a database provider by overriding OnConfiguring in your
DbContext class or in the AddDbContext method when setting up
services.
Microsoft.Data.Entity.Internal.DatabaseProviderSelector.SelectServices(ServiceProviderSource
providerSource) Stack Query Cookies Headers
InvalidOperationException: No database providers are configured.
Configure a database provider by overriding OnConfiguring in your
DbContext class or in the AddDbContext method when setting up
services.
Here's my configuration method:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.AddTransient<ApplicationUnitOfWork>(instance => new ApplicationUnitOfWork());
}
ApplicationUnitOfWork is a façade over EF7 to reduce tight coupling. Here's the whole thing so far:
public class ApplicationUnitOfWork : IUnitOfWork
{
readonly ApplicationDbContext dbContext;
public ApplicationUnitOfWork()
{
dbContext = new ApplicationDbContext();
Products = new ProductRepository(dbContext);
}
public void Dispose() { dbContext.Dispose(); }
public IUserRepository Users { get; }
public IRepository<SoftwareProduct> Products { get; }
public async Task CommitAsync() { await dbContext.SaveChangesAsync(); }
public void Cancel() { throw new NotImplementedException(); }
}
Now when I run the web application, I am able to register a user account and log in and EF creates the database, creates the Identity tables and populates them with user data. The products table is also created - so clearly at some level EF is able to use the ApplicationDbContext and find a provider which it uses to create the database and schema.
However when I try to access my Products controller, which uses the ProductsRepository, EF complains - even though ApplicationDbContext is used in both cases and was used to create the database!
So how come? What's special about ASP.net Identity that it can somehow obtain a provider, but my own code can't? What "magic incantation" am I missing?
its because you are newing it up instead of having it injected for you. The one you are newing up hasn't been configured.
You should change your class to have it passed into the constructor.
DbContext has more than one constructor and you are using the empty one which doesn't configure it.
Better to let it be injected by making your constructor like this:
public ApplicationUnitOfWork(ApplicationDbContext context)
{
dbContext = context;
Products = new ProductRepository(dbContext);
}
your code shows that ApplicationDbContext has been registered with the DI, Identity is using the injected one but you are not since you newed it up yourself with the parameterless constructor
you should also register your ApplicationUnitOfWork so it can be injected:
services.AddScoped<ApplicationUnitOfWork, ApplicationUnitOfWork>();
I'm in a situation where the classic functionality of vnext's DI container is not enough to provide me with the correct functionality. Let's say I have a DataService that gets data from a database like this:
public class DataService : IDataService, IDisposable {
public List<MyObject> GetMyObjects()
{
// do something to fetch the data...
return myObjects;
}
}
I can then register this service in the DI container during the configuration phase in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped(typeof(IDataService), typeof(DataService));
}
This ensures the correct lifecylce of the service (one per request scope), however, I need the service to access a different database when a different request is made. For simplicity reasons, let's say the following scenario applies:
when a request to my Web API is made, the DataService will access the currently logged in user, which contains a claim called Database which contains the information which database to use.
the DataService is then instantiated with the correct database connection.
In order to get the second step to work, I have created a constructor for the DataService like this:
public DataService(IHttpContextAccessor accessor)
{
// get the information from HttpContext
var currentUser = accessor.HttpContext.User;
var databaseClaim = currentUser.Claims.SingleOrDefault(c => c.Type.Equals("Database"));
if (databaseClaim != null)
{
var databaseId = databaseClaim.Value;
// and use this information to create the correct database connection
this.database = new Database(databaseId);
}
}
By using the currently logged in user and his claims, I can ensure that my own authentication middleware takes care of providing the necessary information to prevent attackers from trying to access the wrong database.
Of course adding the IDisposable implementation is required to cleanup any database connections (and gets called correctly using the scope lifecycle).
I can then inject the DataService into a controller like this
public MyController : Controller
{
private IDataService dataService;
public MyController(IDataService dataService)
{
this.dataService = dataService;
}
}
This all works fine so far.
My questions now are:
Is there another way to create the instance other than using the constructor of the DataService? Maybe accessing the object the IServiceCollection provides in a different place other than during the configration phase which runs only once? Maybe using my own OWIN middleware?
Is this method really safe? Could two requests made at the same time accidentally end up with the DataServiceintended for the other request and therefore end up giving out the wrong data?
What you have is fine.
Is there another way to create the instance other than using the constructor of the DataService? Maybe accessing the object the IServiceCollection provides in a different place other than during the configration phase which runs only once? Maybe using my own OWIN middleware?
Not really. You can use delegate registration but it's the same problem.
Is this method really safe?
Yes
Could two requests made at the same time accidentally end up with the DataServiceintended for the other request and therefore end up giving out the wrong data?
Nope. The IHttpContextAcessor uses AsyncLocal (http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html) to provide access to the "current" http context.
Adding Structuremap MVC 5 to an ASP.NET MVC project. I would like to have a singleton of my database connection per request - my controllers would share the same database connection. I am implementing the repository pattern here and need each controller to have a copy of its respective repository. I know this is possible but I think I'm missing or mis-interpretting something wrong.
I have a controller, "Bag," that needs a "IBagRepo"
public class BagController : Controller
{
private readonly IBagRepo repo;
public BagController(IBagRepo repo)
{
this.repo = repo;
}
// actions
}
My first attempt was hooking the singleton database connection in the ControllerConvention, as I assume its called once
public class ControllerConvention : IRegistrationConvention {
public void Process(Type type, Registry registry) {
if (type.CanBeCastTo<Controller>() && !type.IsAbstract) {
// Tried something like
registry.For(type).Singleton().Is(new ApplicationDbContext()); // this
registry.For(type).LifecycleIs(new UniquePerRequestLifecycle());
}
}
}
But it came clear that this isn't the right file to make this change. I went into the registry class that was automatically generated upon installing the nuget package and tried fiddling around with this.
public class DefaultRegistry : Registry {
#region Constructors and Destructors
public DefaultRegistry() {
Scan(
scan => {
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
// httpContext is null if I use the line below
// For<IBagRepo>().Use<BagRepo>().Ctor<ApplicationDbContext>().Is(new ApplicationDbContext());
}
#endregion
}
I haven't seen a problem like this out here yet. Am I passing in the right types within my DefaultRegistry class?
What you're wanting is effectively the default behavior if you had been using the StructureMap.MVC5 nuget: https://www.nuget.org/packages/StructureMap.MVC5/. As long as your DbContext is registered with the default lifecycle, that package is using a nested container per http request which effectively scopes a DbContext to an HTTP request for unit of work scoping.
Different tooling than MVC & EF, but I described similar mechanics for FubuMVC + RavenDb w/ StructureMap in this blog post: http://jeremydmiller.com/2014/11/03/transaction-scoping-in-fubumvc-with-ravendb-and-structuremap/
I ended overriding the default controller factory and not using structuremap