HttpContext and Caching in .NET Core >= 1.0 - c#

I am trying to port some libraries from the old MVC5 System.Web based stack to .Net Core. One issue I am having is the changes in the caching. For example, in MVC5 I was able to read and write i18n related data:
[Code Snippet 1]
public static Dictionary<string, IEnumerable<CoreDictionaryResource>> DictionaryResourcesCache {
get { return (Dictionary<string, IEnumerable<CoreDictionaryResource>>)HttpContext.Current.Cache(string.Concat(_Dictionary, DictionaryID, CultureID)); }
set { HttpContext.Current.Cache(string.Concat(_Dictionary, DictionaryID, CultureID)) = value; }
}
However, I am reliably informed that System.Web and its HttpContext does not contain a Cache field. I can see a Current field and then a whole host of fields within this such as Application and Session but alas no Cache.
I've done the necessary in Startup.cs and the app is configured to use both in memory caching and sessions. I know that the sessions work as I have other POCOs cached using
[Code Snippet 2]
return System.Web.HttpContext.Current.Session.GetObject<User>("AuthenticatedUser");
where GetObject in an extension I created.
Am I barking up the wrong tree trying to use HttpContext to read out from the Cache or perhaps I need to use IDistributedCache as see here, here and on SO.
But really I just to port the method within [Code Snippet 1]...
Any pointer you can give on the new .Net Core with regards to caching would be really helpful.
Just FYI I do not want any logic in Controllers, nor in Views. The application I am building usings separate DLLs for data access and logic so please don't post any examples with DI into the Controllers. This issue is more at an infrastrcture level before it hits the MVC stack.
Thanks guys and gals.

The in memory cache functionality is still there, it has just been moved around a bit. If you add
"Microsoft.Extensions.Caching.Memory": "1.1.0"
to you project.json file and the add
services.AddMemoryCache();
to you Startup.ConfigureServices method, you'll have set up a singleton memory cache instance that works pretty much like the old one did. You get to it via dependency injection so a controller with a constructor can get a instance.
public class HomeController: Controller
{
private IMemoryCache _cache;
public HomeController(IMemoryCache cache)
{
_cache = cache;
}
}
You can then use _cache in the class above to get to the globally available singleton class. There are other sorts of caches that you might want look at as well, including a Redis cache for out of process storage.

You should use the In Memory Cache only as HttpContext cache object was actually appdomain cache object although it is exposed using the HttpContext
From the msdn https://msdn.microsoft.com/en-us/library/system.web.httpcontext.cache(v=vs.110).aspx
There is one instance of the Cache class per application domain. As a result, the Cache object that is returned by the Cache property is the Cache object for all requests in the application domain.
We should use the
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Caching.Memory;
using System;
using Microsoft.Extensions.FileProviders;
namespace CachingQuestion
{
public class Startup
{
static string CACHE_KEY = "CacheKey";
public void ConfigureServices(IServiceCollection services)
{
//enabling the in memory cache
services.AddMemoryCache();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
var fileProvider = new PhysicalFileProvider(env.ContentRootPath);
app.Run(async context =>
{
//getting the cache object here
var cache = context.RequestServices.GetService<IMemoryCache>();
var greeting = cache.Get(CACHE_KEY) as string;
});
}
}
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}

My answer is focused on how to port caching implementation from old ASP.Net MVC to ASP.Net CORE the easiest and fastest way possible, and also focusing on not rely on dependency injection.
There is a perfect equivalent of ASP.Net MVC's HttpContext.Current.Cache. Its implementation was intended exactly to permit easy porting between frameworks. This is:
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. 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;

Related

Changing RedisCacheOptions after registering the StackExchangeRedisCache middleware

