How do I create a session variable in DotVVM viewmodel? - asp.net

I am building a site in DotVVM and when I try the following line of code but I get error: NullReferenceException
HttpContext.Current.Session.Add ("Value", Item3);

DotVVM is an OWIN middleware, so you have to configure OWIN first to enable session. First, you need to declare this method, which turns on ASP.NET session:
public static void RequireAspNetSession(IAppBuilder app) {
app.Use((context, next) =>
{
var httpContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
httpContext.SetSessionStateBehavior(SessionStateBehavior.Required);
return next();
});
// To make sure the above `Use` is in the correct position:
app.UseStageMarker(PipelineStage.MapHandler);
}
Then in the Startup.cs file, call it:
app.RequireAspNetSession();
Then you can use HttpContext.Current.Session["key"] to access your session state.

You can save an object in the Session by doing:
Session["Value"] = Item3;
You can retrieve an object from the Session by doing:
object value = Session["Value"];
Usually, you need to cast the value to the type you used, so if Item3 is a string, then you would do:
string value = (string)Session["Value"];
You can access session variables from your views as well, so you shouldn't need to store it in your viewmodel.

Related

ASP.Net Identity SecurityStampValidator is not called if using custom CookieAuthenticationEvents

Using Microsoft.AspNetCore.Identity (.net 7 at time of writing), the default security stamp validation is not done anymore if I use a custom CookieAuthenticationEvents. Configuring the SecurityStampValidatorOptions has no effect either.
Why is this happening and what can be done to enable the security stamp validation?
program.cs
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.ConfigureApplicationCookie(options =>
options.EventsType = typeof(CustomCookieAuthenticationEvents)
);//this prevents securityStampValidation from occurring.
//set a small interval between validations so we can debug
builder.Services.Configure<SecurityStampValidatorOptions>(o => o.ValidationInterval = TimeSpan.FromSeconds(10));
builder.Services.Configure<SecurityStampValidatorOptions>(o => o.OnRefreshingPrincipal = c =>
{
//breakpoint here is hit if ConfigureApplicationCookie(options.EventsType) is NOT set
return Task.FromResult(0);
});
builder.Services.AddScoped<CustomCookieAuthenticationEvents>();
CustomCookieAuthenticationEvents.cs is an empty class for now
public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{ }
This behavior should be documented as it falls between two products.
The doc says
Events: The Provider may be assigned to an instance of an object created by the application at startup time. The handler calls methods on the provider which give the application control at certain points where processing is occurring. If it is not provided a default instance is supplied which does nothing when the methods are called.
EventsType: If set, will be used as the service type to get the Events instance instead of the property.
Fine, but let's not be misleaded by the events definition, as it is part of the namespace Microsoft.AspNetCore.Authentication.Cookies which is not the Identity namespace.
Instead, let's look at the code for IdentityServiceCollectionExtensions, which, among other things, does for AddIdentity
.AddCookie(IdentityConstants.ApplicationScheme, o =>
{
o.LoginPath = new PathString("/Account/Login");
o.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
};
})
So the cookie Events is set when adding Identity, and when we add our custom options.EventsType = typeof(CustomCookieAuthenticationEvents), the Events property is dismissed.
By looking at this code, we see that OnValidatePrincipal is the only event that is set, so we shouldn't have other unexpected missing functionalities. We also see that a static class is used to call the validation, so we can copy that into our CustomCookieAuthenticationEvents.
public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
public async override Task ValidatePrincipal(CookieValidatePrincipalContext context)
{
await SecurityStampValidator.ValidatePrincipalAsync(context);
//optional custom code
}
}

WCF service operation name

