Generate the code for Blazor services based on controllers? - c#

I'm writing a Blazor webassembly application, and I would like to rationalize the code in the client.
In the server part, there are controllers. In the client part, there are services calling for the controllers. Usually, one service calls only one controller. I'm not comfortable with the code of the client services, as IMO it is redundant with the code of the server controller. See example below.
Server controller sample
[Route("api/[controller]/{Lodge}/[action]")]
[ApiController]
public class CatalogController : ControllerBase
{
private readonly UserManager<ApplicationUser> _userManager;
public CatalogController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
[Authorize]
[HttpGet]
public Item[] GetAllItems(string Lodge)
{
Lodge = HttpUtility.UrlDecode(Lodge);
return new Item[]
{
// Get the data from a database
new Item() { Name = string.Format("{0} 1", Lodge) },
new Item() { Name = string.Format("{0} 2", Lodge) },
new Item() { Name = string.Format("{0} 3", Lodge) }
};
}
}
Client service sample
public interface ICatalogService
{
Task<Item[]> AllItems(string Lodge);
}
public class CatalogService : ICatalogService
{
private readonly HttpClient _httpClient;
public CatalogService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<Item[]> AllItems(string Lodge)
{
return await _httpClient.GetFromJsonAsync<Item[]>(string.Format("api/catalog/{0}/getallitems", HttpUtility.UrlEncode(Lodge)));
}
}
I am not happy with the the code for method AllItems in the service because
Redundant with the definition of the controller. The return type and the parameters could be infered based on controller definition.
Error prone as I have to build the URL for the controller "by-hand". But it is already defined in the controller route.
Error prone again, as I use a generic method GetFromJsonAsync, so I guess there is a deserialize operation somewhere in it. If there is a mismatch between controller return type and service call, I'd prefer a compile error instead of a runtime error.
Actually, I would be very happy NOT writing the code for the service, as it seems all the needed info are already present in the controller definition. Is there any coding scheme, or framework, or code-generator tool to do that for me and solve the all 3 issues above ?

Related

Accessing Claims Principal in the Service layer in the API of a Net core 6 App

