Entity tracking in multiple contexts - c#

in a typical MVC3 app with an EF model, each controller instantiates its own copy of the model container. this means that if I were to create a class in a different file and it needed access to the model, it would need to instantiate its own container.
Consider the following:
namespace X.Web.Controllers
{
public class TestController : Controller
{
EFContainer db = new EFContainer();
public ActionResult Whatever()
{
User u = db.Users.Find(3);
...
}
if I wanted to abstract my fetching of a user in a class Auth then it would have to instantiate its own db since it doesn't have access to the controller's -- all fine until the controller wants to make changes to the returned object:
public ActionResult Whatever()
{
User u = Auth.GetUser();
u.Name = "ekkis";
db.SaveChanges();
...
}
since the user at that point belongs to a different context... so either I have to share my db with Auth or perhaps I'd have to do a moronic double-lookup:
public ActionResult Whatever()
{
int id = Auth.GetUserId();
User u = db.Users.Find(id);
u.Name = "ekkis";
db.SaveChanges();
...
}
what is the recommended way of dealing with this?

Why don't you pass in your model / EF context via constructor injection into your Auth class? That seems to be the most reasonable way (same applies to your controller actually once you have an IOC container set up).
public class Auth
{
public Auth(EFContainer db)
{
//...
}
}
Ideally you would also make this work based on an abstract interface so you can test Auth independently of EF.

Related

Use different Database, with different DBcontext, and connect to it according to different URL request

we actually have an online app, which include a ChatBot.
This chatbot can show us different informations from a database, like
Me : "What is the estimated budget for Projcet1"
Chat bot : "The estimated budget is 50k € for Project1"
Fine. Now, we will deploy this application for different companies. And each company have a different Database.
For those different Database (2 at the moment) I created 2 context. I have 2 models to interact with those database, and I can add Controller for every models.
services.AddDbContext<data_firstContext>(options =>
{
options.UseMySQL(Configuration.GetConnectionString("Database1"));
});
services.AddDbContext<data_secondContext>(options =>
{
options.UseMySQL(Configuration.GetConnectionString("Database2"));
});
The question now is, how can I chose to connect either to the first database, or to the second database.
I can send from the front-end, the URL header of the App or something like that, or just catch it somewhere I guess, but how can I switch from one to an other depending on the Url request, or something I give in parameter from the front-end.
I looked online but none of what I've tried actually work. If someone have an idea, it'd really help me !
Thanks
Assuming you are using EF core, you don't have to pass any parameter as such from the UI. You can do that directly by defining different DBContexts and use them in the constructors of your services by using Dependency Injection. For eg:
Startup.cs:
services.AddDbContext<data_firstContext>(options =>
{
options.UseMySQL(Configuration.GetConnectionString("Database1"));
});
services.AddDbContext<data_secondContext>(options =>
{
options.UseMySQL(Configuration.GetConnectionString("Database2"));
});
FirstDbContext.cs:
public class FirstDbContext : DbContext
{
public DbSet<ViewModels.Tower> Towers { get; set; }
public DbSet<ViewModels.Motherboard> Motherboards { get; set; }
public FirstDbContext(DbContextOptions<FirstDbContext> options)
: base(options)
{
}
}
AdminController.cs:
[Authorize(Policy = "RequireAdminRole")]
public class AdminController : Controller
{
private readonly FirstDbContext _context;
public AdminController(FirstDbContext context)
{
_context = context;
}
public IActionResult Index()
{
return View();
}
public IActionResult Towers()
{
var model = _context.Towers.ToList();
return View(model);
}
}
Check this out: Entity Framework Core Using multiple DbContexts

Share my dbContext with all my repository/service class?

I'm working on a classic .Net Framework Web API solution.
I have 3 layers. Let's call them
MVC - with POST, GET, UPDATE, DELETE controllers.
BIZZ - for business with my service class. My service class are king of repositories with CREATE, READ, UPDATE, DELETE and specific methods.
DATA - with POCO and definition of DB context.
I will not develop the EF layer. It is a classic Entity Framework project with POCO.Here is a sample of a Service and with BaseService class
public abstract class Service : IDisposable
{
protected DbContext dbContext = new DbContext();
public void Dispose()
{
dbContext.Dispose();
}
}
Then I have a cart service and a order service. They are similar in their structure so I will only write the code useful for this example.
public class CartService : Service
{
public Cart Create(Cart cart)
{
// Create the cart
}
public Cart Read(Guid id)
{
// Read
}
public Cart Update(Cart cart)
{
// I do some check first then
}
public void Delete(Cart cart)
{
// Delete
}
public void Checkout(Cart cart)
{
// Validation of cart removed in this example
dbContext.Cart.Attach(cart);
cart.DateCheckout = DateTime.UtcNow;
dbContext.Entry(cart).State = EntityState.Modified; // I think this line can be removed
dbContext.SaveChanges();
using (var orderService = new OrderService())
{
foreach (var order in cart.Orders)
{
order.DateCheckout = cart.DateCheckout;
order.Status = OrderStatus.PD; // pending
orderService.Update(order);
}
}
}
}
public class OrderService : Service
{
public Cart Create(Cart cart)
{
// Create the cart
}
public Cart Read(Guid id)
{
// Read
}
public Cart Update(Cart cart)
{
dbContext.Entry(order).State = EntityState.Modified;
dbContext.SaveChanges();
// More process here...
return order;
}
public void Delete(Cart cart)
{
// Delete
}
}
So, I have a service, cart service, that call another service, order service. I must work like this because I cannot simply accept the cart and all orders in it as it is. When I save a new order or update an existing order I must create a record in some other tables in other databases. The code is not in my example. So, I repeat I have a service that call another service and then I have 2 dbContext. At best this just create 2 context in memory, at worst this create exception. Exception like you cannot attach an entity to 2 contexts or this entity is not in context.
Well, I would like all my service use the same context. I suppose you will al tell me to use Dependency Injection. Yes, well ok but I don't want, each time I create a new service have to pass the context. I don't want to have to do that:
public void Checkout(Cart cart)
{
// ...
using (var orderService = new OrderService(dbContext))
{
// ...
}
}
I would like to do something that impact my base service only if possible. A singleton maybe... At this point I can see your face. Yes I know Singleton are soo bad. Yes but i'm doing a IIS Web API. Each request is a new instance. I don't care about the impact of the singleton. And I can load my database by changing the connection string in config file so the benefit of DI is there already. Well, I also know it is possible to have singleton with DI. I just don't know how.
So, what can I do to be sure I share my dbContext with all my services?
Disclaimer: This example is not intended to be a "good" one and certainly does not follow best practices, but faced with an existing legacy code base which from your example already suffers from a number of questionable practices, this should get you past the multiple context issues.
Essentially if you're not already using a IoC Container to perform dependency injection then what you need is to introduce a unit of work to manage the scope of a DbContext where your base Service class provides a DbContext provided by the unit of work. (Essentially a DbContext Registry)
For the unit of work and assuming EF6 I would recommend Mehdime's DbContextScope which is available as a NuGet package. Alternatively you can find the source code on Github and implement something similar without too much trouble. I like this pattern because it leverages the CallContext to serve as the communication layer between the ContextScope (Unit of Work) created by the DbContextScopeFactory and the AmbientDbContextScope. This will probably take a little time to get your head around but it injects very nicely into legacy applications where you want to leverage the Unit of Work and don't have dependency injection.
What it would look like:
In your Service class you would introduce the AmbientDbContextLocator to resolve your DbContext:
private readonly IAmbientDbContextLocator _contextLocator = new AmbientDbContextLocator();
protected DbContext DbContext
{
get { return _contextLocator.Get<DbContext>(); }
}
And that's it. Later as you refactor to accommodate Dependency injection, just inject the AmbientDbContextLocator instead of 'new'ing it up.
Then, in your web API controllers where you are using your services, (not the services themselves) you need to add the DbContextScopeFactory instance..
private readonly IDbContextScopeFactory _contextScopeFactory = new DbContextScopeFactory();
Lastly, in your API methods, when you want to call your services, you need to simply use the ContextScopeFactory to create a context scope. The AmbientDbContextLocators will retrieve the DbContext from this context scope. The context scope you create with the factory will be done in a using block to ensure your contexts are disposed. So, using your Checkout method as an example, it would look like:
In your Web API [HttpPost] Checkout() method:
using (var contextScope = _contextScopeFactory.Create())
{
using(var service = new CartService())
{
service.Checkout();
}
contextScope.SaveChanges();
}
Your cart service Checkout method would remain relatively unchanged, only instead of accessing dbContext as a variable (new DbContext()) it will access the DbContext property which gets the context through the context locator.
The Services can continue to call DbContext.SaveChanges(), but this isn't necessary and the changes will not be committed to the DB until the contextScope.SaveChanges() is called. Each service will have its own instance of the Context Locator rather than the DbContext and these will be dependent on you defining a ContextScope to function. If you call a Service method that tries to access the DbContext without being within a using (var contextScope = _contextScopeFactory.Create()) block you will receive an error. This way all of your service calls, even nested service calls (CartService calls OrderService) will be interacting with the same DbContext instance.
Even if you just want to read data, you can leverage a slightly faster DbContext using _contextScopeFactory.CreateReadOnly() which will help guard against unexpected/disallowed calls to SaveChanges().
When using the ASP.NET Core stack, the tutorial for using EF with it defaults to using DI to provide your DB context, just not with a service layer. That said, it actually does the right thing for this out of the box. I'll give a brief rundown of the bare minimum necessary for this to work, using whatever the latest versions of ASP.NET Core Web API and EF Core were on NuGet at the time of writing.
First, let's get the boilerplate out of the way, starting with the model:
Models.cs
public class ShopContext : DbContext
{
public ShopContext(DbContextOptions options) : base(options) {}
// We add a GUID here so we're able to tell it's the same object later.
public string Id { get; } = Guid.NewGuid().ToString();
public DbSet<Cart> Carts { get; set; }
public DbSet<Order> Orders { get; set; }
}
public class Cart
{
public string Id { get; set; }
public string Name { get; set; }
}
public class Order
{
public string Id { get; set; }
public string Name { get; set; }
}
Then some bare-bones services:
Services.cs
public class CartService
{
ShopContext _ctx;
public CartService(ShopContext ctx)
{
_ctx = ctx;
Console.WriteLine($"Context in CartService: {ctx.Id}");
}
public async Task<List<Cart>> List() => await _ctx.Carts.ToListAsync();
public async Task<Cart> Create(string name)
{
return (await _ctx.Carts.AddAsync(new Cart {Name = name})).Entity;
}
}
public class OrderService
{
ShopContext _ctx;
public OrderService(ShopContext ctx)
{
_ctx = ctx;
Console.WriteLine($"Context in OrderService: {ctx.Id}");
}
public async Task<List<Order>> List() => await _ctx.Orders.ToListAsync();
public async Task<Order> Create(string name)
{
return (await _ctx.Orders.AddAsync(new Order {Name = name})).Entity;
}
}
The only notable things here are: the context comes in as a constructor parameter as God intended, and we log the ID of the context to verify when it gets created with what.
Then our controller:
ShopController.cs
[ApiController]
[Route("[controller]")]
public class ShopController : ControllerBase
{
ShopContext _ctx;
CartService _cart;
OrderService _order;
public ShopController(ShopContext ctx, CartService cart, OrderService order)
{
Console.WriteLine($"Context in ShopController: {ctx.Id}");
_ctx = ctx;
_cart = cart;
_order = order;
}
[HttpGet]
public async Task<IEnumerable<string>> Get()
{
var carts = await _cart.List();
var orders = await _order.List();
return (from c in carts select c.Name).Concat(from o in orders select o.Name);
}
[HttpPost]
public async Task Post(string name)
{
await _cart.Create(name);
await _order.Create(name);
await _ctx.SaveChangesAsync();
}
}
As above, we take the context as a constructor parameter to triple-check it's what it should be; we also need it to call SaveChanges at the end of an operation. (You can refactor this out of controllers if you want to, but they'll work just fine as units of work for now.)
The part that ties this together is the DI configuration:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
// Use whichever provider you have here, this is where you grab a connection string from the app configuration.
services.AddDbContext<ShopContext>(options =>
options.UseInMemoryDatabase("Initrode"));
services.AddScoped<CartService>();
services.AddScoped<OrderService>();
}
AddDbContext() defaults to registering a DbContext to be created per-request by the container. Web API provides the AddControllers method that puts those into the DI container, and we also register our services manually.
The rest of Startup.cs I've left as-is.
Starting this up and opening https://localhost:5001/shop should log something like:
Context in CartService: b213966e-35f2-4cc9-83d1-98a5614742a3
Context in OrderService: b213966e-35f2-4cc9-83d1-98a5614742a3
Context in ShopController: b213966e-35f2-4cc9-83d1-98a5614742a3
with the same GUID for all three lines in a request, but a different GUID between requests.
A little additional explanation of what goes on above:
Registering a component in a container (using Add() and such above) means telling the container those components exist and that it should create them for you when asked, as well as what identifiers they're available under and how to create them. The defaults for this are more or less "make the component available as its class, and create it by calling its one public constructor, passing other registered components into it" - the container looks at the constructor signature to figure this out.
"Scoped" in an ASP.NET Core app means "per-request." I think in this case one could also use services with a transient lifetime - a new one created every time it's needed, but they'll still get the same DbContext as long as they're created while handling the same request. Which one to do is a design consideration; the main constraint is that you can't inject shorter-lived components into longer-lived components without having to use more complex techniques, which is why I favour having all components as short-lived as possible. In other words, I only make things longer-lived when they actually hold some state that needs to live for that time, while also doing that as sparingly as possible because state bad. (Just recently I had to refactor an unfortunate design where my services were singletons, but I wanted my repositories to be per-request so as to be able to inject the currently logged in user's information into the repository to be able to automatically add the "created by" and "updated by" fields.)
You'll note that with support for doing things this way being built-in to both ASP.NET Core and EF Core, there's actually very little extra code involved. Also, the only thing needed to go from "injecting a context into your controllers" (as the tutorial does) to "injecting a context into services that you use from your controllers" is adding the services into DI - since the controller and context are already under DI, anything new you add can be injected into them and vice versa.
This should give you a quick introduction into how to make things "just work" and shows you the basic use case of a DI container: you declaratively tell it or it infers "this is an X", "this is an Y", "this is a Z and it needs to be created using an X and a Y"; then when you ask the container to give you a Z, it will automagically first create an X and Y, then create Z with them. They also manage the scope and lifetime of these objects, i.e. only create one of a type for an API request. Beyond that it's a question of experience with them and familiarity with a given container - say Ninject and Autofac are much more powerful than the built-in one - but it's variations on the same idea of declaratively describing how to create an object possibly using other objects (its dependencies) and having the container "figure out" how to wire things together.

How to get User ID inside a separate assembly

I'm working on an application which has two projects:
Core - houses the data access layer using repository pattern and domain-driven design
UI - using ASP.Net MVC. Currently, I am able to get the current logged in user's info(id, name, etc..) inside the UI controller via the User property like this:
using Microsoft.AspNet.Identity;
public class ExamController : Controller
{
IExaminationRepository _repository;
public ExamController()
{
_repository = RepositoryFactory.Get<IExaminationRepository>();
}
[HttpPost]
[Authorize(Roles = "Examiner")]
public ActionResult Create(ExamViewModel viewModel)
{
try
{
ExaminationDomain domain = Mapper.Map<ExamViewModel, ExaminationDomain>(viewModel);
//TODO: Move this to the repository
domain.AuthorId = User.Identity.GetUserId();
_repository.Add(domain);
return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
I would like to move the line: domain.AuthorId = User.Identity.GetUserId(); to my repository concrete implementation like this:
using Microsoft.AspNet.Identity;
using System.Security.Principal;
internal class ExaminationRepository
{
public DBEntities context;
public IPrincipal User;
public ExaminationRepository(DBEntities context)
{
this.context = context;
//I'd like to instantiate User property here:
this.User = "not sure what to instantiate with";
}
public void Add(ExaminationDomain domain)
{
Examination newExam = Mapper.Map<ExaminationDomain, Examination>(domain);
newExam.AuthorId = User.Identity.GetUserId();
newExam.CreatedBy = User.Identity.Name;
newExam.CreatedDate = DateTime.Now;
context.Examinations.Add(newExam);
context.SaveChanges();
}
But I am not sure what to instantiate the User property to in the constructor. I've read some suggestions to use WindowsIdentity.GetCurrent().User instead of creating a user property but this doesn't contain the user id, only user name.
Any other suggestions on getting user info?
I'd really appreciate some help on this..
Thanks,
I would decouple your repository from the httpcontext with a custom manager. For example I have a interface called IAUthenticationManager
public interface IAUthenticationManager
{
string CurrentUserId();
bool HasCurrentUserRole(string roleName),
}
Easy to test and fully decoupled.
This won't work easily since the repository can be used in many different contexts, even such contexts where user is not set. If you create a concrete dependency in your constructor, your repository will no longer be an independent data provider.
For example, referencing
HttpContext.Current.User.Identity
directly would create a dependency to a web context and the repository would be unusable in non-web contexts.
The best you could do is just to let the repository client provide this:
public void Add(ExaminationDomain domain, IPrincipal principal)
{
Examination newExam = Mapper.Map<ExaminationDomain, Examination>(domain);
newExam.AuthorId = principal.Identity.GetUserId();
newExam.CreatedBy = principal.Identity.Name;
newExam.CreatedDate = DateTime.Now;
context.Examinations.Add(newExam);
context.SaveChanges();
}
or (which could be possible)
public ExaminationRepository(DBEntities context, IPrincipal user)
{
this.context = context;
this.user = user;
}
The latter case could still be correctly resolved by an IoC container if you tell the container how to resolve the dependency.
In a web context, you could set the container to resolve it to HttpContext.Current.User.
In a non-web context, you could set the container to resolve it to WindowsIdentity.GetCurrent().User.
use HttpContext class witch is a singleton class:
first add a using to Microsoft.AspNet.Identity;
and then you can do some thing like this:
HttpContext.Current.User.Identity.GetUserId();
since GetUserId is an extension method you have a reference to Microsoft.AspNet.Identity
but if you need to access user information in several places of you app I suggest to have a wrapper class with properties that you need and instantiate when user logs in then store it in session variable this way you have two benefits:
1- you don't need to query db to get username, email etc.. on each user info usage across the app.
2- you don't need assembly that your repository lives to aspnet identity.
You need to instantiate this.User with identity information of current thread:
this.User = System.Threading.Thread.CurrentPrincipal;
var currentIdentity = (System.Security.Claims.ClaimsIdentity)User.Identity;
var userId = currentIdentity.Claims
.Where(p => p.Type.EndsWith("nameidentifier")).Single().Value;
Note that the type of CurrentPrincipal.Identity is an IIdentity. You can cast it to System.Security.Claims.ClaimsIdentity, which contains a property named Claims. This property contains all your claims, including userid and 3rd party token (e.g. Facebook token).
To retrieve UserId, find a claims with Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier"

Pattern for doing authorization in repository layer of MVC application

I have a Windows authenticated MVC application with a repository layer. All interaction by the controller with the database is done through the repository. Each controller has a reference to the repository:
public class PostController : Controller
{
private Repository db = new Repository();
[HttpPost]
public ActionResult DeletePost(int id)
{
// Authorize that the user is allowed to delete this post...
db.DeletePost(id);
}
}
My question is whether there is a good way to move my authorization logic into the repository layer. I'd like the Repository.DeletePost() function to refuse to delete posts that were not created by the authenticated user. The problem is that my repository does not know who the authenticated user is. The controller knows (via Controller.User).
Passing the Controller.User into the Repository constructor doesn't work, because the Controller.User is apparently not defined at the time when the constructor is called.
How can I inform the Repository of who the authenticated user is? Would it be best to just construct the Repository within each action? Or is it a bad idea to handle it in the repository layer?
Or is it a bad idea to handle it in the repository layer?
I think the Controller is a better place for your authorization. Let the repository be a gateway to the data and the controller be a gatekeeper to your application. I'd expect to see authorization/authentication logic as early in the life-cycle as possible.
Just do something like:
db.DeletePostForUser(id, User.Identity.UserId);
Then in your repository:
public void DeletePostForUser(int id, int userId)
{
var post = context.Posts.SingleOrDefault(m => m.PostId == id && m.User.UserId == userId)
if (post != null)
{
context.Posts.Remove(post);
context.SaveChanges();
}
}
Good suggestions from both #BigDaddy and #ChrisPratt.
I ended up solving this by creating a base controller, similar to this answer. My base controller class looks like:
public class BaseController : Controller
{
private ILog _log;
private Repository _db;
protected Repository Db
{
get
{
return _db ?? (_db = new Repository(User));
}
}
protected ILog Log
{
get
{
return _log ?? (_log = LogManager.GetLogger(this.GetType()));
}
}
}
All of my controllers inherit from this class, and have built-in access to a lazy-loaded Repository that has a reference to the currently authenticated user.

Centralizing CloudStorageAccount and TableServiceContext instances

In an ASP.NET MVC 3 Web Role, I have realized that I have been writing the below code a lot:
var account =
CloudStorageAccount.Parse(
RoleEnvironment.GetConfigurationSettingValue("DataConnectionString")
);
var ctx =
account.CreateCloudTableClient().GetDataServiceContext();
So, I decided to Centralize this for the entire ASP.NET MVC application and I created the below class with static properties:
internal class WindowsAzureStorageContext {
public static CloudStorageAccount StorageAccount {
get {
return
CloudStorageAccount.Parse(
RoleEnvironment.GetConfigurationSettingValue("DataConnectionString")
);
}
}
public static TableServiceContext TableServiceCtx {
get {
return
StorageAccount.CreateCloudTableClient().GetDataServiceContext();
}
}
}
And, I am using this as below inside my controllers:
public class HomeController : Controller {
private readonly TableServiceContext ctx =
WindowsAzureStorageContext.TableServiceCtx;
public ViewResult Index() {
var model = ctx.CreateQuery<TaskEntity>(Constants.TASKS_TABLE).
Where(x => x.PartitionKey == string.Empty);
return View(model);
}
public ViewResult Create() {
return View();
}
[ActionName("Create")]
[HttpPost, ValidateAntiForgeryToken]
public ViewResult Create_post(TaskEntity taskEntity) {
ctx.AddObject(Constants.TASKS_TABLE, new TaskEntity(taskEntity.Task));
ctx.SaveChangesWithRetries();
return RedirectToAction("Index");
}
}
I know that this is not a unit test friendly and I should reach out that TableServiceContext instance through a interface by DI but when I do that, I consider using this WindowsAzureStorageContext class as well to get an instance of TableServiceContext class.
Is this a good practice? Would it hurt me in any point because I am using the same class for the entire application life-cycle?
Is there any know pattern to do this?
I don't see any problem with doing that. Looks like a nice clean way to do it. I don't know of a known pattern to do this but was just thinking there should be yesterday.
I think you can use repository pattern for a generic data context, with a generic interface on top of it. Not sure if it helps but you can refer my blog http://blogs.shaunxu.me/archive/2010/03/15/azure-ndash-part-5-ndash-repository-pattern-for-table-service.aspx
I don't believe that there is any shared state between instances of the context. With that said, the transaction time for the controller to execute is non-trivial. The longer you hold onto the context, the more likely you are to get conflicts. I've found that one way to keep conflicts and overlaps to a minimum is to keep the load/change/save cycle as short as possible.
Erick

Categories