use an in memory collection in dotnet core - how to make available to all classes - caching

IN dotnet core project, how do you make a collection available in memory to any class at any given time?
I was thinking of the following approach:
public interface IInMemoryCache
{
public void Setup();
}
public class InMemoryCache : IInMemoryCache
{
private List<MyObject> cache;
public void Setup() // entered once when app is started
{
cache = new List<MyObject>() {
new MyObject("param1", "param2"),
new MyObject("param3", "param4")
}
public IList<MyObject> GetCollection()
{
return this.collection;
}
}
and then in Startup:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IInMemoryCache cache)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
cache.Setup();
etc
and also:
public void ConfigureServices(IServiceCollection services) {
services.AddSingleton<IInMemoryCache, InMemoryCache>();
Is this a good approach, does it mean I can inject IInMemoryCache in any class and able to access the cache object? - for the whole lifetime of the app (meaning, while it's up and running, if I restart it, the collection is expected to again initialise from running the Setup method)
So right now in any class I just add: IInMemoryCache cache
and then cache.GetCollection() to retrieve the collection (that was setup ONCE at app startup).
is there a better way like a native feature for caching a collection available to all classes?

Related

Replace default IoC container in MvvmCross

Since MvvmCross v7 sticks on its own IoC container, I would like to replace it with the .NET Core one in order to have an easier life when registering third party libraries such as IHttpClientFactory, Polly, Automapper, etc. through already built-in extensions methods.
To achieve this, I've successfully created a class that implementas MvxSingleton<IMvxIoCProvider> described as follow:
public class HostingAdapter : MvxSingleton<IMvxIoCProvider>, IMvxIoCProvider
{
private IServiceProvider ServiceProvider;
private IServiceCollection ServiceCollection;
public HostingAdapter()
{
var host = Host
.ConfigureServices((context, serviceCollection) =>
{
// Configure local services
ConfigureServices(context, serviceCollection);
ServiceCollection = serviceCollection;
ServiceProvider = ServiceCollection.BuildServiceProvider();
})
.Build();
}
public void RegisterType<TFrom, TTo>() where TFrom : class where TTo : class, TFrom
{
ServiceCollection.AddTransient<TFrom, TTo>();
ServiceProvider = ServiceCollection.BuildServiceProvider();
}
public T GetSingleton<T>() where T : class
{
return ServiceProvider.GetRequiredService<T>();
}
public object GetSingleton(Type type)
{
return ServiceProvider.GetRequiredService(type);
}
.. and all the required methods requested by the interface.
Then on the platform specific side I override the IoC creation as follow:
protected override IMvxIoCProvider CreateIocProvider()
{
var hostingAdapter = new HostingAdapter();
return hostingAdapter;
}
The code seems to work but as soon as the app starts Mvx registers its own "extra" services such as the IMvxLoggerProvider, IMvxSettings and so on. And here issues come:
ServiceProvider = ServiceCollection.BuildServiceProvider(); is called during the Host initialization but Mvx still continue to register services after that. This means IServiceProvider is not 'in sync' with IServiceCollection and a new ServiceCollection.BuildServiceProvider(); call is needed. I temporarily solved updating the provider at each collection registration (like the code above) but I'm aware this affects performances. Anyone knows how to workaround this?
There are plenty of Mvx services that are not registered so the app fails to start. These are the IMvxLogProvider, IMvxAndroidLifetimeMonitor, IIMvxSettings, IMvxStart, etc. I just wonder, why? How can let Mvx handle the registration in my container of all what it needs to start? I partially solved some of them such as the logger thing replacing the default with a custom one, but other callbacks like InitializeLifetimeMonitor are called too late for being registered.
Do I need to change anything in my MvxApplication than the most standard implementation?
Am I really forced to replace the standard IoC container? How can I handle the IServiceCollection's extension methods that 3rd party libraries expose like services.AddHttpClient();?
If it needs, I am on Xamarin classic using the Droid platform. Thanks
Deliberately inspired by Unity.Microsoft.DependencyInjection repository I've workarounded this approaching the problem the other way round: instead of replacing the default IoC container, I manually initialize an IServiceCollection instance and I add it to the Mvx's IoC provider.
To achieve this, I've used the following code:
public class App : MvxApplication
{
public override void Initialize()
{
base.Initialize();
InitializeServiceCollection();
CreatableTypes()
.EndingWith("Service")
.AsInterfaces()
.RegisterAsLazySingleton();
RegisterAppStart<HomeViewModel>();
}
private static void InitializeServiceCollection()
{
IServiceCollection serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
MapServiceCollectionToMvx(serviceProvider, serviceCollection);
}
private static void ConfigureServices(IServiceCollection serviceCollection)
{
serviceCollection.AddHttpClient();
}
private static void MapServiceCollectionToMvx(IServiceProvider serviceProvider,
IServiceCollection serviceCollection)
{
foreach (var serviceDescriptor in serviceCollection)
{
if (serviceDescriptor.ImplementationType != null)
{
Mvx.IoCProvider.RegisterType(serviceDescriptor.ServiceType, serviceDescriptor.ImplementationType);
}
else if (serviceDescriptor.ImplementationFactory != null)
{
var instance = serviceDescriptor.ImplementationFactory(serviceProvider);
Mvx.IoCProvider.RegisterSingleton(serviceDescriptor.ServiceType, instance);
}
else if (serviceDescriptor.ImplementationInstance != null)
{
Mvx.IoCProvider.RegisterSingleton(serviceDescriptor.ServiceType, serviceDescriptor.ImplementationInstance);
}
else
{
throw new InvalidOperationException("Unsupported registration type");
}
}
}
}

How to get .NET Core 3.1 login to persist across multiple EC2 instances in Elastic Beanstalk

I have a small .NET Core 3.1 app that needs to scale occasionally. I need the logged in users information to persist across any instance that EB spins up. I'd like to do this using the SQL Server RDS we have set up. I've attempted to add the Distributed Sql Server Cache and have set up the table for storing the sessionstate, as described in the documentation... but the login info is not being persisted to this table.
In my Startup.cs ConfigureServices I have:
var sqlSessionConnString = new SqlConnectionStringBuilder(Configuration.GetConnectionString("SqlSession"));
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString = sqlSessionConnString.ConnectionString;
options.SchemaName = "dbo";
options.TableName = "TableName";
});
services.AddSession();
When I then log in and check the table there is no data in the table, but my login still works.
What do I have to do to tell Identity to persist the login info in the database instead of in server memory so that my users' login is persisted no matter which instance they are being routed to?
Answering your question: to configure session you also need to add middleware app.UseSession() usage inside your Configure method so
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedSqlServerCache(options =>
{
options.ConnectionString =
#"Server=localhost\SQLEXPRESS;Database=master;Trusted_Connection=True;";
options.SchemaName = "dbo";
options.TableName = "TestCache";
});
services.AddSession();
//to inject httpcontet into controller
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSession();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
//your auth settings
//...
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
And now inside your Controller you can save additional values into cache by calling
_distributedCache.SetString("TestString", "TestValue");
or store data to only specific use session by
_context.Session.SetString("name", "John");
And here is a concrete example
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private readonly IDistributedCache _distributedCache;
private HttpContext _context;
public WeatherForecastController(ILogger<WeatherForecastController> logger,
IDistributedCache distributedCache,
IHttpContextAccessor httpContextAccessor)
{
_distributedCache = distributedCache;
_context = httpContextAccessor.HttpContext;
}
public string Get()
{
_distributedCache.SetString("TestString", "TestValue");
if (_context.Session.Keys.Contains("name"))
return $"Hello {_context.Session.GetString("name")}";
_context.Session.SetString("name", "John");
return "Session was created";
}
}
You will see that inside SQL table data will be inserted
and session cookies were created (.AspNetCore.Session)

