Static class/values available only for current connection/user - c#

I have Asp.NET core 2.I'm using a static class to keep some info available about current user (accessible in all controllers,etc),but seem to me,sometimes,static values from a user are visible to another.how to make static values available only for current connection/user?
public static class globalVariables
{
public static string Value1;
public static string getUserValue1()..
{
....
}
......
I'm using it that way:
globalVariables.getUserValue1(..)
thanks

Seems the problem solved using sessions,thanks to Henk and also info from that link:
Open up startup.cs and add the AddSession() and AddDistributedMemoryCache() lines to the ConfigureServices(IServiceCollection services)
// Add MVC services to the services container.
services.AddMvc();
services.AddDistributedMemoryCache(); // Adds a default in-memory implementation of IDistributedCache
services.AddSession();
Next, we’ll tell ASP.NET Core to use a Memory Cache to store the session data. Add the UseSession() call below to the Configure(IApplicationBulider app, ...)
// IMPORTANT: This session call MUST go before UseMvc()
app.UseSession();
// Add MVC to the request pipeline.
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
Where’s the Session variable gone?
Relax it’s still there, just not where you think it is. You can now find the session object by using HttpContext.Session. HttpContext is just the current HttpContext exposed to you by the Controller class.
If you’re not in a controller, you can still access the HttpContext by injecting IHttpContextAccessor.
Let’s go ahead and add sessions to our Home Controller:
using Microsoft.AspNetCore.Http; // Needed for the SetString and GetString extension methods
public class HomeController : Controller
{
public IActionResult Index()
{
HttpContext.Session.SetString("Test", "Ben Rules!");
return View();
}
public IActionResult About()
{
ViewBag.Message = HttpContext.Session.GetString("Test");
return View();
}
}
You’ll see the Index() and About() methods making use of the Session object. It’s pretty easy here, just use one of the Set() methods to store your data and one of the Get() methods to retrieve it.
Just for fun, let’s inject the context into a random class:
public class SomeOtherClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
private ISession _session => _httpContextAccessor.HttpContext.Session;
public SomeOtherClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void TestSet()
{
_session.SetString("Test", "Ben Rules!");
}
public void TestGet()
{
var message = _session.GetString("Test");
}
}

Related

Unable to resolve service for type 'Microsoft.Extensions.Caching.Memory.IMemoryCache'