Is there a way to configure App Insights to collect the operation name when monitoring a WCF service? All requests get lumped together by URL (which are just POSTs that end in .svc), so there is no easy way to determine which particular operation was called on the service.
Does there need to be a custom Telemetry Initializer that can somehow determine which operation was actually called and set a custom property? if so, how do you determine the current WCF operation name?
Another option for collecting data on WCF operations is to use the Microsoft.ApplicationInsights.Wcf Nuget package. You can read more about this here.
Brett,
Operation name can be customized in two ways:
1) Using a custom telemetry initializer - that specifically sets operation name.
For more information about telemetry initializers: Custom Telemetry Initializers
2) From sdk version 2-beta3, auto-generated request telemetry is accessible though HttpContext extension method:
System.Web.HttpContextExtension.GetRequestTelemetry
Once the request telemetry is retrieved, operation name associated with it can be changed.
Please let me know if this addressed your question.
Thanks,
Karthik
If you want to get the name of the WCF method called from a client in application insight you can use the following ITelemetryInitializer
With .net 5.0, the httprequest object is stored in the raw object properties of the telemetry context.
public class SoapActionHeaderTelemetryInitializer : ITelemetryInitializer
{
private static readonly Regex _soapActionUri = new Regex("^\"(?<uri>.*)\"$", RegexOptions.Compiled);
public void Initialize(ITelemetry telemetry)
{
DependencyTelemetry httpDependency = telemetry as DependencyTelemetry;
if (httpDependency != null)
{
httpDependency.Context.TryGetRawObject("HttpRequest", out var request);
if (request is HttpRequestMessage httpRequest)
{
if (httpRequest.Headers.TryGetValues("SOAPAction", out var values) && values.Any())
{
// SOAP Action is contained within quote : https://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383528
var soapAction = _soapActionUri.Match(values.First()).Groups["uri"].Value;
telemetry.Context.GlobalProperties["SOAPAction"] = soapAction;
}
}
}
}
}

Access appSettings in web.config from Thread in Class Library project