I need to access ClaimsPrincipal within the service layer of a Net Core 6 app.
I could always just builder.Services.AddTransient<IHttpContextAccessor, HttpContextAccessor>(); in the Startup.cs & go my merry way but this is a no-no. Makes it difficult to test and more importantly this is a great example of leaky abstraction.
So, now what I have is the following
public class ClaimsProvider : IClaimsProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ClaimsProvider(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public ClaimsPrincipal? GetClaimsPrincipal()
{
return _httpContextAccessor.HttpContext?.User;
}
}
public interface IClaimsProvider
{
ClaimsPrincipal? GetClaimsPrincipal();
}
Within my Startup.cs AddScoped() that takes an IHttpContextAccessor and return an IClaimsProvider. Then I simply build all services against IClaimsProvider
builder.Services.AddScoped<IClaimsProvider>(provider =>
{
var httpContextAccessor = provider.GetRequiredService<IHttpContextAccessor>();
return new ClaimsProvider(httpContextAccessor);
});
And the usual route for my services where I inject it as a dependency
private readonly IClaimsProvider _claimsProvider;
public SomeService(
IWebHostEnvironment hostingEnvironment,
IMapper mapper, IClaimsProvider claimsProvider, ...)
{
_hostingEnvironment = hostingEnvironment ??
throw new ArgumentNullException(nameof(hostingEnvironment));
_mapper = mapper ??
throw new ArgumentNullException(nameof(mapper));
_claimsProvider = claimsProvider;
}
public void SomeMethod()
{
var u = _claimsProvider.GetClaimsPrincipal();
foreach (var claim in u.Claims)
{
Console.WriteLine($"{claim.Type} : {claim.Value}");
}
}
My question is that is the above approach ok? Potentially, is there any other approach that is better than the one shown above?
To prevent a leaky abstract (the need for an IHttpContextAsccessor in your service), I would recommend using the Adapter Pattern.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddHttpContextAccessor();
services.AddScoped<IClaimsProvider, HttpContextClaimsProvider>();
}
public IClaimsProvider
{
public ClaimsPrinciple ClaimsPrinciple { get; }
}
// Adapter
public HttpContextClaimsProvider : IClaimsProvider
{
public HttpContextClaimsProvider(IHttpContextAccessor httpContext)
{
ClaimsProvider = httpContext?.User?.Principle as ClaimsPrinciple;
}
public ClaimsPrinciple ClaimsPrinciple { get; private set; }
}
public class YourService : IYourService
{
private readonly IClaimsProvider _claimsProvider;
public YourService(IClaimsProvider claimsProvider)
{
_claimsProvider= claimsProvider;
}
}
In our design each controller action receives an FooRequest. This is a POCO object where the properties are filled from the model binder by using corresponding attributes:
public class FooRequest : RequestBase
{
[FromRoute]
public int Id { get; set; }
[FromQuery]
public DateTime? Start { get; set; }
[FromBody]
public SomeComplexObject Configuration { get; set; }
}
Additionally we made a derived class using the suffix WithUser that has a ClaimsPrincipal as additional property:
public class FooRequestWithUser : FooRequest, IRequest<FooResponse>
{
public ClaimsPrincipal User { get; set; }
}
In a next step we made a helper class that provides a helper method that can receive the request instance, a claims principal and a type T:
public class RequestBase
{
public T Of<T>(ClaimsPrincipal user) where T: class, new()
{
// Check if T has base of own type
// Create instance and iterate all props to get value
// from this and and set value in instance.
// Additionally use reflection to set user property.
}
}
When our normal request class is derived from this one, we can call it within our controller and create a model containing the user as an additional property and forward it into our services by using MediatR:
public IActionResult DoFoo(FooRequest request)
{
var requestWithUser = request.Of<FooRequestWithUser>(User);
var result = mediator.Send(requestWithUser);
return Ok(result);
}
By this approach the claims principal is bound to the request consumed by the service and not something it has to additionally receive. Also it makes clear, that this request must be somehow authenticated and the service should check for some potential permissions or similar.
The approach you have described is generally considered a valid way to access the ClaimsPrincipal in the service layer of a .NET Core 6 app, as it abstracts the implementation details of the IHttpContextAccessor, making it easier to test and maintain.
An alternative approach could be to use the built-in dependency injection in ASP.NET Core to directly inject the ClaimsPrincipal into the service, without the need for a separate IClaimsProvider interface.
You can do this by registering the ClaimsPrincipal as a service in the ConfigureServices method of the Startup class.

AddSingleton doesn't work in my ASP.NET Core project

I have the problem that I want to add my service CourseService as Singleton and I want to add only once my initial data like this:
context.Courses.Add(new Course { ... });
But it turned out that every time when I upload my page with courses my initial data added again and again to my database. So I see the same courses on my page as much time as I upload this page. Can't understand where is the problem.
My interface:
public interface ICourseService
{
IEnumerable<CourseDto> GetCourses();
Task<IEnumerable<CourseDto>> GetCoursesAsync();
}
My service:
public class CourseService : BaseService<Course, CourseDto>, ICourseService
{
public CourseService(IMapper mapper, DataContext context) : base(mapper,context)
{
context.Courses.Add(new Course { ... });
context.Courses.Add(new Course { ... });
context.SaveChanges();
}
public IEnumerable<CourseDto> GetCourses() { ... }
public async Task<IEnumerable<CourseDto>> GetCoursesAsync() { ... }
}
My controller:
public class CourseController : Controller
{
private readonly CourseService _courseService;
public CourseController(CourseService courseService)
{
_courseService = courseService;
}
[Route("courses")]
public async Task<IActionResult> GetCourses()
{
var courses = await _courseService.GetAllAsync();
return View("CourseList", courses);
}
}
And I added my service as Singleton in the method ConfigureServices:
services.AddSingleton<ICourseService, CourseService>();
services.AddEntityFrameworkSqlite().AddDbContext<DataContext>();
AddDbContext by default adds context with Scoped lifetime, so it can't be resolved in singleton services. Possible workarounds:
register ICourseService as scoped
register context as scoped or transient (would not recommend)
inject IServiceScopeFactory and use it to create scope and resolve context from the scope (on each method call), like in this answer
Personally I would go with the first approach.
Read more:
Service lifetimes
Data seeding in EF Core

