.NET Core 1.1 web api in memory caching - 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);
}

Related

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

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?

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)

botframework v4, accessing appsettings.json

How do I read the appsettings.json file in my botframework (v4) app? I see the configuration is set up in the Startup.cs, but how do I access the settings in other classes?
One of the goals of the v4 ASP.NET core integration was to be idiomatic to existing .NET Core patterns. One of the things this means is that when you implement an IBot and add it with AddBot<TBot>, it becomes a participant in dependency injection just like an ASP.NET MVC controller would. This means that any services you might need to access, including configuration types such as IOptions<T>, will be injected into your bot via the constructor if you ask for them.
In this case, you just want to leverage the "options pattern" from the Configuration APIs and that would look something like this:
Startup.cs
public class Startup
{
private readonly IConfiguration _configuration;
public Startup(IConfiguration configuration)
{
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
}
public void ConfigureServices(IServiceCollection services)
{
// Bind MySettings to a section named "mySettings" from config
services.Configure<MySettings>(_configuration.GetSection("mySettings"));
// Add the bot
services.AddBot<MyBot>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseBotFramework();
}
}
MyBot.cs
public class MyBot : IBot
{
private readonly IOptions<MySettings> _mySettings;
public MyBot(IOptions<MySettings> mySettings)
{
_mySettings = mySettings ?? throw new ArgumentNullException(nameof(mySettings));
}
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
// use _mySettings here however you like here
}
}

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();
}
}

How to persist policy authorization results for users in ASP.NET Core, MVC 6?

Currently I have a simple custom policy handler that looks like so:
protected override void Handle(AuthorizationContext context, UserPolicyRequirement requirement)
{
// authorize user against policy requirements
if (_authorizationTask.AuthorizeUserAgainstPolicy(context.User, requirement))
{
// User passed policy req's
context.Succeed(requirement);
}
}
Problem is, this authorization step takes a long time to execute, but this is required in many different areas of the website. Is there any readily available mechanisms to save/cache the results of this policy authorization so that I only need to do this once per session?
I am currently using Windows Authentication, if that helps.
If per session way does not cause any problem, you can use Session to store user data. Simple implementation is something like below:
First you need a service to get user data from any store
public interface IGetUserDataService
{
<type> GetUserData();
}
I assume that there is Session configuration(see) and IGetUserDataService implementation.
Then you need to create a middleware to handle Session
public class SessionMiddleware
{
private readonly RequestDelegate _next;
private readonly IGetUserDataService _getUserDataService;
public SessionMiddleware(RequestDelegate next, IGetUserDataService getUserDataService)
{
_next = next;
_getUserDataService = getUserDataService;
}
public async Task Invoke(HttpContext context)
{
//user data is obtained only once then is stored in Session
if (context.Session.Get("UserData") == null)
{
context.Session.Set("UserData", getUserDataService.GetData());
}
await _next.Invoke(context);
}
}
//In Startup.cs
app.UseMiddleware<SessionMiddleware>();
Finally get and use session data in handler
public class YourHandler : AuthorizationHandler<YourRequirement>
{
private readonly IHttpContextAccessor _accessor;
public YourHandler(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
protected override void Handle(AuthorizationContext context, PermissionRequirement requirement)
{
var userData =(<type>)_accessor.HttpContext.Session.Get("UserData");
// check
}
}

Resources