Can I able to access appSettings section in my ASP.NET web.config file from a method in another referenced Class Library project when it is called as a new Thread?
I'm accessing the setting through a property as
private static string TempXmlFolder
{
get
{
return System.Web.HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["ReceiptTempPath"] ?? "~/Receipts/TempXML");
}
}
There is an extension method for the matter to generate the receipt.
internal static void GenerateReceipt(this IMatter matter)
{
try
{
string XmlFile = TempXmlFolder + "/Rec_" + matter.MatterID + ".xml";
// ...
// Generating receipt from the matter contents
// ...
// Saving generated receipt
}
catch (Exception ex)
{
ex.WriteLog();
}
}
I'm calling the receipt generation as a new thread from the class library like
Thread printThread = new Thread(new ThreadStart(this.GenerateReceipt));
// To avoid exception 'The calling thread must be STA, because many UI components require this' (Using WPF controls in receipt generation function)
printThread.SetApartmentState(ApartmentState.STA);
printThread.Start();
// ...
// Do another stuffs
// ...
// Wait to generate receipt to complete
printThread.Join();
But since the HttpContext.Current is null inside the Thread, I'm not able to access the current web server configuration file.
Can you suggest there any way other than passing the current HttpContext to the Thread? If no, what are the things I've to take care to keep thread safety?
Edit #1
Currently I'm passing the HttpContext to the thread like
System.Web.HttpContext currentContext = System.Web.HttpContext.Current;
Thread printThread = new Thread(() => this.GenerateReceipt(currentContext));
and in the function,
internal static void GenerateReceipt(this IMatter matter, System.Web.HttpContext htCont)
{
string TempXmlFolder = htCont.Server.MapPath(ConfigurationManager.AppSettings["ReceiptTempPath"] ?? "~/Receipts/TempXML");
//...
Pass the TempXmlFolder into the thread. Don't rely on HttpContext.Current. Alternatively, pass the value of HttpContext.Current to the thread and calculate the value of TempXmlFolder later.
You can pass the value using any way you want. Maybe a field or a local variable that you capture with a lambda.

ASP.NET - Create custom context object?

How do I create a globally accessible Context object similar to the HttpContext object?
I want to create a custom class library which I want to reference from a website project. In the website project I want to be able to call the following globally:
ClassLibraryName.Context
I cannot create a global property directly in my classlibrary, so how should this be implemented? (I've seen other applications/products use this approach, one of which is Sitecore which has a custom Sitecore.Context object available)
Edit
Might this be a 'valid' solution?
namespace MyLibrary
{
public class Context
{
public static object ContextualObject
{
get;
set;
}
}
}
Yes, this is not hard to implement, if you always run this class in the context of an ASP.NET application, use this approach:
namespace MyLibrary
{
public class Context
{
public static object ContextualObject
{
get
{
var ctx = System.Web.HttpContext.Current.Items[typeof(Context)];
if (ctx == null)
{
ctx = new Context();
System.Web.HttpContext.Current.Items.Add(typeof(Context), ctx);
}
return ctx;
}
set { System.Web.HttpContext.Current.Items[typeof(Context)] = ctx; }
}
}
}
Essentially wrapping the existing HTTP context to provide your own service. This approach also doesn't store the object while the app lives, it only creates it for the current context, and when that response ends, it will die, and be regenerated during the next lifecycle. If that is not OK, store a static reference to context.
I've used this approach similarly in a class library I have at http://nucleo.codeplex.com, it works well.
HTH.
It depends on the lifetime you want the Context object to have. If you want all clients to use the same context, you can go with a singleton implementation.
If you want the context to be unique for each thread or http request you have to use a per request/thread implementation. One way to implement a per http request implementation would be to have a HttpModule create the object at every BeginRequest event and stick it in the HttpContext Items collection.
public static object ContextualObject
{
get { return HttpContext.Current.Items["MyContext"];}
}
You could create an instance of the object on Session_Start in the Global.asax.

ASP.Net MVC: How do you access the session from a different project within the solution?

I have 2 projects in my solution.
MVC Web application
Class library
The MVC Web application references the class library.
The class library contains a class that extends the default ASP.Net Controller.
I'm putting a variable in session in the application's Global.asax.
protected void Session_Start(object sender, EventArgs args)
{
HttpContext.Current.Session["DomainName"] = Request.Url.Host;
}
In the class library I'm trying to get the value from the HttpContext.Session, but HttpContext.Session keeps coming up null.
public class MyController : System.Web.Mvc.Controller
{
public MyController () : base()
{
//HttpContext.Session is always null at this point
ViewData["DomainName"] = HttpContext.Session["DomainName"];
}
}
HttpContext.Current.Session doesn't seem to be an option in controllers. Any ideas?
Two issues -- the HttpContext property in the Controller class is the current session. Unfortunately, it's not available in the constructor of the controller. Obviously because it's not passed in the constructor, it has to be set via the property afterwards. You might consider adding a property to hold the domain name and referencing the session from it -- that way it would be available for use when needed.
protected string DomainName
{
get { return this.HttpContext.Session["DomainName"] as string; }
}
The set it in ViewData in your actions or in OnActionExecuting/OnActionExecuted.
protected override void OnActionExecuted( ActionExecutedContext context )
{
ViewData["DomainName"] = this.HttpContext.Session["DomainName"];
// or ViewData["DomainName"] = this.DomainName; // if you used the property
}
If you're just trying to add ViewData from the session, try doing it in the OnActionExecuting method. This is where I typically add ViewData I want for every View.
You just use Session by itself (it's a property of Controller), but that just maps to Controller.HttpContext.Session (in other words, what you're already using), so it won't solve your problem, which must be elsewhere.
I'm not sure why you're putting this in the Session, though, as you can read Request.Url.Host directly during the Action.
When you create cookie then you must write
Response.AppendCookie("Your cookie name");
And if you want to get that then something like this
if (Request.Cookies["Your cookie name"] != null)
{
string value = Request.Cookies["Your cookie name"].Value;
}
and must if there are different solutions
then
machineKey
need to be same which is under
system.web
in web.config and then write
<httpCookies domain=".yourdomainname.com" />

Resources