I'm trying to use Redis in my Asp.Net Core project. I added the packages StackExchange.Redis and Microsoft.Extensions.Caching.StackExchange.Redis to my project and the following code to register the middleware:
services.AddStackExchangeRedisCache(options =>
{
// do config
});
So far that works fine. But in this project Configuration settings are provided by a custom ConfigurationService which is pulling them from an external source. If changes in any of the configuration settings concerning Redis are detected I need to be able to reconfigure the StackExchangeRedisCache middleware.
How would I do that?
If I get the question right, does the case is... The project configuration setting was customize to fetch from another source by some interval ? Therefore, the setting on redis could change by time to time ?
If that's the case, I think we got to drop usage of AddStackExchangeRedisCache extensions. If you want to keep using this, here is some problem that got to solve:
Remove RedisCache singleton instance that previously register in ServiceCollection.
Remove config for RedisCacheOptions previously register in ServiceCollection
Register new RedisCacheOptions and RedisCache in ServiceCollection
Re-initialize ServiceProvider somehow
These step should be done atomicity, otherwise, exceptions likely to raise.
Quite cumbersome... does it ?
Another approach was to create a placeholder for IDistributedCache instance, register it as singleton and update the IDistributedCache instance if change was detected from the Configuration.
// Place holder, and register it as singleton
public class CacheAccessor : IDisposable
{
private RedisCache RedisCache { get; set; }
public void SetRedisCache(IOptions<RedisCacheOptions> options)
{
RedisCache?.Dispose();
RedisCache = new RedisCache(options);
}
public IDistributedCache LatestRedisCache => RedisCache;
public void Dispose()
{
RedisCache?.Dispose();
}
}
// Register it
services.AddSingleton<CacheAccessor>();
// Initialize it for the first time (write some extension or put it somewhere in program.cs would be nice.
// Run after the webhost instance got built and before it run)
// host was an instance of IHost
using var scope = host.Services.CreateScope();
var serviceProvider = scope.ServiceProvider;
serviceProvider.GetRequiredService<CacheAccessor>().SetRedisCache(Pass the option here).
// By time to time, if the config changed, extract the option and SetRedisCache again.
// And of course, get access to the cache would be difference
public IActionResult TestMethod([FromServices] CacheAccessor accessor)
{
var cache = accessor.LatestRedisCache;
}

.NET Core data protection - where is it used?