automating logging messages for api controllers

I have the following controller method in my Entity Framework Core project.
I just started learning about .NetCore logging and I love it!
However I have a feeling it might become difficult to attach logging to all of my controllers.
Currently my project has quite a few controllers for various endpoints.
I started adding logging code, and I fear that I may be making it more difficult than needs to be.
So I inject the logger, then I create a few strings like Message, Controller, and ErrorMessage.
I then assign values to those inside the controller method as needed.
I was wondering if there was a way to do this in a more automated way instead of manually all that to every controller method.
Unfortunately I can't create a base class because my controllers need to use ControllerBase.
[Route("api/[controller]")]
[ApiController]
public class PetrolStationController : ControllerBase
{
private readonly petrol_ProdContext _context;
private readonly ILogger _logger;
public PetrolStationController(petrol_ProdContext context, ILogger<PetrolStationController> logger)
{
_context = context;
_stationService = stationService;
_logger = logger;
}
public string Message { get; set; }
public string Controller { get; set; }
public string ErrorMessage { get; set; }
[HttpGet("GetStationNodeObject/{stationId}")]
public async Task<ActionResult<StationList>> GetStationLocation(Guid stationID)
{
Controller = this.GetType().Name.ToString();
var stationList = await _context.StationList.FindAsync(stationID);
if (stationList == null)
{
ErrorMessage = "stationID does not exist";
Message = $"Endpoint: {Controller}: {DateTime.UtcNow.ToLongTimeString()} => {ErrorMessage}";
_logger.LogInformation(Message);
return NotFound();
}
var petrolStation = await _stationService.GetStationLocation(stationID);
Message = $"Endpoint: {Controller}: {DateTime.UtcNow.ToLongTimeString()}, Success!";
_logger.LogInformation(Message);
return petrolStation;
}
}
I was hoping someone could provide some insight for a better way of doing this.
thank you all
Check this tutorial: https://nblumhardt.com/2019/10/serilog-in-aspnetcore-3/
I am using this approach quit often. This will log all exceptions automatically and if you want more you can easily add it inside your controller. Biggest PRO is that you set up everything at one place and you get uniform log format. You can use log server or file to save logs.

How do I generate a url inside a c# service?

Really simple I hope. I just want to do the equivalent of
Url.Action("SomeAction", "SomeController", new { id = 3 });
But inside a service class. Not inside a controller class or IActionResult method
In a plain old service class. Because of the service call having all the data I don't want to pass in other information so my service call is nice and clean.
I've come close but nothing seems to work, either that or it cant be done.
I tried to dependency inject this
services.AddScoped<IUrlHelper>(x => x
.GetRequiredService<IUrlHelperFactory>()
.GetUrlHelper(x.GetRequiredService<IActionContextAccessor>().ActionContext));
In my service call I used (DI) this
public AdminService(..., IUrlHelper urlHelper)
so in my service method I could to this
string editUrl = _urlHelper.Action("EditRole", "Admin", new { id = 0 });
which got rid of all the red squiglies but at run time this bit caused me a problem
.GetUrlHelper(x.GetRequiredService<IActionContextAccessor>().ActionContext));
You can inject IUrlHelper interface inside a service class.
public class ServiceClass
{
private readonly IActionContextAccessor _actionContextAccessor;
private readonly IUrlHelperFactory _urlHelperFactory;
public ServiceClass(IActionContextAccessor actionContextAccessor,
IUrlHelperFactory urlHelperFactory,)
{
_actionContextAccessor = actionContextAccessor;
_urlHelperFactory = urlHelperFactory;
}
public string CreateUrl()
{
var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);
string url = urlHelper.Action("SomeAction", "SomeController");
return url;
}
}
#SMM I had to add this to my startup but otherwise, works, so thank you
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddSingleton<IUrlHelper, UrlHelper>();
Url.Action generates only an url.
#Url.Action("actionName", "controllerName", new { id = id })
Html.ActionLink generates an tag automatically.