.NET Core 1.1 web api in memory caching

I need to cache some information using in memory caching option in .net core web api. Need to get some information from the database in the startup and cache it for 24 hrs. All the controllers in the API should read the data from this cache.
How can I achieve this?
First add MemoryCache in configuration:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddMemoryCache();
}
Then use the IMemoryCache provided byt the assembly Microsoft.Extensions.Caching.Memory
public interface IMemoryCache : IDisposable
{
bool TryGetValue(object key, out object value);
ICacheEntry CreateEntry(object key);
void Remove(object key);
}
Then Inject IMemoryCache anywhere in your classes
public YourClassConstructor(IMemoryCache cache)
{
this.cache = cache;
}
You can set your cache like this (for example in your BLL) :
cache.Set(“Key”, DataToCache);
And in your controllers you can read your cache like this:
[HttpGet()]
public string Get()
{
return cache.Get<TypeOfYourCachedData>(CacheKey);
}

How to unit test an action filter attribute for web api in asp.net core?

I have written an action filter for a web api. If a method in the api controller throws an unhandled exception, then the filter creates an internal error 500 response.
I need to know how to test the filter?
I have researched extensively but could not create a suitable test. I tried context mocking, a service locator implementation and even an integration test using a test server.
The web api controller looks like this:
namespace Plod.Api.ApiControllers
{
[TypeFilter(typeof(UnhandledErrorFilterAttribute))]
[Route("api/[controller]")]
public class GamesController : BaseApiController
{
public GamesController(IGameService repository,
ILogger<GamesController> logger,
IGameFactory gameFactory
) : base(
repository,
logger,
gameFactory
)
{ }
// ..... controller methods are here
}
}
The complete controller is found here.
The filter is this:
namespace Plod.Api.Filters
{
public class UnhandledErrorFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Exception != null)
{
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.ExceptionHandled = true;
}
}
}
}
I even welcome changes to the filter implementation as a possible work around. Any help or ideas would be much appreciated. Thanks.
You probably can't. However, what you can do is spin up a TestServer and then hit it with a HttpClient. This really is an integration test and not a unit test. However, it's the good kind of integration test because it can be run safely in pipelines.
This document explains how to do this:
https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1
The issue you are going to face is that you will need to mock the underlying services inside your app. If you don't do that, your whole server will spin up and attempt to hit the database etc. Here is an example. This is using Moq. Incidentally I am sharing the ConfigureServices method with unit tests so they use the same object mesh of mocked services. You can still use the full functionality of Moq or NSubstitute to test the back-end (or even front -end).
I can hit my attributes in the test with breakpoint.
private void ConfigureServices(IServiceCollection services)
{
var hostBuilder = new WebHostBuilder();
hostBuilder.UseStartup<TestStartup>();
hostBuilder.ConfigureServices(services =>
{
ConfigureServices(services);
});
_testServer = new TestServer(hostBuilder);
_httpClient = _testServer.CreateClient();
}
private void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_storageManagerFactory.Object);
services.AddSingleton(_blobReferenceManagerMock.Object);
services.AddSingleton(_ipActivitiesLoggerMocker.Object);
services.AddSingleton(_loggerFactoryMock.Object);
services.AddSingleton(_hashingService);
services.AddSingleton(_settingsServiceMock.Object);
services.AddSingleton(_ipActivitiesManager.Object);
services.AddSingleton(_restClientMock.Object);
_serviceProvider = services.BuildServiceProvider();
}
public class TestStartup
{
public void Configure(
IApplicationBuilder app,
ISettingsService settingsService)
{
app.Configure(settingsService.GetSettings());
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var mvc = services.AddMvc(option => option.EnableEndpointRouting = false);
mvc.AddApplicationPart(typeof(BlobController).Assembly);
services.AddSingleton(new Mock<IHttpContextAccessor>().Object);
return services.BuildServiceProvider();
}
}