I am using .NET Core 3.1. I have the following code snippet in Startup.cs inside ConfigureServices:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddDataProtection()
.SetApplicationName("MyApplication")
.PersistKeysToFileSystem(new DirectoryInfo(Path.Combine("AppData", "Keys")));
// ...
}
As far as I understand, this is only needed if I want to protect some input with IDataProtectionProvider, like so:
public class MyClass
{
readonly IDataProtectionProvider _rootProvider;
public MyClass(IDataProtectionProvider rootProvider)
{
_rootProvider = rootProvider;
}
public void Test()
{
IDataProtector protector = provider.CreateProtector("Test123");
string protectedPayload = protector.Protect("Hello world");
Console.WriteLine($"Protect returned: {protectedPayload}");
}
}
However, we are not using this functionality aynwhere in our application. Is it safe to remove AddDataProtection from ConfigureServices? Does any part of .NET Core application (TempData, AntiForgery tokens, ...) use it behind the scenes (so that Visual Studio doesn't find string IDataProtectionProvider)?
According to this document, here's a sentence said:
It cannot directly be used to protect or unprotect data. Instead, the
consumer must get a reference to an IDataProtector by calling
IDataProtectionProvider.CreateProtector(purpose)
hence based on this saying, it seems that data protection doesn't work as _rootProvider didn't be called in some place. And your another misgiving is some default setting or effect may work in some other places, you may refer to this document to see the common usage of data protection api.
And in my opinion, it's really hard to say it have no influence in your project as if by any chance we ignore some thing, that may lead to something unexpected. So if your app runs well now, why not just leave it there.

Configure Asp.net MVC project and EntityFramework to use Redis as Cache provider

I want to use EF Second Level Cache and change the default cache provider of EF in an ASP.NET MVC project to use Redis instead of its InMemory cache provider.
I have the following sample code for MVC Core:
// Add Redis cache service provider
var jss = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
const string redisConfigurationKey = "redis";
services.AddSingleton(typeof(ICacheManagerConfiguration),
new CacheManager.Core.ConfigurationBuilder()
.WithJsonSerializer(serializationSettings: jss, deserializationSettings: jss)
.WithUpdateMode(CacheUpdateMode.Up)
.WithRedisConfiguration(redisConfigurationKey, config =>
{
config.WithAllowAdmin()
.WithDatabase(0)
.WithEndpoint("localhost", 6379);
})
.WithMaxRetries(100)
.WithRetryTimeout(50)
.WithRedisCacheHandle(redisConfigurationKey)
.WithExpiration(ExpirationMode.Absolute, TimeSpan.FromMinutes(10))
.Build());
services.AddSingleton(typeof(ICacheManager<>), typeof(BaseCacheManager<>));
My project is not MVC Core, it is MVC. Also I use StructureMaps as
IOC.
Now I have 2 question:
How should I configure EF in my project to use Redis as its Second Level Cache Provider (I'm using this package)?
Can I use the saved data in the cache (Redis) in this way in a separate project on my computer (Sharing cache of an MVC application between several application)?
Redis in StructureMap
There is a fair amount of code in StructureMap to get that service registered. Singletons are a little different in that system, and you are better off using a "factory" pattern to create and manage your redis cache. SM has an auto-factory that will do the trick.
If you'd like, I can post some basic code here. But - you'd need to test in your environment to get it to work (I'm not a fan of posting theoretical code here on stack).
If you haven't used redis before, I'd create a console app and make sure that you can configure it and get it to run before inserting IOC and your .Net MVC app into the picture (you're a strong coder, so I'm not tell you something you don't already know there).
Can your other apps use the Redis cache?
Yes. As long as they have access to the endpoint you create (in the code above, it's listening on localhost:6379).
You can just use the same "config" setup that you use in your MVC app. When you do, both apps will hit the same redis "server" and share the same cached objects.
Think of redis the same way you think of your database: as long as both systems use the same "config" (eg - connect string), then they can access the same data.
redis is super-fast and really cool. You're gonna love it!
Totally Untested Sample Code
Here's a quick overview just so my comment on code structure make more sense. If others have suggestions on adjustments, feel free to add to comments and I'll update.
public interface ICacheManagerConfigFactory {
ICacheManagerConfiguration CreateCacheManager();
}
public CacheManagerFactory:ICacheManagerConfigFactory {
private static ICacheManagerConfiguration _cache;
private static object syncRoot = new Object();
public ICacheManagerConfiguration CreateCacheManager() {
if(_cache!=null) { return _cache; }
//locking to make sure that we only create 1 _cache object (thread-safe)
lock(syncRoot) {
//idiot-proofing our thread-safe code
if(_cache!=null) { return _cache; }
//create _cache if it doesn't already exist
var jss = new JsonSerializerSettings {
NullValueHandling = NullValueHandling.Ignore,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
_cache= new CacheManager.Core.ConfigurationBuilder()
.WithJsonSerializer(serializationSettings: jss, deserializationSettings: jss)
... etc ...
.Build();
}
return _cache;
}
}
And, this is just a quick test method which proves we can pull the static config from memory. It's also pulled from SM's documentation for auto-factory.
public void Simple_factory_creation() {
var container = new Container(cfg => {
cfg.For<ICacheManagerConfiguration>().Use<CacheManagerFactory>();
cfg.For<ICacheManagerConfigFactory>().CreateFactory();
});
var cache = container.GetInstance<ICacheManagerConfiguration>();
}

IOptions Injection

It seems to me that it's a bad idea to have a domain service require an instance of IOptions<T> to pass it configuration. Now I've got to pull additional (unnecessary?) dependencies into the library. I've seen lots of examples of injecting IOptions all over the web, but I fail to see the added benefit of it.
Why not just inject that actual POCO into the service?
services.AddTransient<IConnectionResolver>(x =>
{
var appSettings = x.GetService<IOptions<AppSettings>>();
return new ConnectionResolver(appSettings.Value);
});
Or even use this mechanism:
AppSettings appSettings = new AppSettings();
Configuration.GetSection("AppSettings").Bind(appSettings);
services.AddTransient<IConnectionResolver>(x =>
{
return new ConnectionResolver(appSettings.SomeValue);
});
Usage of the settings:
public class MyConnectionResolver
{
// Why this?
public MyConnectionResolver(IOptions<AppSettings> appSettings)
{
...
}
// Why not this?
public MyConnectionResolver(AppSettings appSettings)
{
...
}
// Or this
public MyConnectionResolver(IAppSettings appSettings)
{
...
}
}
Why the additional dependencies? What does IOptions buy me instead of the old school way of injecting stuff?
Technically nothing prevents you from registering your POCO classes with ASP.NET Core's Dependency Injection or create a wrapper class and return the IOption<T>.Value from it.
But you will lose the advanced features of the Options package, namely to get them updated automatically when the source changes as you can see in the source here.
As you can see in that code example, if you register your options via services.Configure<AppSettings>(Configuration.GetSection("AppSettings")); it will read and bind the settings from appsettings.json into the model and additionally track it for changes. When appsettings.json is edited, and will rebind the model with the new values as seen here.
Of course you need to decide for yourself, if you want to leak a bit of infrastructure into your domain or pass on the extra features offered by the Microsoft.Extensions.Options package. It's a pretty small package which is not tied to ASP.NET Core, so it can be used independent of it.
The Microsoft.Extensions.Options package is small enough that it only contains abstractions and the concrete services.Configure overload which for IConfiguration (which is closer tied to how the configuration is obtained, command line, json, environment, azure key vault, etc.) is a separate package.
So all in all, its dependencies on "infrastructure" is pretty limited.
In order to avoid constructors pollution of IOptions<>:
With this two simple lines in startup.cs inside ConfigureServices you can inject the IOptions value like:
public void ConfigureServices(IServiceCollection services)
{
//...
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
services.AddScoped(cfg => cfg.GetService<IOptions<AppSettings>>().Value);
}
And then use with:
public MyService(AppSettings appSettings)
{
...
}
credit
While using IOption is the official way of doing things, I just can't seem to move past the fact that our external libraries shouldn't need to know anything about the DI container or the way it is implemented. IOption seems to violate this concept since we are now telling our class library something about the way the DI container will be injecting settings - we should just be injecting a POCO or interface defined by that class.
This annoyed me badly enough that I've written a utility to inject a POCO into my class library populated with values from an appSettings.json section. Add the following class to your application project:
public static class ConfigurationHelper
{
public static T GetObjectFromConfigSection<T>(
this IConfigurationRoot configurationRoot,
string configSection) where T : new()
{
var result = new T();
foreach (var propInfo in typeof(T).GetProperties())
{
var propertyType = propInfo.PropertyType;
if (propInfo?.CanWrite ?? false)
{
var value = Convert.ChangeType(configurationRoot.GetValue<string>($"{configSection}:{propInfo.Name}"), propInfo.PropertyType);
propInfo.SetValue(result, value, null);
}
}
return result;
}
}
There's probably some enhancements that could be made, but it worked well when I tested it with simple string and integer values. Here's an example of where I used this in the application project's Startup.cs -> ConfigureServices method for a settings class named DataStoreConfiguration and an appSettings.json section by the same name:
services.AddSingleton<DataStoreConfiguration>((_) =>
Configuration.GetObjectFromConfigSection<DataStoreConfiguration>("DataStoreConfiguration"));
The appSettings.json config looked something like the following:
{
"DataStoreConfiguration": {
"ConnectionString": "Server=Server-goes-here;Database=My-database-name;Trusted_Connection=True;MultipleActiveResultSets=true",
"MeaningOfLifeInt" : "42"
},
"AnotherSection" : {
"Prop1" : "etc."
}
}
The DataStoreConfiguration class was defined in my library project and looked like the following:
namespace MyLibrary.DataAccessors
{
public class DataStoreConfiguration
{
public string ConnectionString { get; set; }
public int MeaningOfLifeInt { get; set; }
}
}
With this application and libraries configuration, I was able to inject a concrete instance of DataStoreConfiguration directly into my library using constructor injection without the IOption wrapper:
using System.Data.SqlClient;
namespace MyLibrary.DataAccessors
{
public class DatabaseConnectionFactory : IDatabaseConnectionFactory
{
private readonly DataStoreConfiguration dataStoreConfiguration;
public DatabaseConnectionFactory(
DataStoreConfiguration dataStoreConfiguration)
{
// Here we inject a concrete instance of DataStoreConfiguration
// without the `IOption` wrapper.
this.dataStoreConfiguration = dataStoreConfiguration;
}
public SqlConnection NewConnection()
{
return new SqlConnection(dataStoreConfiguration.ConnectionString);
}
}
}
Decoupling is an important consideration for DI, so I'm not sure why Microsoft have funnelled users into coupling their class libraries to an external dependency like IOptions, no matter how trivial it seems or what benefits it supposedly provides. I would also suggest that some of the benefits of IOptions seem like over-engineering. For example, it allows me to dynamically change configuration and have the changes tracked - I've used three other DI containers which included this feature and I've never used it once... Meanwhile, I can virtually guarantee you that teams will want to inject POCO classes or interfaces into libraries for their settings to replace ConfigurationManager, and seasoned developers will not be happy about an extraneous wrapper interface. I hope a utility similar to what I have described here is included in future versions of ASP.NET Core OR that someone provides me with a convincing argument for why I'm wrong.
I can't stand the IOptions recommendation either. It's a crappy design to force this on developers. IOptions should be clearly documented as optional, oh the irony.
This is what I do for my configuraition values
var mySettings = new MySettings();
Configuration.GetSection("Key").Bind(mySettings);
services.AddTransient(p => new MyService(mySettings));
You retain strong typing and don't need need to use IOptions in your services/libraries.
You can do something like this:
services.AddTransient(
o => ConfigurationBinder.Get<AppSettings>(Configuration.GetSection("AppSettings")
);
Using Net.Core v.2.2, it's worked for me.
Or then, use IOption<T>.Value
It would look something like this
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
I would recommend avoiding it wherever possible. I used to really like IOptions back when I was working primarily with core but as soon as you're in a hybrid framework scenario it's enough to drive you spare.
I found a similar issue with ILogger - Code that should work across frameworks won't because I just can't get it to bind properly as the code is too dependent on the DI framework.

Application startup code in ASP.NET Core

Reading over the documentation for ASP.NET Core, there are two methods singled out for Startup: Configure and ConfigureServices.
Neither of these seemed like a good place to put custom code that I would like to run at startup. Perhaps I want to add a custom field to my DB if it doesn't exist, check for a specific file, seed some data into my database, etc. Code that I want to run once, just at app start.
Is there a preferred/recommended approach for going about doing this?
I agree with the OP.
My scenario is that I want to register a microservice with a service registry but have no way of knowing what the endpoint is until the microservice is running.
I feel that both the Configure and ConfigureServices methods are not ideal because neither were designed to carry out this kind of processing.
Another scenario would be wanting to warm up the caches, which again is something we might want to do.
There are several alternatives to the accepted answer:
Create another application which carries out the updates outside of your website, such as a deployment tool, which applies the database updates programmatically before starting the website
In your Startup class, use a static constructor to ensure the website is ready to be started
Update
The best thing to do in my opinion is to use the IApplicationLifetime interface like so:
public class Startup
{
public void Configure(IApplicationLifetime lifetime)
{
lifetime.ApplicationStarted.Register(OnApplicationStarted);
}
public void OnApplicationStarted()
{
// Carry out your initialisation.
}
}
This can be done by creating an IHostedService implementation and registering it using IServiceCollection.AddHostedService<>() in ConfigureServices() in your startup class.
Example
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
public class MyInitializer : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken)
{
// Do your startup work here
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
// We have to implement this method too, because it is in the interface
return Task.CompletedTask;
}
}
using Microsoft.Extensions.DependencyInjection;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddHostedService<MyInitializer>();
}
}
Notes
The main application will not be started until after your code has finished executing.
Constructor dependency injection is available to the IHostedService implementation.
I can recommend this blog post for more info, and an example of how to use async: https://andrewlock.net/running-async-tasks-on-app-startup-in-asp-net-core-3/
For more background reading, see this discussion: https://github.com/dotnet/aspnetcore/issues/10137
Basically there are two entry points for such custom code at startup time.
1.) Main method
As a ASP.NET Core application has got the good old Main method as entry point you could place code before the ASP.NET Core startup stuff, like
public class Program
{
public static void Main(string[] args)
{
// call custom startup logic here
AppInitializer.Startup();
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
2.) Use your Startup class
As you already stated in your question is the Configure and ConfigureServices a good place for your custom code.
I would prefer the Startup class. From the runtime perspective it does not matter, if the call is called in startup or somewhere else before the host.Run() call. But from a programmer's point of view who is accustomed to the ASP.NET framework then his first look for such logic would be the Startup.cs file. All samples and templates put there the logic for Identity, Entity Framework initialization and so on. So as a convention I recommend to place the initialization stuff there.

Categories