Castle Windsor - Pass resolve arguments to the inferior layers

I have a web api project where controllers depend on some storage layer. E.g.
each controller has similar code:
public class ResourceController : ApiController
{
private readonly IResourceStorage _resourceStorage;
private readonly IOtherStorage _otherStorage;
public ResourceController(IResourceStorage resourceStorage, IOtherStorage otherStorage)
{
_resourceStorage = resourceStorage;
_otherStorage = otherStorage;
}
// Web API Actions
}
A common code for storage looks like this:
public class ResourceStorage : DBProvider, IResourceStorage
{
public ResourceStorage(string connectionString) : base(connectionString)
{
}
// storage methods
}
Based on some specific condition of the Web Api request, I need to be able to inject different connectionStrings to Storages of controller. Pseudo-code could look like that:
public class WindsorControllerActivator : IHttpControllerActivator
{
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
string connectionString = ChooseDbConnectionStringBasedOnRequestContext(request);
var controller =
(IHttpController)_container.Resolve(connectionString, controllerType, new {databaseType = connectionString});
request.RegisterForDispose(
new Release(
() => _container.Release(controller)));
return controller;
}
}
I do not know how to pass the parameter to the storage in the less destructive way:)
What have I tried? Alternatives:
Pass parameterers as Castle Windsor Additional Arguments and treat it in next layer using DynamicParameters. The arguments get the controller layer but I could not find a way to get them to the storage - it has its own CreationContext, and I cannot find a way to pass it on from controller to storage.
Have N(equal to number of connection strings) containers and choose one of those inside ControllerActivator. Seems a huge and ugly solution, totally non-flexible but it works.
Create N sets of Controllers each with their own name and inside of storage DynamicParameters check Handler's component name and choose the connection string. From the ControllerActivator pass in the key to the correct set of Controllers. Also ugly - too many registrations of controllers and a lot of plumbing code.
You could use a factory pattern:
public interface IResourceStorageFactory
{
IResourceStorage Create(int numberOfResources);
}
public class ResourceStorageFactory : IResourceStorageFactory
{
public IResourceStorage Create(HttpRequestMessage request)
{
var connectionString = ChooseDbConnectionStringBasedOnRequestContext(request);
return new ResourceStorage(connectionString);
}
}
and then simply
private readonly IResourceStorage _resourceStorage;
private readonly IOtherStorage _otherStorage;
public ResourceController(IResourceStorageFactory resourceStorageFactory, IOtherStorage otherStorage)
{
_resourceStorage = resourceStorageFactory.Create(Request);
_otherStorage = otherStorage;
}
The solution I found is introducing a connection string provider:
public interface IConnectionStringProvider
{
string ConnectionString { get; }
}
When register this provider as per web request with a factory method:
kernel.Register(
Component.For(typeof (IConnectionStringProvider))
.ImplementedBy(typeof (ConnectionStringProvider))
.UsingFactoryMethod(
(k, context) =>
new ConnectionStringProvider(context.AdditionalArguments["connectionString"].ToString()))
.LifestylePerWebRequest());
And inside the controller activator first resolve the connection string provider with the right parameters and then the controller:
// The lifecycle of connection string provider if per web request.
// We resolve it first time with the correct parameters,
// so it is injected with the correct connection string throught the lifecycle of the request.
_container.Resolve<IConnectionStringProvider>(new {connectionString});
var controller =
(IHttpController) _container.Resolve(controllerType);
It still not perfect and looks kind of hackish but it allows to keep dependencies on the interfaces of the inferior layer and requires less changes of the codebase from the solutions I've found

Categories

Resources