I have a WEB API using .NET 6. I used MemoryCache to store data. But when I run the Api I get the following error:
Unable to resolve service for type 'Microsoft.Extensions.Caching.Memory.IMemoryCache'
myContoroler:
public class myContoroler : Controller
{
private readonly MemoryCache _memoryCache = new MemoryCache(optionsAccessor: null);
[HttpPost]
public async Task<IActionResult> myAPI(Model modelS)
{
var users = _memoryCache.Get("UserData")
...
}
}
in your Startup.cs file, you need to introduce MemoryCache:
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
.
.
.
}
If there is no Startup.cs file, this should be done in Program.cs (in .NET 6.0, Startup.cs class is removed and Program.cs class is the place where register the dependencies of the application and the middleware.)
builder.Services.AddMemoryCache();
It is also recommended to use dependency injection to use the MemoryCache in the controller:
public class myContoroler : Controller
{
private readonly MemoryCache _memoryCache;
public myContoroler(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
[HttpPost]
public async Task<IActionResult> myAPI(Model modelS)
{
var users = _memoryCache.Get("UserData")
...
}
}

Is it the right way to use IHttpContextAccessor in a class?

I am working on an Asp.Net Core web application running on .NET Core 3.1.
For a wizard form, I am relying on a User class that holds the data stored in session. As we can access HttpContext from a controller, I had this GetUser() method inside my controller to retrieve session data when necessary :
private User GetUser()
{
var session = HttpContext.Session.GetComplexData<User>("user");
if (session == null)
{
User user = new User();
HttpContext.Session.SetComplexData("user", user);
}
return (User)HttpContext.Session.GetComplexData<User>("user");
}
Then I wanted to move that GetUser() method inside the User class so I used DI to provide that class's ctor with IHttpContextAccessor :
public class User
{
private readonly IHttpContextAccessor _contextAccessor;
public User(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
// fields removed here to keep it short
public User GetUser()
{
var session = _contextAccessor.HttpContext.Session.GetComplexData<User>("user");
if (session == null)
{
User user = new User(_contextAccessor);
_contextAccessor.HttpContext.Session.SetComplexData("user", user);
}
return (User)_contextAccessor.HttpContext.Session.GetComplexData<User>("user");
}
}
But then, in order to use that GetUser() method in my controller(s), I also have to provide this controller's ctor with IHttpContextAccessor as my User needs it to be instantiated :
public class NewController : Controller
{
private readonly IHttpContextAccessor _contextAccessor;
public NewController(IHttpContextAccessor contextAccessor)
{
_contextAccessor = contextAccessor;
}
[HttpGet]
public IActionResult Step1()
{
// Get session data
User user = new User(_contextAccessor); // <--
user = user.GetUser();
// Other code...
// ---
return View("Step1");
}
}
So my question is... Is it the right way to do it?
Or shall I just stick with my very first GetMethod() inside the controller without bothering with DI and duplicate it in other controllers if I need to access session there...?
Or perhaps you can show me something I don't know that would be more a good practice..
THanks
You don't want your User class to be tightly coupled to the HttpContext so your first method would be better. However, to improve readability and reusability (is that a word?) you could create users through an interface IUserManager and use dependency injection to provide the session there. The class would look something like this:
public class UserManager : IUserManager
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserManager(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public User Create()
{
// create user here using session in _httpContextAccessor
}
}
In order for this to work, don't forget to register the necessary dependencies in your container:
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddTransient<IUserManager, UserManager>();
}
Source material: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-3.1#use-httpcontext-from-custom-components.

asp.net core Web API controller stop responding with error 500

I'm making a PoC for a search functionality. Ihave not done web API for a while and now Istarted from this tutorial and others, changing what I needed to for connecting my database and so on.
This is one of those things that worked on friday, and on monday morning it just don't.
I have created a web API project which has a valuesController already scaffolded. So I created a new one called searchController. I wrotea method that simply returned a string and looked like this:
[Produces("application/json")]
[Route("api/Search")]
public class SearchController : Controller
{
private readonly DbContext _context;
public SearchController(DbContext context)
{
_context = context;
}
// GET api/Search/search criteria
[HttpGet("{q}")]
public string Search(string q)
{
return $"this will be your answer to: {q}";
}
}
I don't know why it worked and why it is not working now. I have changed it to look exactly as the values controller which I post below.
Values Controller
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace AbAeterno.Controllers
{
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public void Post([FromBody]string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
}
and Search Controller
using AbAeterno.Models;
using Microsoft.AspNetCore.Mvc;
namespace AbAeterno.Controllers
{
[Route("api/[controller]")]
public class SearchController : Controller
{
private readonly DbContext _context;
public SearchController(DbContext context)
{
_context = context;
}
// GET api/Search/search criteria
[HttpGet]
public string Get()
{
return "this method is alive";
}
}
}
When I run the solution, the default controller is values and it is working, when I changed url to
http://localhost:52457/api/search/
it says the page does not work, error 500, in chrome.
Update startup.cs
I have found two ways of registering connection string, I wrote both but the second one, using DI, does not work as expected and when calling database, connection string is null. This is how I have it.
public void ConfigureServices(IServiceCollection services)
{
//Configure DB connection strings
DbContext.ConnectionString = Configuration.GetConnectionString("DatabaseEF");
//services.AddDbContext<DbContext>(opt => opt.UseSqlServer(Configuration.GetConnectionString("DatabaseEF")));
// Add framework services.
services.AddMvc();
}
Solution
I removed the constructor and private variable and it worked again, as suggested by #Yuri S
Although there is a solution section in the question, that one is not considering Dependency Injection and should be taken as a temporary one.
If you want to use dependency injection, your files should look like this:
Db Context
public partial class MyDbContext : DbContext
{
public virtual DbSet<Table> Table { get; set; }
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
//Configure DB connection strings
services.AddDbContext<MyDbContext>(opt => opt.UseSqlServer(Configuration.GetConnectionString("MyDbEF")));
// Add framework services.
services.AddMvc();
}
With those two changes your connection string is registered in the DI manager components, so you can create a constructor in your controller that receives, injected, the context object configured with your connection string. like this
Controller constructor
private readonly MyDbContext _context;
public SearchController(MyDbContext context)
{
_context = context;
}

Asp.Net Core: Use memory cache outside controller

In ASP.NET Core its very easy to access your memory cache from a controller
In your startup you add:
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
}
and then from your controller
[Route("api/[controller]")]
public class MyExampleController : Controller
{
private IMemoryCache _cache;
public MyExampleController(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
[HttpGet("{id}", Name = "DoStuff")]
public string Get(string id)
{
var cacheEntryOptions = new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromHours(1));
_cache.Set("key", "value", cacheEntryOptions);
}
}
But, how can I access that same memory cache outside of the controller. eg. I have a scheduled task that gets initiated by HangFire, How do I access the memorycache from within my code that starts via the HangFire scheduled task?
public class ScheduledStuff
{
public void RunScheduledTasks()
{
//want to access the same memorycache here ...
}
}
Memory cache instance may be injected to the any component that is controlled by DI container; this means that you need configure ScheduledStuff instance in the ConfigureServices method:
public void ConfigureServices(IServiceCollection services) {
services.AddMemoryCache();
services.AddSingleton<ScheduledStuff>();
}
and declare IMemoryCache as dependency in ScheduledStuff constructor:
public class ScheduledStuff {
IMemoryCache MemCache;
public ScheduledStuff(IMemoryCache memCache) {
MemCache = memCache;
}
}
I am bit late here, but just wanted to add a point to save someone's time. You can access IMemoryCache through HttpContext anywhere in application
var cache = HttpContext.RequestServices.GetService<IMemoryCache>();
Please make sure to add MemeoryCache in Startup
services.AddMemoryCache();
There is another very flexible and easy way to do it is using System.Runtime.Caching/MemoryCache
System.Runtime.Caching/MemoryCache:
This is pretty much the same as the old day's ASP.Net MVC's HttpRuntime.Cache. You can use it on ASP.Net CORE without any dependency injection, in any class you want to. This is how to use it:
// First install 'System.Runtime.Caching' (NuGet package)
// Add a using
using System.Runtime.Caching;
// To get a value
var myString = MemoryCache.Default["itemCacheKey"];
// To store a value
MemoryCache.Default["itemCacheKey"] = myString;

Getting a Configuration Value in ASP.NET 5 (vNext)

I am struggling with some concepts in ASP.NET 5 (vNext).
One of those is the Dependency Injection approach used for configuration. It seems like I have to pass a parameter all the way through the stack. I'm probably misunderstanding something or doing it wrong.
Imagine I have a config property named "contactEmailAddress". I'll use that config property to send an email when a new order is placed. With that scenario in mind, my ASP.NET 5 stack will look like this:
Startup.cs
public class Startup
{
public IConfiguration Configuration { get; set; }
public Startup(IHostingEnvironment environment)
{
var configuration = new Configuration().AddJsonFile("config.json");
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppSettings>(Configuration.GetSubKey("AppSettings"));
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseErrorPage();
app.UseMvc(routes =>
{
routes.MapRoute("default",
"{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index" });
}
);
app.UseWelcomePage();
}
AppSettings.cs
public class AppSettings
{
public string ContactEmailAddress { get; set; }
}
config.json
{
"AppSettings": {
"ContactEmailAddress":"support#mycompany.com"
}
}
OrderController.cs
[Route("orders")]
public class OrdersController : Controller
{
private IOptions<AppSettings> AppSettings { get; set; }
public OrdersController(IOptions<AppSettings> appSettings)
{
AppSettings = appSettings;
}
[HttpGet("new-order")]
public IActionResult OrderCreate()
{
var viewModel = new OrderViewModel();
return View(viewModel);
}
[HttpPost("new-order")]
public IActionResult OrderCreate(OrderViewModel viewModel)
{
return new HttpStatusCodeResult(200);
}
}
Order.cs
public class Order()
{
public void Save(IOptions<AppSettings> appSettings)
{
// Send email to address in appSettings
}
public static List<Order> FindAll(IOptions<AppSettings> appSettings)
{
// Send report email to address in appSettings
return new List<Order>();
}
}
As the example above shows, I'm passing AppSettings through the entire stack. This does not feel correct. To further my worries, this approach will not work if I'm attempt to use a third-party library that needs to access configuration settings. How can a third-party library access configuration settings? Am I misunderstanding something? Is there a better way to do this?
You are entangling 2 different run time resource provider, AppSettings and Dependency Injection.
AppSettings, provides run-time access to Application specific values like UICulture strings, Contact Email, etc.
DI Containers are factories that Manage access to Services and their lifetime scopes. For example, If a MVC Controller needed access to your EmailService, you would configure
public void ConfigureServices(IServiceCollection services)
{
// Add all dependencies needed by Mvc.
services.AddMvc();
// Add EmailService to the collection. When an instance is needed,
// the framework injects this instance to the objects that needs it
services.AddSingleton<IEmailService, EmailService>();
}
Then, if our Home Controller needs access to your EmailService, we add a dependency on it's Interface by adding it as a parameter to the Controller constructor
public class HomeController : Controller
{
private readonly IEmailService _emailService;
private readonly string _emailContact;
/// The framework will inject an instance of an IEmailService implementation.
public HomeController(IEmailService emailService)
{
_emailService = emailService;
_emailContact = System.Configuration.ConfigurationManager.
AppSettings.Get("ContactEmail");
}
[HttpPost]
public void EmailSupport([FromBody] string message)
{
if (!ModelState.IsValid)
{
Context.Response.StatusCode = 400;
}
else
{
_emailService.Send(_emailContact, message);
The purpose of Dependancy Injection is to manage access and lifetimes of services.
In the previous example, in our Application Startup, we configured the DI Factory to associate application requests for IEmailService with EmailService. So when our Controllers are instantiate by the MVC Framework, the framework notices that our Home Controller expects IEmailService, the framework checks our Application Services Collection. It finds mapping instructions and Inject a Singleton EmailService (a descendant of the occupying Interface) into our Home Controller.
Super Polymorphic Factorific - alodocious!
Why is this important?
If your contact email changes, you change the AppSetting value and are done. All requests for "ContactEmail" from ConfigurationManager are Globally changed. Strings are easy. No need for Injection when we can just hash.
If your Repository, Email Service, Logging Service, etc changes, you want a Global way to change all references to this service. Service reference aren't as easily transferred as immutable string literals. Service instantiation should be handled by a factory to configure the Service's settings and dependencies.
So, in a year you develop a RobustMailService:
Class RobustMailService : IEmailService
{
....
}
As long as your new RobustMailService inherits and implements the IEmailService Interface, you can substitute all references to your mail service Globally by changing :
public void ConfigureServices(IServiceCollection services)
{
// Add all dependencies needed by Mvc.
services.AddMvc();
// Add RobustMailService to the collection. When an instance is needed,
// the framework injects this instance to the objects that needs it
services.AddSingleton<IEmailService, RobustMailService>();
}
This can be achieved using IOptions assessor service as it seems you were trying.
We can begin by creating a class with all of the variables that your controller needs from configuration.
public class VariablesNeeded
{
public string Foo1{ get; set; }
public int Foo2{ get; set; }
}
public class OtherVariablesNeeded
{
public string Foo1{ get; set; }
public int Foo2{ get; set; }
}
We now need to tell the middleware that the controller needs this class in the constructor of the controller using dependency injection, we do this using IOptions accessor service.
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
public class MyController: Controller{
private readonly VariablesNeeded _variablesNeeded;
public MyController(IOptions<VariablesNeeded> variablesNeeded) {
_variablesNeeded= variablesNeeded.Value;
}
public ActionResult TestVariables() {
return Content(_variablesNeeded.Foo1 + _variablesNeeded.Foo2);
}
}
To get the variables from your configuration files, we create a constructor for the startup class, and a configuration property.
public IConfigurationRoot Configuration { get; }
public Startup(IHostingEnvironment env)
{
/* This is the fairly standard procedure now for configuration builders which will pull from appsettings (potentially with an environmental suffix), and environment variables. */
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
Now we need to make sure the pipeline actually supplies the controller with this service.
In your ConfigureServices method in your Startup class, you want to use the Options middleware, and inject an object of type VariablesNeeded in to the pipeline.
public void ConfigureServices(IServiceCollection services)
{
// Tells the pipeline we want to use IOption Assessor Services
services.AddOptions();
// Injects the object VariablesNeeded in to the pipeline with our desired variables
services.Configure<VariablesNeeded>(x =>
{
x.Foo1 = Configuration["KeyInAppSettings"]
x.Foo2 = Convert.ToInt32(Configuration["KeyParentName:KeyInAppSettings"])
});
//You may want another set of options for another controller, or perhaps to pass both to our "MyController" if so, you just add it to the pipeline
services.Configure<OtherVariablesNeeded>(x =>
{
x.Foo1 = "Other Test String",
x.Foo2 = 2
});
//The rest of your configure services...
}
For more information see the chapter on Using Options and configuration objects in the ASPCore Docs

Categories

Resources