Using FluentScheduler - ASP.NET Core MVC

I currently have a simple website setup with ASP.NET Core MVC (.NET 4.6.1), and I would like to periodically do some processes like automatically send emails at the end of every day to the registered members.
After doing some searching, I came across two common solutions - Quartz.NET and FluentScheduler.
Based on this SO thread, I found the approach of using FluentScheduler more easier to digest and use for my simple task. After quickly implementing the following lines of code into my Program.cs class, I had the emails going out successfully every minute (for testing purposes).
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
var registry = new Registry();
JobManager.Initialize(registry);
JobManager.AddJob(() => MyEmailService.SendEmail(), s => s
.ToRunEvery(1)
.Minutes());
host.Run();
}
}
However, now apart from sending emails I also need to do some back-end processing for e.g. updating the user records in the DB when mails are being sent out. For this, I normally inject my Entity Framework Context into the constructor of my controllers and use it to get/update SQL records.
My question is, since I cannot really inject these services into the main method, where would be the appropriate place to initialize the registry and add jobs for scheduling?
Thanks for the help, I am a little new to this so a little guidance would be much appreciated!
Instead of Program's Main function, I initialized the same in Startup.cs before app.UseMvc..
public void Configure(...., IDependencyObject dependencyObject)
{
....
JobManager.Initialize(new MyRegistry(dependencyObject));
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "api/{controller}/{action}/{id?}");
});
}
My registry class looks like this:
public class MyRegistry: Registry
{
public MyRegistry(IDependencyObject dependencyObject)
{
Schedule(() => new SyncUpJob(dependencyObject)).ToRunNow().AndEvery(10).Seconds();
}
}
My Job class looks like this:
public class SyncUpJob: IJob
{
public SyncUpJob(IDependencyObject dependencyObject)
{
DependencyObject= dependencyObject;
}
public IDependencyObject DependencyObject{ get; set; }
public void Execute()
{
// call the method to run weekly here
}
}
You can define all your jobs and their schedules, by subclassing from FluentScheduler Registry class. something like:
public class JobRegistry : Registry {
public JobRegistry() {
Schedule<EmailJob>().ToRunEvery(1).Days();
Schedule<SomeOtherJob>().ToRunEvery(1).Seconds();
}
}
public class EmailJob : IJob {
public DbContext Context { get; } // we need this dependency, right?!
public EmailJob(DbContext context) //constructor injection
{
Context = context;
}
public void Execute()
{
//Job implementation code: send emails to users and update database
}
}
For injecting dependencies into jobs, you need to implement FluentScheduler IJobFactory interface. GetJobIntance method is called by FluentScheduler for creating job instances. Here you can use any DI library you want; In this sample implementation, I'm going to assume that you use Ninject:
public class MyNinjectModule : NinjectModule {
public override void Load()
{
Bind<DbContext>().To<MyDbContextImplemenation>();
}
}
public class JobFactory : IJobFactory {
private IKernel Kernel { get; }
public JobFactory(IKernel kernel)
{
Kernel = kernel;
}
public IJob GetJobInstance<T>() where T : IJob
{
return Kernel.Get<T>();
}
}
Now you can start your jobs in main method by calling:
JobManager.JobFactory = new JobFactory(new StandardKernel(new MyNinjectModule()));
JobManager.Initialize(new JobRegistry());

Resources