Can we write to cache using serilog? - asp.net-core

I'm trying to write logs in cache using serilog. Is it possible?
namespace MemoryCacheApplication.Controllers
{
public class HomeController : Controller
{
private readonly IMemoryCache memoryCache;
public HomeController(IMemoryCache memoryCache)
{
this.memoryCache = memoryCache;
}
}
}

trying to write logs in cache using serilog
If it indeed requires writing logs in cache using Serilog under a specific scenario, to achieve it, you can try to implement a custom LogEventSink, like below.
public class MyMemoryCacheSink : ILogEventSink
{
private IMemoryCache memoryCache;
public MyMemoryCacheSink(IMemoryCache memoryCache)
{
this.memoryCache = memoryCache;
}
public void Emit(LogEvent logEvent)
{
memoryCache.Set("Clog", logEvent.MessageTemplate.Text);
var log = memoryCache.Get<string>("Clog");
}
}
public static class MyMemoryCacheSinkExtensions
{
public static LoggerConfiguration MyMemoryCacheSink(
this LoggerSinkConfiguration loggerConfiguration, IMemoryCache memoryCache)
{
return loggerConfiguration.Sink(new MyMemoryCacheSink(memoryCache));
}
}
Use custom Sink
private readonly IMemoryCache memoryCache;
public HomeController(IMemoryCache memoryCache)
{
this.memoryCache = memoryCache;
}
public IActionResult Index()
{
var logger = new ReloadableLogger(cfg => cfg.WriteTo.MyMemoryCacheSink(memoryCache));
logger.Information("hello world");
Test Result

Related

ASP.NET Core 6 - using memorycache in view component

I am writing an application using ASP.NET Core 6 MVC.
I have a controller that instantiates an IMemoryCache and stores some values into the cache.
public HomeController(ILogger<HomeController> logger, IMemoryCache memoryCache)
{
_logger = logger;
_cache = memoryCache;
}
In some other part of the application I assign a value to the cache.
In the _Layout.cshtml I am using a view component
#await Component.InvokeAsync("Menu")
I need to access the cache from the view component.
I set IMemoryCache in the constructor, and the try to get the data.
public class MenuViewComponent : ViewComponent
{
private const string ClientInfoCacheKey = "ClientInfo";
private IMemoryCache _cache;
public async Task<IViewComponentResult> InvokeAsync(IMemoryCache memoryCache)
{
_cache = memoryCache;
var fromCache = _cache.Get<string>(ClientInfoCacheKey);
// ......
}
}
But the problem I am facing is that _cache is always null...
Is there a way to access cache from a view component?
Thanks
You should inject IMemoryCache to your MenuViewComponent from constructor. Your code should be:
public class MenuViewComponent : ViewComponent
{
private const string ClientInfoCacheKey = "ClientInfo";
private readonly IMemoryCache _cache;
public MenuViewComponent(IMemoryCache memoryCache)
{
_cache = memoryCache;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var fromCache = _cache.Get<string>(ClientInfoCacheKey);
// ......
}
}

How to convert ODataQueryOptions<DtoType> to ODataQueryOptions<EntityType> to query the underlying storage?

I have a webapi controller using ProductDTO type for clients but the repository is using a Product type.
I would like to use odata on my endpoint. I receive the ODataQueryOptions parameter and I want to pass it to repository (implemented using CosmosDB).
I cant seem to figure out how to convert from ODataQueryOptions<ProductDTO> to ODataQueryOptions<Product>.
[Route("api/[controller]")]
public class ProductsController<ProductsDTO, Product> : ControllerBase
{
IRepository<Product> _repository;
IMapper _mapper;
[HttpGet]
public async Task<ActionResult<IList<ProductDTO>>> Get(ODataQueryOptions<ProductDTO> queryOptions)
{
var mappedQueryOptions = ... // convert 'queryOptions' to ODataQueryOptions<Product> ???
var products = await _repository.Get(mappedQueryOptions);
return Ok(_mapper.Map<IEnumerable<Product>, IEnumerable<ProductDTO>>(products));
}
}
In my aspnetcore service composition I create and inject automapper
var configuration = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new ProductProfile());
cfg.AddExpressionMapping();
});
internal class ProductProfile : Profile
{
public ProductProfile()
{
CreateMap<Product, ProductDto>().ReverseMap();
}
}
I managed to extract the queryoptions filter as a lambda expression Expression<Func<Product, bool>> (using automapper MapExpression) and passed it to repository , that works to a certain extent but I want to get the select , top, skip, etc. as well.
Any suggestions on how that could be done?
can you review the example?
[ODataRouteComponent("api")]
public class UsersController : ODataController
{
#region ctor
private readonly ILogger<UsersController> _logger;
private readonly IMapper _mapper;
private readonly UserManager<AspNetUser> _userManager;
public UsersController(IMapper mapper,
UserManager<AspNetUser> userManager,
ILogger<UsersController> logger)
{
_mapper = mapper;
_userManager = userManager;
_logger = logger;
}
#endregion
[HttpGet]
[EnableQuery(PageSize = 10)]
public async Task<IActionResult> Get(ODataQueryOptions<UserDto> options)
{
var user = await _userManager.GetUserAsync(User);
_logger.LogInformation("{UserUserName} is getting all users", user.UserName);
return Ok(await _userManager.Users.GetAsync(_mapper, options));
}
[HttpGet]
[EnableQuery]
public async Task<IActionResult> Get(int key, ODataQueryOptions<UserDto> options)
{
var user = await _userManager.GetUserAsync(User);
_logger.LogInformation("{UserUserName} is getting user with id {Key}", user.UserName, key.ToString());
return Ok(_userManager.Users.Get(_mapper, options).FirstOrDefault(x => x.Id == key));
}
}
services.AddControllers(opt =>{}).AddOData((opt, _) =>
{
opt.EnableQueryFeatures().AddRouteComponents("api", EdmModelBuilder.GetEdmModelv1());
});
public class MappingProfile : Profile
{
public MappingProfile() : base(nameof(WebUI))
{
CreateMap<AspNetUser, UserDto>();
}
}
internal static class EdmModelBuilder
{
internal static IEdmModel GetEdmModelv1()
{
ODataConventionModelBuilder builder = new();
builder.EnableLowerCamelCase();
#region UserDto
builder.EntitySet<UserDto>(nameof(DataListContext.Users))
.EntityType
.Page(25, 15);
var f5 = builder.Function("Get");
f5.Parameter<int>("key").Required();
f5.ReturnsFromEntitySet<UserDto>(nameof(DataListContext.Users));
#endregion
return builder.GetEdmModel();
}
}

