I am thinking to implement something like a static SessionHelper class where I would like to keep some data in Session.
But it seems like is impossible to use Session object out of the Controller class. Right?
Or may be I am wrong... I.e. is this link a solution ASP.NET MVC - How to access Session data in places other than Controller and Views
Let me know, please!
Anyway for now I cannot refer to Session object in that class which lives in Models folder.
public static class SessionHelper
{
public static bool ShowSuccessPopup
{
get
{
if (Session["ShowSuccessPopup"] == null)
{
Session["ShowSuccessPopup"] = false;
return false;
}
else
{
var result = (bool)Session["ShowSuccessPopup"].ToString();
return result;
}
}
set {Session["ShowSuccessPopup"] = value; }
}
}
The Session object is only set in the request-cycle, so anything outside the request-cycle won't have access to it (i.e. Controllers and Views are fine, but models no). If you need to work with the session in something outside of the request-cycle, then you must inject the Session object as a dependency. However, you're not going to be able to accomplish that with a static class. So you might instead try something like:
public class SessionHelper
{
private HttpSessionState session;
public SessionHelper (HttpSessionState session)
{
this.session = session;
}
public bool ShowSuccessPopup { ... }
}
Alternatively, you may be able to get by with merely injecting the session into your actual methods individually, but you wouldn't be able to continue using a property:
public static bool ShowSuccessPopup (HttpSessionState session)
{
// do something with session
}
Thanks to Chris Pratt!
Just like an alternative I am gonna share my approach.
public partial class BaseController : Controller
{
public SessionBox SessionBox;
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
SessionBox = new SessionBox(filterContext.HttpContext);
base.OnActionExecuting(filterContext);
}
}
public class SessionBox
{
private HttpContextBase context { get; set; }
public SessionBox(HttpContextBase context)
{
this.context = context;
}
public bool ShowSuccessPopup
{
get
{
if (context.Session["ShowSuccessPopup"] == null)
{
context.Session["ShowSuccessPopup"] = false;
return false;
}
else
{
var result = Convert.ToBoolean(context.Session["ShowSuccessPopup"].ToString());
return result;
}
}
set { context.Session["ShowSuccessPopup"] = value; }
}
}
Notice that you should inheritine Controller class on BaseController class
and later in the Controller class you can do like
if (SessionBox.ShowSuccessPopup)
{
SessionBox.ShowSuccessPopup = false;
Here are extra links that demonstrate difference between
http://msdn.microsoft.com/en-us/library/system.web.sessionstate.httpsessionstate.aspx
and
http://msdn.microsoft.com/en-us/library/system.web.httpcontextbase.aspx
Related
We can access session data in controllers and views like this:
Session["SessionKey1"]
How do you access Session values from a class other than a controller or view?
I'd use dependency injection and pass the instance of the HttpContext (or just the session) to the class that needs access to the Session. The other alternative is to reference HttpContext.Current, but that will make it harder to test since it's a static object.
public ActionResult MyAction()
{
var foo = new Foo( this.HttpContext );
...
}
public class Foo
{
private HttpContextBase Context { get; set; }
public Foo( HttpContextBase context )
{
this.Context = context;
}
public void Bar()
{
var value = this.Context.Session["barKey"];
...
}
}
You just need to call it through the HttpContext like so:
HttpContext.Current.Session["MyValue"] = "Something";
Here is my version of a solution for this problem. Notice that I also use a dependency injection as well, the only major difference is that the "session" object is accessed through a Singleton
private iSession _Session;
private iSession InternalSession
{
get
{
if (_Session == null)
{
_Session = new SessionDecorator(this.Session);
}
return _Session;
}
}
Here is the SessionDecorator class, which uses a Decorator pattern to wrap the session around an interface :
public class SessionDecorator : iSession
{
private HttpSessionStateBase _Session;
private const string SESSIONKEY1= "SESSIONKEY1";
private const string SESSIONKEY2= "SESSIONKEY2";
public SessionDecorator(HttpSessionStateBase session)
{
_Session = session;
}
int iSession.AValue
{
get
{
return _Session[SESSIONKEY1] == null ? 1 : Convert.ToInt32(_Session[SESSIONKEY1]);
}
set
{
_Session[SESSIONKEY1] = value;
}
}
int iSession.AnotherValue
{
get
{
return _Session[SESSIONKEY2] == null ? 0 : Convert.ToInt32(_Session[SESSIONKEY2]);
}
set
{
_Session[SESSIONKEY2] = value;
}
}
}`
Hope this helps :)
Haven't done it myself, but this sample from Chad Meyer's blog might help (from this post: http://www.chadmyers.com/Blog/archive/2007/11/30/asp.net-webforms-and-mvc-in-the-same-project.aspx)
[ControllerAction]
public void Edit(int id)
{
IHttpSessionState session = HttpContext.Session;
if (session["LoggedIn"] == null || ((bool)session["LoggedIn"] != true))
RenderView("NotLoggedIn");
Product p = SomeFancyDataAccess.GetProductByID(id);
RenderView("Edit", p);
}
I would also wrap all session variables into a single class file. That way you can use intelliSense to select them. This cuts down on the number of paces in code where you need to specify the "strings" for the session.
I want to change the connection to a database at runtime in a REST Api. I want to put a variable of the request and let the Api decide which connectionstring to use.
For example:
I put the variable "dbid" with the value "develop" in the request header and send it to the Api.
The Api sees the header and gets the correct connectionstring from the web.config.
I have three layers (data, business, api). The data contains EntityFramework to get and set data. Like this:
public class WebsiteContext : IocDbContext, IWebsites
{
public DbSet<Website> Websites { get; set; }
public IEnumerable<Website> GetAll()
{
return Websites.ToList();
}
}
(IoCDbContext.cs)
public class IocDbContext : DbContext, IDbContext
{
public IocDbContext() : base("develop")
{
}
public void ChangeDatabase(string connectionString)
{
Database.Connection.ConnectionString= connectionString;
}
}
In the business I have a class to retrieve data from the datalayer and do some logical stuff (not needed here, but still good for the story).
public class Websites : IWebsites
{
private readonly Data.Interfaces.IWebsites _websiteContext;
#region Constructor
public Websites(Data.Interfaces.IWebsites websiteContext)
{
_websiteContext = websiteContext;
}
#endregion
#region IWebsites implementation
public IEnumerable<Website> GetWebsites()
{
List<Data.Objects.Website> websiteDtos = _websiteContext.GetAll().ToList();
return websiteDtos.Select(web => web.ToModel()).ToList();
}
#endregion
}
public static class WebsiteMapper
{
public static Website ToModel(this Data.Objects.Website value)
{
if (value == null)
return null;
return new Website
{
Id = value.Id,
Name = value.Name
};
}
}
And, last but not least, the controller:
public class WebsiteController : ApiController
{
private readonly IWebsites _websites;
public WebsiteController(IWebsites websites)
{
_websites = websites;
}
public IEnumerable<Website> GetAll()
{
return _websites.GetWebsites().ToList();
}
}
My Unity configuration:
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<Business.Interfaces.IWebsites, Websites>();
container.RegisterType<IDbContext, IocDbContext>();
container.RegisterType<IWebsites, WebsiteContext>();
// e.g. container.RegisterType<ITestService, TestService>();
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
So as you can see the connection string with the name "develop" is used by default. This will return a website with the name "website". Now I would change the header variable "dbid" to "live". The api should see this and should get the connectionstring that corresponds with the name "live". This last part is something I am trying, but nothing works.
This I tried:
Adding session to webapi. This means I break the stateless idea of REST api: not done
Statics cannot work either, because everyone could get the same connectionstring, but its user specific
Google, but most of the examples don't work for me
Searching StackOverflow... See previous point.
This is driving me crazy! There should be a way to change the connectionstring given by a value in a request header, right?
I have the same scenario in a multi-tenant application I created where I use a different connection string for each tenant.
It doesn't matter the implementation you choose, but you have to determine how you are going to differentiate each request per connection string. In my application, I created a custom route value, and used it in the url to differentiate each request. The important thing is to create whatever this mechanism is, and it needs to be the 1st thing you register in your DI framework, on a per request basis.
For example (using Ninject):
private static void RegisterServicdes(IKernel kernel)
{
kernel.Bind<ISiteContext>().To<SiteContext>().InRequestScope();
kernel.Bind<IDbContextFactory>().To<DbContextFactory>().InRequestScope();
// register other services...
}
Rather than your implementation of your DbContext, I would change to be this, then always create your DbContext instance via a DbContextFactory.
public class IocDbContext : DbContext, IDbContext
{
public IocDbContext(string connectionStringType) : base(connectionStringType) { }
}
Then you need to create a DbContextFactory that you use when you create your DbContext, and take the above class as a dependency. Or you can take the dependency into your services, and pass it into the DbContextFactory instead.
public interface IDbContextFactory
{
TestModel CreateContext();
}
public class DbContextFactory : IDbContextFactory
{
private string _siteType;
public DbContextFactory(ISiteContext siteContext)
{
_siteType = siteContext.Tenant;
}
public TestModel CreateContext()
{
return new TestModel(FormatConnectionStringBySiteType(_siteType));
}
// or you can use this if you pass the IMultiTenantHelper dependency into your service
public static TestModel CreateContext(string siteName)
{
return new TestModel(FormatConnectionStringBySiteType(siteName));
}
private static string FormatConnectionStringBySiteType(string siteType)
{
// format from web.config
string newConnectionString = #"data source={0};initial catalog={1};integrated security=True;MultipleActiveResultSets=True;App=EntityFramework";
if (siteType.Equals("a"))
{
return String.Format(newConnectionString, #"(LocalDb)\MSSQLLocalDB", "DbOne");
}
else
{
return String.Format(newConnectionString, #"(LocalDb)\MSSQLLocalDB", "DbTwo");
}
}
}
Then you can use it like so when accessing your DbContext:
public class DbAccess
{
private IDbContextFactory _dbContextFactory;
public DbAccess(IDbContextFactory dbContextFactory)
{
_dbContextFactory = dbContextFactory;
}
public void DoWork()
{
using (IocDbContext db = _dbContextFactory.CreateContext())
{
// use EF here...
}
}
}
ISiteContext interface implementation (for using route).
public interface ISiteContext
{
string Tenant { get; }
}
public class SiteContext : ISiteContext
{
private const string _routeId = "tenantId";
private string _tenant;
public string Tenant { get { return _tenant; } }
public SiteContext()
{
_tenant = GetTenantViaRoute();
}
private string GetTenantViaRoute()
{
var routedata = HttpContext.Current.Request.RequestContext.RouteData;
// Default Routing
if (routedata.Values[_routeId] != null)
{
return routedata.Values[_routeId].ToString().ToLower();
}
// Attribute Routing
if (routedata.Values.ContainsKey("MS_SubRoutes"))
{
var msSubRoutes = routedata.Values["MS_SubRoutes"] as IEnumerable<IHttpRouteData>;
if (msSubRoutes != null && msSubRoutes.Any())
{
var subRoute = msSubRoutes.FirstOrDefault();
if (subRoute != null && subRoute.Values.ContainsKey(_routeId))
{
return (string)subRoute.Values
.Where(x => x.Key.Equals(_routeId))
.Select(x => x.Value)
.Single();
}
}
}
return string.Empty;
}
}
API action:
[Route("api/{tenantId}/Values/Get")]
[HttpGet]
public IEnumerable<string> Get()
{
_testService.DoDatabaseWork();
return new string[] { "value1", "value2" };
}
you need to create a factory class for Dynamic picking of connection string.
It is the responsibility of that class to give correct connectionString based on the certain Parameter.
I would like to implement my own IRuntimePolicy. I am following the given example, but I need to access our database or the best would be to have our UserSession object injected.
When is the security object created on runtime? Is this possible? I have not found any examples.
We use Ninject 3.2.3 I believe (or the latest available for MVC 5).
I imagine something like
public class GlimpseSecurityPolicy : IRuntimePolicy
{
private readonly IAclManager aclManager;
private readonly IUserSession userSession;
public GlimpseSecurityPolicy(IUserSession userSession, IAclManager aclManager)
{
this.userSession = userSession;
this.aclManager = aclManager;
}
public RuntimeEvent ExecuteOn
{
// check policy when request ends and when executing a resource (like glimpse.axd)
get { return RuntimeEvent.EndRequest | RuntimeEvent.ExecuteResource; }
}
public RuntimePolicy Execute(IRuntimePolicyContext policyContext)
{
if (!this.aclManager.IsUserAllowed(UserAction.AccessGlimpse, this.userSession.GetUser()))
{
return RuntimePolicy.Off;
}
return RuntimePolicy.On;
}
}
Ultimately, we came up with only one option: to use DependencyResolver.Current.GetService<IThing>().
The code result is then straightforward and ugly:
public class GlimpseSecurityPolicy : IRuntimePolicy
{
public RuntimeEvent ExecuteOn => RuntimeEvent.EndRequest | RuntimeEvent.ExecuteResource;
public RuntimePolicy Execute(IRuntimePolicyContext policyContext)
{
var aclManager = DependencyResolver.Current.GetService<IAclManager>();
var userSession = DependencyResolver.Current.GetService<IUserSession>();
if (!aclManager.IsUserAllowed(UserAction.AccessGlimpse, userSession.GetUser()))
{
return RuntimePolicy.Off;
}
return RuntimePolicy.On;
}
}
I was just working on some application architecture and this may sound like a stupid question but please explain to me how the following works:
Interface:
public interface IMatterDAL
{
IEnumerable<Matter> GetMattersByCode(string input);
IEnumerable<Matter> GetMattersBySearch(string input);
}
Class:
public class MatterDAL : IMatterDAL
{
private readonly Database _db;
public MatterDAL(Database db)
{
_db = db;
LoadAll(); //Private Method
}
public virtual IEnumerable<Matter> GetMattersBySearch(string input)
{
//CODE
return result;
}
public virtual IEnumerable<Matter> GetMattersByCode(string input)
{
//CODE
return results;
}
Controller:
public class MatterController : ApiController
{
private readonly IMatterDAL _publishedData;
public MatterController(IMatterDAL publishedData)
{
_publishedData = publishedData;
}
[ValidateInput(false)]
public JsonResult SearchByCode(string id)
{
var searchText = id; //better name for this
var results = _publishedData.GetMattersBySearch(searchText).Select(
matter =>
new
{
MatterCode = matter.Code,
MatterName = matter.Name,
matter.ClientCode,
matter.ClientName
});
return Json(results);
}
This works, when I call my controller method from jquery and step into it, the call to the _publishedData method, goes into the class MatterDAL.
I want to know how does my controller know to go to the MatterDAL implementation of the Interface IMatterDAL. What if I have another class called MatterDAL2 which is based on the interface. How will my controller know then to call the right method?
I am sorry if this is a stupid question, this is baffling me.
EDIT:
Based on the responses, it seems like this is where the dependency is being resolved:
This is a ninject call:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICpdMatterDAL>().To<CachedCpdData>();
}
Where CachedCpdData is:
public class CachedCpdData : ICpdMatterDAL
{
private static readonly object CacheLockObject = new object();
private readonly MatterDAL _matterData;
public CachedCpdData()
{
_matterData = DomainModel.DataAccessManager.Instance.Matters;
}
public IEnumerable<Matter> GetMattersForAutoCompleteByCode(string input)
{
var cacheKey = string.Format("matter-search-{0}", input ?? "");
var result = HttpRuntime.Cache[cacheKey] as IEnumerable<Matter>;
if (result == null)
{
lock (CacheLockObject)
{
result = HttpRuntime.Cache[cacheKey] as IEnumerable<Matter>;
if (result == null)
{
result = _matterData.GetMattersForAutoCompleteByCode(input).ToList();
HttpRuntime.Cache.Insert(cacheKey, result, null, DateTime.Now.AddSeconds(60), TimeSpan.Zero);
}
}
}
return result;
}
public IEnumerable<Matter> GetMattersByMatterCodeSearch(string input)
{
return _matterData.GetMattersByMatterCodeSearch(input);
}
}
The rason why your code is using the right implementation of IMatterDAL is because it's being passed as a parameter in the constructor of MatterController. I'm almost sure that your code is using some Dependency Injection framework to resolve IMatterDAL.
In fact Ninject is a DI Framework. Your code should have something like
kernel.Bind<IMatterDAL>().To<MatterDAL >();
according to anti-if campaign it is a best practice not to use ifs in our code. Can anyone tell me if it possible to get rid of the if in this piece of code ?
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(OrganisationInput organisationInput)
{
if (!this.ModelState.IsValid)
{
return View(organisationInput.RebuildInput<Organisation, OrganisationInput>());
}
var organisation = organisationInput.BuildEntity<Organisation, OrganisationInput>();
this.organisationService.Create(organisation);
return this.RedirectToAction("Index");
}
Looks like a valid use for "if".
The anti-if campaign appears to be against the abuse of if statments (i.e. too many nested ifs) etc, not for complete eradication of them.
They are Necessary.
Since you are asking: Yes, it is possible.
Create an abstract class A with an abstract method that returns ActionResult, created via a factory method that uses a dictionary of Func's that creates A-subclassed instances based on Controller state computed from model (in your case, from the controllerInstance.Model.IsValid property value).
Sample code:
public abstract class ModelStateValidator
{
private Controller controller;
protected Controller Controller {
get { return controller; }
}
public abstract ActionResult GetResult();
#region initialization
static ModelStateValidator() {
creators[ControllerState.InvalidModel] = () => new InvalidModelState();
creators[ControllerState.ValidModel] = () => new ValidModelState();
}
#endregion
#region Creation
private static Dictionary<ControllerState, Func<ModelStateValidator>> creators = new Dictionary<ControllerState, Func<ModelStateValidator>>();
public static ModelStateValidator Create(Controller controller) {
return creators[GetControllerState(controller)]();
}
private static ControllerState GetControllerState(Controller c) {
return new ControllerState(c);
}
internal class ControllerState
{
private readonly Controller controller;
private readonly bool isModelValid;
public ControllerState(Controller controller)
: this(controller.ModelState.IsValid) {
this.controller = controller;
}
private ControllerState(bool isModelValid) {
this.isModelValid = isModelValid;
}
public static ControllerState ValidModel {
get { return new ControllerState(true); }
}
public static ControllerState InvalidModel {
get { return new ControllerState(false); }
}
public override bool Equals(object obj) {
if (obj == null || GetType() != obj.GetType()) //I can show you how to remove this one if you are interested ;)
{
return false;
}
return this.isModelValid == ((ControllerState)obj).isModelValid;
}
public override int GetHashCode() {
return this.isModelValid.GetHashCode();
}
}
#endregion
}
class InvalidModelState : ModelStateValidator
{
public override ActionResult GetResult() {
return Controller.View(organisationInput.RebuildInput<Organisation, OrganisationInput>());
}
}
class ValidModelState : ModelStateValidator
{
public override ActionResult GetResult() {
return this.Controller.RedirectToAction("Index");
}
}
80 additional lines, 4 new classes to remove a single if.
your usage then, instead of if, calls the method like this:
ActionResult res = ModelStateValidator.Create(this).GetResult();
NOTE: Of course it should be tweaked to acommodate the code that is between the ifs in your original question, this is only a sample code :)
Adds additional unnecessary complexity? YES.
Contains ifs? NO.
Should you use it? Answer that yourself :)
Use a ternary operator :-)
It's worse, but it's not an if.
It is a valid if in this case, as it can only be one of two states. The anti-if campaign seems more geared to the endlessly open-ended if/else/switch statments. Looks like a good campaign!
You could just use a While loop... but that seems like a glorified if statement.
private static bool Result(bool isValid)
{
while(!isValid)
{
return true;
}
return false;
}