_httpContext.Session["HistoryviewModel"] = viewModel;
my error is:
Error CS0021 Cannot apply indexing with [] to an expression of type 'ISession'
When you work with an ASP.NET Core application and you need to save in a session an instance of your customized type please consider the following solution. The solution is tested with an ASP.NET Core MVC application:
In the Startup.cs file add in the method Configure for the variable app of type
IApplicationBuilder this code:
app.UseSession();
Register a service for sessions in the Startup.cs file, in the method
ConfigureServices using the services variable:
services.AddSession();
Install using NuGet the package System.Text.Json
In my ASP.NET Core application, in the cs file with MVC controllers, I add:
using System.Text.Json;
using Microsoft.AspNetCore.Http;
In this file, in the namespace with MVC controllers, I add the following type that
will perform the necessary actions to set and get my object from a session:
public static class SessionExtensions
{
public static void SetObjectAsJson<T>(this ISession session, string key, T value)
{
session.SetString(key, JsonSerializer.Serialize<T>(value));
}
public static object GetObjectFromJson<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default(T) : JsonSerializer.Deserialize<T>(value);
}
}
Also, I add a custom type that I will save in the session:
public class MyClass {
public int MyInt { get; set; }
public string MyString { get; set; }
}
Now, in the first controller I put to a session an instance of the custom type:
var myComplexObject = new MyClass();
myComplexObject.MyInt = 1;
myComplexObject.MyString = "Hello World!";
HttpContext.Session.SetObjectAsJson<MyClass>("Test", myComplexObject);
In the second controller I get this instance from the session:
var myComplexObject = HttpContext.Session.GetObjectFromJson<MyClass>("Test");
I hope this solution helps.
Related
I have default Program.cs file from Web Api template in .NET 6.0.
I am adding variable "test" so I can use its value in controllers.
var builder = WebApplication.CreateBuilder(args);
const string test = "test123";
builder.Configuration.Bind(test);
//rest of the file...
And now I want to use variable "test" outside Program.cs but I have no idea how to do it. I cannot just simply use it because when trying to read it in controller like this:
string localVar = test;
I am getting an error "'test' is not null here. Cannot use local variable or local function declared in a top-level statement in this context".
This is probably some stupid mistake but I can't figure it out...
Starting C# 9, we don't need to explicitly mention the Main method in Program.cs file as we can use the top-level statements feature. However, it doesn't mean that we shouldn't use the default Program class in the created file at all. In your case, you have a need to define the static/const property so you can change the newly created structure into the old one.
namespace WebApplication;
public class Program
{
public static string Test { get; private set; }
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
Program.Test = "approach1";
builder.Services.Configure<MyOptions>(x => x.Test = "approach2");
///
}
public class MyOptions
{
public string Test { get; set; }
}
I assumed that you have a need to set the value to the Program.Test field during runtime, so in the first approach, I used the static field with a private set; accessor instead of the constant.
In the second approach, I used the C# options feature to configure the MyOptions.Test field value, this will be very flexible and useful to write unit tests later. But, you need to inject the MyOptions class wherever is required.
In the below controller template, I specified how to access the configured values at Program.cs file, inside the Get method
public class TestController : ControllerBase
{
private readonly MyOptions _myOptions;
public TestController (IOptions<MyOptions> myOptions)
{
_myOptions = myOptions.Value;
}
public IActionResult Get()
{
string test1 = Program.Test;
string test2 = _myOptions.Test;
///
}
}
Add public partial class Program { } at the very end of your Program.cs file and add constant, property or whatever you like in there.
I'm migrating my ASP.net MVC project to core version. I have an extension class with method, that returns user's name by user id (Guid).
public static class IdentityHelpers
{
public static MvcHtmlString GetUserName(this HtmlHelper html, string id)
{
var manager = HttpContext.Current
.GetOwinContext().GetUserManager<AppUserManager>();
return new MvcHtmlString(manager.FindByIdAsync(id).Result.UserName);
}
}
As I'm rewriting this to .NET Core, I don't know how to get user manager instance here. Normally I would just inject this through DI, but I don't know what to do as I'm using extension method so I can't inject it.
How can I get UserManager in static class?
Things have changed in the new version. Access the current HttpContext via the HtmlHelper.ViewContext and from there you should be able to get access to an IServiceProvider that can be used to resolve services.
public static class IdentityHelpers {
public static MvcHtmlString GetUserName(this HtmlHelper html, string id) {
HttpContext context = html.ViewContext.HttpContext;
IServiceProvider services = context.RequestServices;
var manager = services.GetService<AppUserManager>();
return new MvcHtmlString(manager.FindByIdAsync(id).Result.UserName);
}
}
I wrote a Configuration provider according to this (Basic sample of Entity Framework custom provider) article.
There I also override the Set method of the base class to save the configuration back to the database.
public virtual void Set(string key, string value)
{
// save key / value pair to db.
}
This also works fine if I save the configuration like this:
IConfiguration configuration = ...
configuration["Key"] = "value";
But this does not work if I want to save it through a custom options class:
public class MyOptions
{
public string Key { get; set; }
}
public class HomeController : Controller
{
private readonly MyOptions _options;
public HomeController(IOptions<MyOptions> optionsAccessor)
{
_options = optionsAccessor.Value;
}
public IActionResult Index()
{
_options.Key = "new value"; // <-- not calling the set in my provider
}
}
Has someone an idea how to solve this?
Update
I know that the configuration framework is not able to track changes I did to MyOptions and therefor it cannot update it automagically. But is there an other way to trigger to save the configuration?
If your options types are simple (with no nested classes and collections), you can achieve storing changes to IConfigurationProvider after modifying you typed options class by using MutableOptions NuGet Package (I am the author of that package):
IOptionsMutator<SimpleOptions> optionsMutator;
optionsMutator.Mutate(options => options with { Bar = 42 });
Essentially what IOptionsMutator implementation does is:
Gets IOptionMonitor.CurrentValue.
Calls your mutator function on it to get modified options instance.
Compares which properties have changed using reflection.
Writes changed properties values to IConfiguration.
I have implemented a Custom ViewLocationExpander in a vnext project. I want to read a app setting value from the appsettings.json file in the ViewLocationExpander and hence the IOptions<> has been injected into the custom ViewLocationExpander 's constructor. However, while adding the custom ViewLocationExpander to the RazorViewEngine options an object of the ViewLocationExpander is required, which cannot be created due to the dependency.
Below is the code
public MyViewLocationExpander(IOptions<MyAppSettings> MyAppSettings)
{
var appSettings = MyAppSettings.Value;
client = appSettings.Client // client is a private field and is used in ExpandViewLocations function
}
MyAppSettings.cs is as below:
public class MyAppSettings
{
public string Client { get; set; }
}
In Startup.cs ConfigureServices method
services.Configure<RazorViewEngineOptions>(config =>
{
//config.ViewLocationExpanders.Add(new MyViewLocationExpander());
// MyViewLocationExpander cannot be created as it has a dependency on IOptions<MyAppSettings>
});
Any help on how to add the custom ViewLocationExpander to the RazorViewEngineOptions would be great.
One way to resolve services from the container is to resolve them in ExpandViewsMethod
using Microsoft.Extensions.DependencyInjection;
public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
{
var service = context.ActionContext.HttpContext.RequestServices.GetService<IService>();
}
I would like to access to the Active Directory from my company in many controllers from my ASP.NET vNext project, and I inserted the domain name into my config.json file, so I can access it from the Configuration class. I find it heavy to always instantiate a new Configuration object at every time I want to access to my config.json, is there any way through the IConfiguration API to access to the Configuration class initialized into the Startup class ?
An example of how you can do this:
Let's assume you have a config.json like below:
{
"SomeSetting1": "some value here",
"SomeSetting2": "some value here",
"SomeSetting3": "some value here",
"ActiveDirectory": {
"DomainName": "DOMAIN-NAME-HERE"
}
}
Create a POCO type having your option information:
public class ActiveDirectoryOptions
{
public string DomainName { get; set; }
}
In Startup.cs, when configuring services:
services.Configure<ActiveDirectoryOptions>(optionsSetup =>
{
//get from config.json file
optionsSetup.DomainName = configuration.Get("ActiveDirectory:DomainName");
});
In all controllers which want to get this config setting, do something like...Here the options is injected by the DI system:
public class HomeController : Controller
{
private readonly IOptions<ActiveDirectoryOptions> _activeDirectoryOptions;
public HomeController(IOptions<ActiveDirectoryOptions> activeDirectoryOptions)
{
_activeDirectoryOptions = activeDirectoryOptions;
}
public IActionResult Index()
{
string domainName = _activeDirectoryOptions.Options.DomainName;
........
}
}
Responding to the comment:
Dependency Injection was one of my option, but assume that you inject many repository inside your controller and a UserManager object because you want some user management, your constructor will be very busy. And all the time you want to use your controller, an IOptions object will be instanciate, but what if you just want to use this object in one method of your controller ?
There are couple of options that I can think of:
From within the action, you can do
var options = HttpContext.RequestServices.GetRequiredService<IOptions<ActiveDirectoryOptions>>().Options;
You can have a parameter to the action which is decorated with FromServicesAttribute. This attribute will cause the parameter value to be retrieved from the DI.
Example:
public IActionResult Index([FromServices] IOptions<ActiveDirectoryOptions> options)
I prefer #2 over #1 as in case of unit testing it gives you information on all dependent pieces.