Admin lock or unlock account user in .Net Core

I am doing the management of a user's account when necessary I can Lock a user's account in case they violate it. Or can be unlocked if required. I got an error like this. Where am I wrong, I use .Net Core 5 to build my program. Error: "An unhandled exception occurred while processing the request.
NullReferenceException: Object reference not set to an instance of an object."
enter image description here
Interface
public bool LockUser(string email);
public bool UnlockUser(string email);
Repo
public bool LockUser(string email)
{
var userTask = _userManager.FindByEmailAsync(email);
userTask.Wait();
var user = userTask.Result;
var lockUserTask = _userManager.SetLockoutEnabledAsync(user, true);
lockUserTask.Wait();
var lockDateTask = _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.Now);
lockDateTask.Wait();
return lockDateTask.Result.Succeeded && lockUserTask.Result.Succeeded;
}
Controller
public ActionResult LockUser(string email)
{
if (!_userRepository.LockUser(email))
{
throw new ArgumentException("Error");
}
return RedirectToAction("Index");
}
Please refer the following sample code, the UserRepository should like this, add the usermanager via the constructor parameter:
public interface IUserRepository
{
public bool LockUser(string email);
public bool UnlockUser(string email);
}
public class UserRepository : IUserRepository
{
private readonly UserManager<IdentityUser> _userManager;
public UserRepository(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
public bool LockUser(string email)
{
var userTask = _userManager.FindByEmailAsync(email);
userTask.Wait();
var user = userTask.Result;
var lockUserTask = _userManager.SetLockoutEnabledAsync(user, true);
lockUserTask.Wait();
var lockDateTask = _userManager.SetLockoutEndDateAsync(user, DateTimeOffset.Now);
lockDateTask.Wait();
return lockDateTask.Result.Succeeded && lockUserTask.Result.Succeeded;
}
public bool UnlockUser(string email)
{
//...
throw new NotImplementedException();
}
}
Then, add the service to the service container:
public void ConfigureServices(IServiceCollection services)
{
//...
services.AddScoped<IUserRepository, UserRepository>();
services.AddControllersWithViews();
}
Then, in the MVC controller:
public class HomeController : Controller
{
private readonly IUserRepository _userRepository;
public HomeController(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public IActionResult Index(int id)
{
string email = "aa#hotmail.com";
if (!_userRepository.LockUser(email))
{
throw new ArgumentException("Error");
}
return View();
}
The debug screenshot like this:

.Net Core use new ApplicationBuilder instead of IApplicationBuilder from configure

I am attempting to create a "registry" so that just creating a new class implementing IServiceCollectionInitializer or IApplicationBuilderInitializer allows it to be loaded. Instead of having a giant start up class the registry would add those automatically.
My problem is I dont know how to make the app either use a new application builder or retrieve the the one given automatically without getting it from startup.
public class ServiceCollectionInitializerRegistry
{
private readonly IList<IServiceCollectionInitializer> _serviceCollectionInitializers;
private readonly IServiceCollection _serviceCollection;
private readonly IServiceProvider _serviceProvider;
public ServiceCollectionInitializerRegistry(IServiceCollection serviceCollection)
{
_serviceCollectionInitializers = new List<IServiceCollectionInitializer>();
_serviceCollection = serviceCollection;
_serviceProvider = serviceCollection.BuildServiceProvider();
}
public ServiceCollectionInitializerRegistry WithInitializers(
params IServiceCollectionInitializer[] initializers)
{
if (!initializers.Any())
{
return this;
}
foreach (var initializer in initializers)
{
_serviceCollectionInitializers.Add(initializer);
}
return this;
}
public ServiceCollectionInitializerRegistry WithAssemblyInitializers()
{
var assembly = Assembly.GetEntryAssembly();
var initializerTypes =
assembly.GetTypes()
.Where(type => typeof(IServiceCollectionInitializer).IsAssignableFrom(type));
if (!initializerTypes.Any())
{
return this;
}
foreach (var type in initializerTypes)
{
_serviceCollectionInitializers.Add((IServiceCollectionInitializer)Activator.CreateInstance(type));
}
return this;
}
public void Build()
{
var configuration = _serviceProvider.GetRequiredService<IConfiguration>();
foreach (var serviceCollectionInitializer in _serviceCollectionInitializers)
{
var logger = _serviceProvider.GetRequiredService<ILoggerProvider>()
.CreateLogger(serviceCollectionInitializer.GetType().AssemblyQualifiedName);
serviceCollectionInitializer
.Initialize(
configuration,
_serviceCollection, logger);
}
}
}
public class ExceptionHandlingInitializer : IServiceCollectionInitializer, IApplicationBuilderInitializer
{
public void Initialize(IConfiguration configuration, IServiceCollection services, ILogger logger)
{
services.AddSingleton<IExceptionMapper, ExceptionMapper>();
services.AddSingleton<IExceptionHandler, ExceptionHandler>();
}
public void Initialize(IConfiguration configuration, IApplicationBuilder builder, ILoggerFactory loggerFactory)
{
builder.UseExceptionHandlerMiddleware();
}
}

Injecting Dependency into Web API Controller

I want to inject unity container into WebController.
I have UnityDependencyResolver:
public class UnityDependencyResolver : IDependencyResolver
{
readonly IUnityContainer _container;
public UnityDependencyResolver(IUnityContainer container)
{
this._container = container;
}
public object GetService(Type serviceType)
{
try
{
return _container.Resolve(serviceType);
}
catch
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return _container.ResolveAll(serviceType);
}
catch
{
return new List<object>();
}
}
public void Dispose()
{
_container.Dispose();
}
}
Then, in my Global.asax I add the following line:
var container = new UnityContainer();
container.RegisterType<IService, Service>
(new PerThreadLifetimeManager()).RegisterType<IDALContext, DALContext>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
Then, If I use the following in a Web Controller:
private IService _service;
public HomeController(IService srv)
{
_service = srv;
}
It works fine.
But I want to inject it into WebAPI Controller, so if I do it the same way:
private IService _service;
public ValuesController(IService srv)
{
_service = srv;
}
It does not work, it says that constructor is not defined.
Ok, I create one more constructor:
public ValuesController(){}
And in this case it uses only this constructor and never the one where I should inject unity container.
Please advise.
Add this in your WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Routes and other stuff here...
var container = IocContainer.Instance; // Or any other way to fetch your container.
config.DependencyResolver = new UnityDependencyResolver(container);
}
}
And if you want the same container you can keep it in a static variable, like so:
public static class IocContainer
{
private static readonly Lazy<IUnityContainer> Container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
return container;
});
public static IUnityContainer Instance
{
get { return Container.Value; }
}
}
More info can be found here:
http://www.asp.net/web-api/overview/advanced/dependency-injection
On a sidenote, I can also recommend the nuget-package Unity.Mvc. It adds a UnityWebActivator and support for PerRequestLifetimeManager.
https://www.nuget.org/packages/Unity.Mvc/