Situation
We have a controller where users can submit any number of E-Mail addresses to invite other (potential) members as friends. If an address is not found in the database, we send an E-Mail message to that user. Since the user does not has to wait for this process to complete in order to continue working this is done asynchronously.
Sending E-Mails can take a long time if servers respond slowly, are down or overloaded. The E-Mail sender should update the database according to the status received from the E-Mail server, so for example setting the friend request into "Error" state, when a permanent failure occurs, for example if the address does not exists. For this purpose, the E-Mail component implements the function SendImmediateAsync(From,To,Subject,Content,Callback,UserArg). After the message has been delivered (or it failed), the Callback is called with certain arguments about the Delivery state.
When it eventually calls the delegate, the DbContext object has already been disposed (since the controller has been too) and I cannot manually create a new one using new ApplicationDbContext() because there is no constructor that accepts a connection string.
Question
How can I write to the database long after the controller has been disposed? I have not yet figured out how to manually create a DbContext object for myself. An object of type ApplicationDbContext is passed to the constructor of the Controller and I hoped I could instantiate one for myself, but the Constructor has no arguments I can supply (for example connection string). I want to avoid to manually create an SQL Connection and assemble INSERT statements manually and would prefer to work with the entity model we have already set up.
Code
The code shows the affected segment only without any error checking for readability.
[Authorize]
public class MembersController : Controller
{
private ApplicationDbContext _context;
public MembersController(ApplicationDbContext context)
{
_context = context;
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Friends()
{
MailHandler.SendImmediateAsync(FROM,TO,SUBJECT,CONTENT,
delegate (Guid G, object any)
{
//THIS IS NOT WORKING BECAUSE _context IS DISPOSED
var ctx = _context;
Guid Result = (Guid)any; //user supplied argument
if (G != Guid.Empty)
{
ctx.MailConfirmation.Add(new MailConfirmation()
{
EntryId = Result,
For = EntryFor.FriendRequest,
Id = G
});
if (G == MailHandler.ErrorGuid)
{
var frq = _context.FriendRequest.SingleOrDefault(m => m.Id == Result);
frq.Status = FriendStatus.Error;
ctx.Update(frq);
}
ctx.SaveChanges();
}
}, req.Id);
//rendering view
}
}
First of all, when you are using EF Core with ASP.NET Core's dependency injection, each DbContext instance is scoped per-request, unless you have specified otherwise in ".AddDbContext". This means you should not attempt to re-use an instance of DbContext after that HTTP request has completed. See https://docs.asp.net/en/latest/fundamentals/dependency-injection.html#service-lifetimes-and-registration-options
DbContextOptions, on the other hand, are singletons and can be re-used across requests.
If you need to close the HTTP request and perform an action afterwards, you'll need to create a new DbContext scope an manage it's lifetime.
Second of all, you can overload DbContext's base constructor and pass in DbContextOptions directly. See https://docs.efproject.net/en/latest/miscellaneous/configuring-dbcontext.html
Together, this is what a solution might look like.
public class MembersController : Controller
{
private DbContextOptions<ApplicationDbContext> _options;
public MembersController(DbContextOptions<ApplicationDbContext> options)
{
_options = options;
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Friends()
{
MailHandler.SendImmediateAsync(FROM,TO,SUBJECT,CONTENT, CreateDelegate(_options) req.Id);
}
private static Action<Guid, object> CreateDelegate(DbContextOptions<ApplicationDbContext> options)
{
return (G, any) =>
{
using (var context = new ApplicationDbContext(options))
{
//do work
context.SaveChanges();
}
};
}
}
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base (options) { }
// the rest of your stuff
}
This assumes, of course, that your "MailHandler" class is properly using concurrency to run the delegate so it doesn't block the thread processing the HTTP request.
Why not just pass the dbContext as the userArgs to your SendImmediateAsync? Then the dbContext will not get disposed and can be passed back when you do the callback. I'm pretty sure that should work.
Related
Our Application (MVC Based) accepts user payment information update request over GET method.Default method used by the application is POST.
Currently if we pass any sensitive information over a GET Method via Querystring, then Request sucessfully works.The reason is that it hits the same Edit Action method in Controller
[HttpGet]
[ValidateRequest(true)]
public ActionResult Edit (parameters)
But what we want is that Any request with sensitive information (like Credit Card etc.) sent over a GET method should be rejected by the application.
Anyhow can we reject GET method through Routing if sensitive information is passed? Please suggest valid approach.
My current route that calls Action is mentioned below:
routes.MapRoute("ChargeInformation", "ChargeInformationt.aspx/{seq}", new { controller = "Payment", action = "Edit", seq = UrlParameter.Optional });
Routing's only responsibility is to map URLs to route values and from route values back to URLs. It is a separate concern than authorizing the request. In fact, the built-in routing extension methods (MapRoute, MapPageRoute, and IgnoreRoute) completely ignore the incoming query string.
For request authorization, MVC has an IAuthorizationFilter interface that you can hook into. You can also (optionally) combine it with an attribute to make it run conditionally on specific action methods, as shown below.
In this case, you just want to reject specific query string key names that are passed into the request. It is unclear what action you wish to take in this case, so I am just setting to HTTP 403 forbidden as an example.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web.Mvc;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class DisallowQueryStringKeysAttribute : FilterAttribute, IAuthorizationFilter
{
private readonly IEnumerable<string> keysSplit;
public DisallowQueryStringKeysAttribute(string keys)
{
this.keysSplit = SplitString(keys);
}
public void OnAuthorization(AuthorizationContext filterContext)
{
var queryStringKeys = filterContext.HttpContext.Request.QueryString.AllKeys;
// If any of the current query string keys overlap with the non-authorized keys
if (queryStringKeys.Intersect(this.keysSplit, StringComparer.OrdinalIgnoreCase).Any())
{
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
// You must set the result property to a handler to run to tell the
// framework that the filter should do something other than run the
// action method. In this case, we just set it to an empty result,
// which implements the null object pattern. You could (if so inclined),
// make a class to set the status code or do something else
// (such as redirect) to indicate that the request is invalid.
filterContext.Result = new EmptyResult();
}
}
private string[] SplitString(string original)
{
if (String.IsNullOrEmpty(original))
{
return new string[0];
}
var split = from piece in original.Split(',')
let trimmed = piece.Trim()
where !String.IsNullOrEmpty(trimmed)
select trimmed;
return split.ToArray();
}
}
Usage
[HttpGet]
[ValidateRequest(true)]
[DisallowQueryStringKeys("creditCard, password")]
public ActionResult Edit (string creditCard, string password)
I am exposing my repository operations through web api. Repository has been implemented with Entity framework and Unit Of Work Pattern. I have many instances of the same database. Each one represent the data of a different Client. Now the issue is how can I set the connection string dynamically through each webapi call? Should I get connection string parameter with each call ? Or I should host web Api per client ?
Based on the information provided, I would use the same controller and look up the connection string rather than rather than hosting separate Web API instances for each client. There would be more complexity in hosting multiple instances and given the only difference indicated is the connection string, I do not think the complexity would be justified.
The first thing we will need to do is determine which client is calling in order to get the appropriate connection string. This could be done with tokens, headers, request data, or routing. Routing is simplest and most generally accessible to clients, so I will demonstrate using it; however, carefully consider your requirements in deciding how you will make the determination.
[Route( "{clientId}" )]
public Foo Get( string clientId ) { /* ... */ }
Next we need to get the right DbContext for the client. We want to keep using DI but that is complicated in that we do not know until after the Controller is created what connection string is needed to construct the object. Therefore, we need to inject some form of factory rather than the object itself. In this case we will represent this as a Func<string, IUnitOfWork> with the understanding it takes the 'clientId' as a string and returns an appropriately instantiated IUnitOfWork. We could alternatively use a named interface for this.
[RoutePrefix("foo")]
public class FooController : ApiController
{
private Func<string, IUnitOfWork> unitOfWorkFactory;
public FooController( Func<string, IUnitOfWork> unitOfWorkFactory )
{
this.unitOfWorkFactory = unitOfWorkFactory;
}
[Route( "{clientId}" )]
public Foo Get( string clientId )
{
var unitOfWork = unitOfWorkFactory(clientId);
// ...
}
}
All that remains is configuring our dependency injection container to provide us that Func<string, IUnitOfWork>. This could vary significantly between implementation. The following is one possible way to do it in Autofac.
protected override void Load( ContainerBuilder builder )
{
// It is expected `MyDbContext` has a constructor that takes the connection string as a parameter
// This registration may need to be tweaked depending on what other constructors you have.
builder.Register<MyDbContext>().ForType<DbContext>().InstancePerRequest();
// It is expected `UnitOfWork`'s constructor takes a `DbContext` as a parameter
builder.RegisterType<UnitOfWork>().ForType<IUnitOfWork>().InstancePerRequest();
builder.Register<Func<string, Bar>>(
c =>
{
var dbContextFactory = c.Resolve<Func<string, DbContext>>();
var unitOfWorkFactory = c.Resolve<Func<DbContext, IUnitOfWork>>();
return clientId =>
{
// You may have injected another type to help with this
var connectionString = GetConnectionStringForClient(clientId);
return unitOfWorkFactory(dbContextFactory(connectionString));
};
});
}
Autofac is used since comments indicates Autofac is currently being used, though similar results would be possible with other containers.
With that the controller should be able to be instantiated and the appropriate connection string will be used for each request.
Example registration based on linked project:
builder.Register<Func<string, IEmployeeService>>(
c =>
{
var dbContextFactory = c.Resolve<Func<string, IMainContext>>();
var unitOfWorkFactory = c.Resolve<Func<IMainContext, IUnitOfWork>>();
var repositoryFactory = c.Resolve<Func<IMainContext, IEmployeeRepository>>();
var serviceFactory = c.Resolve<Func<IUnitOfWork, IEmployeeService>>();
return clientId =>
{
// You may have injected another type to help with this
var connectionString = GetConnectionStringForClient(clientId);
IMainContext dbContext = dbContextFactory(connectionString);
IUnitOfWork unitOfWork = unitOfWorkFactory(dbContext);
IEmployeeRepository employeeRepository = repositoryFactory(dbContext);
unitOfWork.employeeRepositoty = employeeRepository;
return serviceFactory(unitOfWork);
};
});
If you find the registration grows too cumbersome because of needing to do a little wiring manually, you probably need to look at updating (or creating a new) container after you have determined the client so that you can rely more on the container.
You can change the connectionstring per DbContext instance
Example:
public class AwesomeContext : DbContext
{
public AwesomeContext (string connectionString)
: base(connectionString)
{
}
public DbSet<AwesomePeople> AwesomePeoples { get; set; }
}
And then use your DbContext like this:
using(AwesomeContext context = new AwesomeContext("newConnectionString"))
{
return context.AwesomePeoples.ToList();
}
Depending on how many ConnectionStrings there are you can make a DB table for the client / constring mapping or save it in the solution (array for example).
If you can't/don't want to change the constructor you can do it later as well
Add this to your DbContext override:
public void SetConnectionString(string connectionString)
{
this.Database.Connection.ConnectionString = connectionString;
}
And call the method before you do any DB operations:
using(AwesomeContext context = new AwesomeContext())
{
context.SetConnectionString(ConfigurationManager.ConnectionStrings["newConnectionString"].ConnectionString)
return context.AwesomePeoples.ToList();
}
While questions of this sort have been frequently asked, I think I have a more specific constraint that makes the problem a little more interesting. I am writing a client-side application in Dart using an MVC pattern. My goal is simple: listen for clicks on a button, trigger an async request to a back-end API, and present that data to the user.
Minimally, I have one each of a model, view, and controller class. The model class implements methods to make requests and bundle up the data it receives. The view class has the DOM subtree of interest as a field and implements methods to manipulate the elements therein. The controller has a single instance each of the model and view classes as its fields and registers event handlers on the elements of the view. The controller's event handlers fire off calls to the model to make requests and return data, which will then be passed to the view for rendering.
The issue arises when I attempt to capture the incoming data from the async request into an instance variable of the model. I'd like to keep everything nicely encapsulated (that's why I'm using Dart in the first place), and I'd like to avoid using a global variable to hold the data that comes from the async request. A minimal example of my current layout looks something like below. I've made all of the fields and methods public here for clarity's sake.
// view.dart
class FooView {
// The root element of the view with which we're concerned.
static final Element root = querySelector('#thisView');
FooView() { init(); }
void init() { root.hidden = false; }
// Appends the new data into an unordered list.
void update(List<Map<String,String>> list) {
UListElement container = root.querySelector('ul#dataContainer');
container
..hidden = true
..children.clear();
for ( Map<String,String> item in list ) {
container.append(new LIElement()
..id = item['id']
..text = item['text']
);
container.hidden = false;
}
// model.dart
class FooModel {
// Instance variable to hold processed data from the async request.
List<Map<String,String>> dataList;
// Makes async request, returning data to caller.
List<Map<String,String>> getData() {
HttpRequest
.getString('example.com/api/endpoint')
.then( (String data) {
dataList = JSON.decode(data);
});
return dataList;
}
}
// controller.dart
class FooController {
FooModel model;
FooView view;
FooController() {
model = new FooModel;
view = new FooView;
}
void registerHandlers() {
// When this button is clicked, the view is updated with data from the model.
ButtonElement myButton = view.root.querySelector('#myButton');
myButton.onClick.listen( (Event e) {
view.update(model.getData());
});
}
}
The errors I'm seeing involve the model.dataList field coming up null at the end of all of this. My first blush is that I do not understand scoping of callback functions. The way I first understood it, the callback would handle the request's data when it arrived and just set the instance variable when it was ready. Perhaps the instance variable is aliased and modified within the scope of the callback, but the variable I want to return is never touched.
I have thought about passing a Future object to a method of the view, which will then just do the processing itself and add the elements to the DOM as a side effect. That technique would break my MVC design (even more than it's broken now in this minimal working example).
It is also very possible that I am using asynchronous programming completely incorrectly. Thinking more on this, my async call is useless because I basically make a blocking call to view.update() in the controller when the event fires. Maybe I should pass a request Future to the controller, and fire the request's then() method from there when the event handler is triggered.
In Dart, in what scope do callback functions reside, and how can I get data out of them with minimal side effects and maximal encapsulation?
N.B. I hate to belabor this oft-discussed question, but I have read previous answers to similar questions to no avail.
The getData method initiates the asynchronous HTTP request then immediately returns before having received/parsed the response. That is why model.datalist is null.
To make this work with minimal effort, you can make getData synchronous:
(note: I changed the dataList type, just to make it work with the sample JSON service http://ip.jsontest.com/)
// model.dart
class FooModel {
// Instance variable to hold processed data from the async request.
Map<String, String> dataList;
// Makes async request, returning data to caller.
Map<String, String> getData() {
var request = new HttpRequest()
..open('GET', 'http://ip.jsontest.com/', async: false)
..send();
dataList = JSON.decode(request.responseText);
return dataList;
}
}
Though this may violate your objective, I agree with your concerns re: blocking call and would personally consider keeping the HTTP request asynchronous and making getData return a new future that references your model class or parsed data. Something like:
// model.dart
class FooModel {
// Instance variable to hold processed data from the async request.
Map<String,String> dataList;
// Makes async request, returning data to caller.
Future<Map<String, String>> getData() {
return HttpRequest
.getString('http://ip.jsontest.com/')
.then( (String data) {
dataList = JSON.decode(data);
return dataList;
});
}
}
and in the controller:
void registerHandlers() {
// When this button is clicked, the view is updated with data from the model.
ButtonElement myButton = FooView.root.querySelector('#myButton');
myButton.onClick.listen( (Event e) {
model.getData().then((Map<String, String> dataList) {
view.update(dataList);
});
});
}
You return datalist in getData before the HttpRequest has returned.
// Makes async request, returning data to caller.
List<Map<String,String>> getData() {
return HttpRequest // <== modified
.getString('example.com/api/endpoint')
.then( (String data) {
return JSON.decode(data); // <== modified
});
// return dataList; // <== modified
void registerHandlers() {
// When this button is clicked, the view is updated with data from the model.
ButtonElement myButton = view.root.querySelector('#myButton');
myButton.onClick.listen( (Event e) {
model.getData().then((data) => view.update(data)); // <== modified
});
}
You can use Stream to make your design loosely coupled and asynchronous:
class ModelChange {...}
class ViewChange {...}
abstract class Bindable<EventType> {
Stream<EventType> get updateNotification;
Stream<EventType> controllerEvents;
}
class Model implements Bindable<ModelChange> {
Stream<ModelChange> controllerEvents;
Stream<ModelChange> get updateNotification => ...
}
class View implements Bindable<ViewChange> {
Stream<ViewChange> controllerEvents;
Stream<ViewChange> get updateNotification => ...
}
class Controller {
final StreamController<ViewChange> viewChange = new StreamController();
final StreamController<ModelChange> modelChange = new StreamController();
Controller.bind(Bindable model, Bindable view) {
view.controllerEvents = viewChange.stream;
model.controllerEvents = modelChange.stream;
view.updateNotification.forEach((ViewChange vs) {
modelChange.add(onViewChange(vs));
});
model.updateNotification.forEach((ModelChange mc) {
viewChange.add(onModelChange(mc));
});
}
ModelChange onViewChange(ViewChange vc) => ...
ViewChange onModelChange(ModelChange mc) => ...
}
I have 3 action name delete on 3 different controllers which are on "registration,profile and questions" they all have action delete methods. How can I from my registration-delete method call out profile-delete and questions-delete . That way when a user wants to delete their account all they have to do is go on registration-delete instead of going on registration,profile and questions delete methods. I want in 1 [HttpPost, ActionName("Delete")](registration) to call out the other 2 ActionName("Delete") methods as i prefer people to delete everything in one place is this possible? assuming that each user shares a unique ID that is the same all across. The comment in the code im just using to illustrate any help would be great
registration-delete below
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed()
{
var ss = User.Identity.Name;
var getid = (from s in db.registration where ss == s.email select s.RegistrationID).FirstOrDefault();
registration registration = db.registration.Find(getid);
//This delete's the registration
db.buyers.Remove(registration);
// How can i call-out profile-delete actionname here and questions-delete like
//if (question-delete != null){
// include ActionResult deleteconfirmed("question-delete" }
db.SaveChanges();
return RedirectToAction("logout");
}
If I understand your question, you are asking to call other controller's action method from the currently executing action method? You typically would not do that. The first rule of Controller's action methods is: no action method should be more than 10 lines of code. Basically, an action method is really supposed to be a simple method to collect a view, or call an action in your domain, and return.
In other words, the SRP pattern:
http://codebetter.com/karlseguin/2008/12/05/get-solid-single-responsibility-principle/
Instead, you would organize your Domain-logic (what you describe is considered domain model logic, not controller logic) for this repetitive code such as deleting questions here, but when user is deleted also delete questions, etc.
// an example of IOC injection of your IUserService
private IUserService
public RegistrationController(IUserService userService)
{
_userService = userService;
}
[HttpPost]
public ActionResult Delete()
{
// insert modelstate and/or validation logic
//
if (User.Identity.IsAuthenticated == false)
{
return RedirectToAction("index", "Home");
}
// good practice to never bubble up exceptions to users
try
{
if (_userService.DeleteByEmail(User.Identity.Name) == false)
{
ModalState.AddModelError(String.Empty, "Not allowed?");
return View();
}
// save it all in a single atomic operation here to keep it all in
// a single Transaction Scope that will rollback properly if there is
// an error.
//
db.SaveChanges();
}
catch (Exception)
{
ModalState.AddModelError(String.Empty, "An error occurred...");
return View();
}
// all ok!
return RedirectToAction("logout");
}
Notice how clean this action method is. It just gets down to business with a single line of code or two, and a whole bunch of exit paths to handle the user's experience properly in many different situations.
Now, your domain logic can be encapsulated into a service (or provider, or alike):
namespace MyWebsite.Models
{
public class UserService : IUserService
{
// Todo: convert to some IoC lovin'
//
public Boolean DeleteByEmail(String email)
{
var user =
(from user in db.Registrations
where
user.Email == email
select s).FirstOrDefault();
if (user == null)
return false;
// call into other services to delete them
ProfileDataService.DeleteByUserId(user.UserId);
QuestionsService.DeleteByUserId(user.UserId);
// finally, delete the user
db.Registrations.Delete(user);
// note, I am NOT calling db.SaveChanges() here. Look
// back at the controller method for the atomic operation.
//
return true;
}
}
}
This can be implemented 100s of different ways. The point being is to abstract that logic out to a common code base, or "Domain". I chose to put that logic in your current Website namespace under Models as a shortcut in this example.
As for your other Delete() methods on your other controllers, you would just call into a QuestionsService.DeleteByUserId() and ProfileDataService.DeleteByUserId() to handle those. You can even share those services across the domain, as I showed above.
I have a ASP.NET Web API (.NET 4) application which has a few controllers. We will run several instances of the Web API application on IIS with one difference. Only certain controllers will be available under certain IIS instances. What I was thinking is to disable/unload the controllers that are not applicable to an instance when the instance starts up.
Anyone got some information that could guide me in the right direction on this?
You can put your own custom IHttpControllerActivator in by decorating the DefaultHttpControllerActivator. Inside just check for a setting and only create the controller if allowed.
When you return null from the Create method the user will receive 404 Not Found message.
My example shows a value in App Settings (App.Config or Web.Config) being checked but obviously this could any other environment aware condition.
public class YourCustomControllerActivator : IHttpControllerActivator
{
private readonly IHttpControllerActivator _default = new DefaultHttpControllerActivator();
public YourCustomControllerActivator()
{
}
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
if (ConfigurationManager.AppSettings["MySetting"] == "Off")
{
//Or get clever and look for attributes on the controller in controllerDescriptor.GetCustomAttributes<>();
//Or use the contoller name controllerDescriptor.ControllerName
//This example uses the type
if (controllerType == typeof (MyController) ||
controllerType == typeof (EtcController))
{
return null;
}
}
return _default.Create(request, controllerDescriptor, controllerType);
}
}
You can switch your activator in like so:
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new YourCustomControllerActivator());
Update
It has been a while since I looked at this question but if I was to tackle it today I would alter the approach slightly and use a custom IHttpControllerSelector. This is called before the activator and makes for a slightly more efficient place to enable and disable controllers... (although the other approach does work). You should be able to decorate or inherit from DefaultHttpControllerSelector.
Rather than unloading the controllers, I think I'd create a custom Authorize attribute that looked at the instance information in deciding to grant authorization.
You would add the following to each controller at the class level, or you could also add this to individual controller actions:
[ControllerAuthorize (AuthorizedUserSources = new[] { "IISInstance1","IISInstance2","..." })]
Here's the code for the Attribute:
public class ControllerAuthorize : AuthorizeAttribute
{
public ControllerAuthorize()
{
UnauthorizedAccessMessage = "You do not have the required access to view this content.";
}
//Property to allow array instead of single string.
private string[] _authorizedSources;
public string UnauthorizedAccessMessage { get; set; }
public string[] AuthorizedSources
{
get { return _authorizedSources ?? new string[0]; }
set { _authorizedSources = value; }
}
// return true if the IIS instance ID matches any of the AllowedSources.
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
//If no sources are supplied then return true, assuming none means any.
if (!AuthorizedSources.Any())
return true;
return AuthorizedSources.Any(ut => ut == httpContext.ApplicationInstance.Request.ServerVariables["INSTANCE_ID"]);
}
The IHttpControllerActivator implementation doesn't disable the routes defined using attribute routing , if you want to switch on/off a controller and have a default catch all route controller. Switching off using IHttpControllerActivator disables the controller but when the route is requested it doesn't hit the catch all route controller -it simply tries to hit the controller that was removed and returns no controller registered.