Create a Helper class from a Controller's method thats getting fat - asp.net-core

This method receive a CSV file (Sale, Date) POSTed from front-end, cleans a table and inserts csv file record into the table, then gets latest Date and returns it to the front-end. I know the code looks a bit ugly and I have a lot to learn but what I am trying to figure out here is how to create a Helper class from this code since the method is getting too fat I gess?
So I tried to migrate some of the code to a Helper class then I created a static Class but the problem is that I couldn't inject dependencies into its constructor since it was an static Class... then no database service from the Helper class and this is not too "helper".
So in your opinion, is this method too long/ fat?
Is there a need for a Helper Class?
How can I build it?
Cheers
Here my Controller
private IDataService<Sale> _SaleDataService;
private readonly MyOptions _myOptions;
public DateTime LastWindowDay;
public ForecastApiController(IDataService<Sale> service, IOptions<MyOptions> optionsAccessor)
{
_SaleDataService = service;
_myOptions = optionsAccessor.Value;
}
[HttpPost("api/Sales/uploadFile")]
public async Task<IActionResult> UploadFiles()
{
try
{
//clean table
var all = _SaleDataService.GetAll();
if (all.Count() > 0)
{
foreach (var item in all)
{
_SaleDataService.Delete(item);
}
}
var files = Request.Form.Files;
//Read Request for the unique uploaded file (method can process multiple but frontend will post just one)
foreach (var file in files)
{
using (System.IO.StreamReader sr = new StreamReader(file.OpenReadStream()))
{
//Reads the file one line at a time until its end
String line = await sr.ReadLineAsync();
while (sr.Peek() >= 0)
{
//Split the values on the line and convert them into a propper format
var fileLine = sr.ReadLine();
var lineToArray = fileLine.Split(',').ToArray();
var timeElement = DateTime.Parse(lineToArray[0]);
var saleAmauntElement = float.Parse(lineToArray[1], System.Globalization.CultureInfo.InvariantCulture);
//Discard negative values and store data into database
if (saleAmauntElement >= 0)
{
//Store line into database
Sale sl = new Sale
{
SaleDate = timeElement,
SaleAmount = saleAmauntElement
};
_SaleDataService.Create(sl);
LastWindowDay = sl.SaleDate;
}
}
// Simple Moving Average method will be used and the restriction is that it will calculate only within a week after -
// - last day of historical data.
// Create an array and stores the next 7 days from the last day that there is data
string[] predictionDays = new string[7];
for (int i = 0; i < 7; i++)
{
predictionDays[i] = LastWindowDay.AddDays(i + 1).ToString("dd/MM/yyyy");
}
//returns the array to frontend to let the user select a prediction day
return Json(predictionDays);
}
}
return Json(new { message = "Error trying to process information" });
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { message = ex.Message });
}
}

Following the Repository pattern helps to maintain the controllers and the project in general, it is like having a helper class with all your operations (business logic and DB).
In your Startup.cs
services.AddScoped<IRepository, Repository>();
Create an Interface
public interface IRepository
{
IEnumerable<MyData> GetData();
}
Create your helper class
public partial class Repository : IRepository
{
private DBContext _context;
private ILogger<Repository> _logger;
private IHttpContextAccessor _httpContextAccessor;
public Repository(DBContext context, ILogger<Repository> logger, IHttpContextAccessor httpContextAccessor)
{
_context = context;
_logger = logger;
_httpContextAccessor = httpContextAccessor;
}
public async Task<bool> SaveChangesAsync()
{
return (await _context.SaveChangesAsync()) > 0;
}
public IEnumerable<MyData> GetData()
{
_logger.LogInformation("Getting All Data from the Database");
return _context.Data.ToList();
}
}
Finally inject it in your Controller
public class RequestsController : Controller
{
private IRepository _repository;
private ILogger<RequestsController> _logger;
private IConfiguration _config;
public RequestsController(IRepository repository,ILogger<RequestsController> logger,IConfiguration config)
{
_repository = repository;
_logger = logger;
_config = config;
}
// GET: Requests
public IActionResult Index()
{
var data = _repository.GetData()
return View(data);
}
}

The best practice approach is to follow the repository pattern as advised by Steve Tolba's answer. The repository pattern alleviates the bulk of query logic, transformation of view Models and the calling of business models from the controller.
However, if for whatever reason you do not want to follow the repository pattern and just want to split up your controller, you may pass the reference to the controller as a parameter to another action method:
[HttpGet("myActionMethodThatUsedToBeFat")]
public int MyActionMethodThatUsedToBeFat()
{
await HelperAction(this);
//Do stuff...
}
private async Task<byte[]> HelperAction(Controller controller)
{
//Do stuff in here that would have made the calling action method too bulky
}

Related

How to write Xunit test case of factory design pattern code block which is tightly coupled?

I would like to write xunit test case of below method. Could you please suggest alternate design so i can write xunit test case with minimum change in my current project.
public ActionResult Index(int id = 0, AssetFilterType filter = AssetFilterType.All)
{
using (var tracer = new Tracer("AssetController", "Index"))
{
RemoveReturnUrl();
ViewBag.JobId = id;
var response = ContextFactory.Current.GetDomain<EmployeeDomain>().GetEmployeeFilterAsync(id,
CurrentUser.CompanyId, filter); // Not able write unit test case , please suggest alternate design.
return View("View", response);
}
}
current design is as follow
public interface IDomain
{
}
public interface IContext
{
D GetDomain<D>() where D : IDomain;
string ConnectionString { get; }
}
public class ApplicationContext : IContext
{
public D GetDomain<D>() where D : IDomain
{
return (D)Activator.CreateInstance(typeof(D));
}
public string ConnectionString
{
get
{
return "DatabaseConnection";
}
}
}
public class ContextFactory
{
private static IContext _context;
public static IContext Current
{
get
{
return _context;
}
}
public static void Register(IContext context)
{
_context = context;
}
}
//var response = ContextFactory.Current.GetDomain**< EmployeeDomain>**().GetEmployeeFilterAsync(id,
CompanyId, filter);
This line serve purpose to call specific class method i.e GetEmployeeFilterAsync from EmployeeDomain. Although it is very handy and widely used in our application but due to design issue i am not able to write unit
test case.
Could you please suggest design so with the minimum change we can write unit test case.
Don't use the Service Locator anti-pattern, use Constructor Injection instead. I can't tell what AssetDomain is from the OP, but it seems as though it's the dependency that matters. Inject it into the class:
public class ProbablySomeController
{
public ProbablySomeController(AssetDomain assetDomain)
{
AssetDomain = assetDomain;
}
public AssetDomain AssetDomain { get; }
public ActionResult Index(int id = 0, AssetFilterType filter = AssetFilterType.All)
{
using (var tracer = new Tracer("AssetController", "Index"))
{
RemoveReturnUrl();
ViewBag.JobId = id;
var response = AssetDomain.GetAssetFilterAsync(id, CurrentUser.CompanyId, filter);
return View("View", response);
}
}
}
Assuming that AssetDomain is a polymorphic type, you can now write a test and inject a Test Double:
[Fact]
public void MyTest()
{
var testDouble = new AssetDomainTestDouble();
var sut = new ProbablySomeController(testDouble);
var actual = sut.Index(42, AssetFilterType.All);
// Put assertions here
}
step1 : Required library
step 2 : When the application starts , register required domain like
protected void Application_Start()
UnityConfig.RegisterComponents();
Step 3: create one static class and register all your domain
example
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
Initialize domain which will injected in controller
container.RegisterType<IPricingDomain, PricingDomain>();
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
step 4 :
so you can inject respective interface in constructor
in controller file.
goal : get rid of below any pattern in your project.
and start writing unit test cases.

.Net Core integration tests with mock keeps returning null

I am trying to be a good boy and write some unit and integration tests but I am slowly giving up. On the controller side, I have:
public class PointController : BaseApiController
{
private readonly IPointL _pointL;
public PointController(IMapper mapper, IPrincipal principal, IPointL pointL) : base(mapper, principal)
{
_pointL = pointL;
}
// GET: api/<PointController>
/// <summary>
/// Get point by id.
/// </summary>
/// <param name="id">Point id.</param>
/// <returns></returns>
[HttpGet("{id}")]
public async Task<PointDTO> GetPointAsync(int id)
{
// Get point.
var pointModel = await _pointL.GetPointAsync(id);
var pointDTO = _mapper.Map<PointDTO>(pointModel);
return pointDTO;
}
}
So the line var pointModel = await _pointL.GetPointAsync(id); in fact calls my logic layer:
public class PointL : BaseL, IPointL
{
private readonly IBaseDAL _baseDAL;
public PointL(IMapper mapper, IPrincipal principal, IBaseDAL baseDAL) : base(mapper, principal)
{
_baseDAL = baseDAL;
}
///<inheritdoc/>
public async Task<PointModel> GetPointAsync(int id)
{
_logger.Info($"Get point with id {id}.");
var pointDBModel = await _baseDAL.GetPointAsync(id);
if (pointDBModel is null)
{
throw new Exception($"Point with id {id} not found.");
}
var pointModel = _mapper.Map<PointModel>(pointDBModel);
return pointModel;
}
}
which drops to the final, DAL layer with the await _baseDAL.GetPointAsync(id);
public partial class BaseDAL
{
///<inheritdoc/>
public async Task<Point> GetPointAsync(int id)
{
_logger.Info($"Get point from database with id {id}.");
using var dbGlistaContext = new GlistaContext(_options);
var point = await dbGlistaContext.Point.FindAsync(id);
return point;
}
}
Now my plan was to write an integration test for the GetPointAsync(int id) method on the controller. I guess, this is the usual way or am I mistaken?
Either way, this is my not working attempt:
private readonly PointController _sut;
private readonly Mock<IPointL> _pointLMock = new Mock<IPointL>();
protected readonly Mock<IMapper> _mapperMock = new Mock<IMapper>();
protected readonly Mock<IPrincipal> _principalMock = new Mock<IPrincipal>();
public PointControllerTests()
{
_sut = new PointController(_mapperMock.Object, _principalMock.Object, _pointLMock.Object);
}
[Fact]
public async System.Threading.Tasks.Task GetPointAsync_ShouldReturnPoint_WhenPointExists()
{
// Arrange.
var pointId = 1;
var pointModel = new PointModel
{
Id = pointId,
Name = "T01",
};
_pointLMock.Setup(x => x.GetPointAsync(pointId))
.ReturnsAsync(pointModel);
// Act.
var point = await _sut.GetPointAsync(pointId); // Keeps returning null no matter what! :(
// Assert.
Assert.Equal(pointId, point.Id);
}
This is for some reason NOT working as mock keeps returning null values! Does anybody have an idea why? I have watched some tutorials but I can't figure out the solution.
EDIT:
I was able to find out that the controller
var pointModel = await _pointL.GetPointAsync(id);
var pointDTO = _mapper.Map<PointDTO>(pointModel);
gets the pointModel, however, the mapping to DTO returns null. Thus I tried setting the mapper mock
var pointDTO = new PointDTO();
mapperMock.Setup(m => m.Map<PointModel, PointDTO>(It.IsAny<PointModel>())).Returns(pointDTO);
However, without any success.
You need to setup the Map method of the mapper mock object.
_mapperMock.Setup(x => x.Map<PointDTO>(It.IsAny<PointDTO>()))
.Returns(...);

Is there a way to use any IDistributedCache as a ResponseCache in .net core?

I want to cache responses from APIs to DistributedSqlServerCache.
The default ResponseCaching only uses a memory cache. There is a constructor which allows to configure what cache to use, but it's internal.
I wrote a filter. If the response is not cached and the http response is OK and the ActionResult is an ObjectActionResult, it serializes the value as JSON and saves it to SQL cache.
If the response is cached, it deserializes it and sets the result as an OkObject result with the deserielized object.
It works ok, but it has some clumsy things (like, to use the attribute, you have to specify the type which will be de/serialized, with typeof()).
Is there a way to cache responses to a distributed sql cache, which doesn't involve me hacking together my own mostly working solution?
Another option would be to copy-pasta the netcore ResponseCacheMiddleWare, and modify it to use a diffirent cache. I could even make it a nuget package maybe.
Are there any other solutions out there?
Here's the filter I put together (simplified for display purposes)
namespace Api.Filters
{
/// <summary>
/// Caches the result of the action as data.
/// The action result must implement <see cref="ObjectResult"/>, and is only cached if the HTTP status code is OK.
/// </summary>
public class ResponseCache : IAsyncResourceFilter
{
public Type ActionType { get; set; }
public ExpirationType ExpirationType;
private readonly IDistributedCache cache;
public ResponseCache(IDistributedCache cache)
{
this.cache = cache;
}
public async Task OnResourceExecutionAsync(ResourceExecutingContext executingContext, ResourceExecutionDelegate next)
{
var key = getKey(executingContext);
var cachedValue = await cache.GetAsync(key);
if (cachedValue != null && executingContext.HttpContext.Request.Query["r"] == "cache")
{
await cache.RemoveAsync(key);
cachedValue = null;
}
if (cachedValue != null)
{
executingContext.Result = new OkObjectResult(await fromBytes(cachedValue));
return;
}
var executedContext = await next();
// Only cache a successful response.
if (executedContext.HttpContext.Response.StatusCode == StatusCodes.Status200OK && executedContext.Result is ObjectResult result)
{
await cache.SetAsync(key, await toBytes(result.Value), getExpiration());
}
}
private async Task<byte[]> toBytes(object value)
{
using var stream = new MemoryStream();
await JsonSerializer.SerializeAsync(stream, value, ActionType);
return stream.ToArray();
}
private async Task<object> fromBytes(byte[] bytes)
{
using var stream = new MemoryStream(bytes);
using var reader = new BinaryReader(stream, Encoding.Default, true);
return await JsonSerializer.DeserializeAsync(stream, ActionType);
}
}
public class ResponseCacheAttribute : Attribute, IFilterFactory
{
public bool IsReusable => true;
public ExpirationType ExpirationType;
public Type ActionType { get; set; }
public ResponseCacheAttribute(params string[] queryParameters)
{
this.queryParameters = queryParameters;
}
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
var cache = serviceProvider.GetService(typeof(IDistributedCache)) as IDistributedCache;
return new ResponseCache(cache)
{
ExpirationType = ExpirationType,
ActionType = ActionType
};
}
}
}
In the end I made a nuget package, sourced on github. See this issue for some more context as to why a new package was made.

A way to always filter query results with Entity Framework Core

I am using an OrgID on all my models in an ASP.NET application using EF as a way to partition the database. I do this so that the database can be shared among multiple users while ensuring that only the data of their organization is available to them.
This forces me to write this OrgID with every insert and to filter every call I make to the database.
So for instance I query the current user's OrgID and insert this in my controllers' Update methods like this:
store.OrgID = await _userManager.GetUserAsync(User).OrgID;
_context.Update(store);
await _context.SaveChangesAsync();
Then when I want to list out objects I again need to filter by OrgID:
var orgID = await _userManager.GetUserAsync(User).OrgID;
var stores = await _context.Stores.Where(s => s.OrgID == orgID).ToListAsync();
I'd love to find a way to override the ApplicationDBContext so that these are handled automatically otherwise it's quite a tedious and error prone task to always handle this in every call to the database.
Any suggestions would be greatly appreciated.
Check Global Query Filters.
Global query filters are LINQ query predicates (a boolean expression
typically passed to the LINQ Where query operator) applied to Entity
Types in the metadata model (usually in OnModelCreating). Such filters
are automatically applied to any LINQ queries involving those Entity
Types, including Entity Types referenced indirectly, such as through
the use of Include or direct navigation property references.
Create an interface that has the OrgID :
public interface IOrgID
{
public int OrgID { get; set; }
}
All your models must implement this interface e.g.:
public class ApplicationUser : IdentityUser, IOrgID
{
public int OrgID { get; set; }
//...
}
public class Stores : IOrgID
{
public int OrgID { get; set; }
//...
}
Use generic repository and create CRUD methods considering the OrgID from the currently logged in user:
public class MyRepo
{
private readonly ApplicationDbContext _context;
private readonly IHttpContextAccessor _accessor;
private readonly int _orgID;
public MyRepo(ApplicationDbContext context, IHttpContextAccessor accessor)
{
_context = context;
_accessor = accessor;
var userId = _accessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
_orgID = _context.Users.Find(userId).OrgID;
}
public async Task<T> GetAsync<T>(Expression<Func<T, bool>> whereExp)
where T : class, IOrgId
{
return await _context.Set<T>().Where(x => x.OrgId == _orgID).FirstOrDefaultAsync(whereExp);
}
public async Task<bool> Create<T>(T entitiy)
where T : class, IOrgId
{
_context.Set<T>().Add(entitiy);
return await _context.SaveChangesAsync() > 0;
}
public async Task<bool> UpdateAsync<T>(T entity)
where T : class, IOrgId
{
_context.Entry<T>(entity).State = EntityState.Modified;
return await _context.SaveChangesAsync() > 0;
}
public async Task<IEnumerable<T>> ListAsync<T>(Expression<Func<T, bool>> whereExp)
where T : class, IOrgId
{
return await _context.Set<T>().AsNoTracking().Where(x => x.OrgId == _orgID).Where(whereExp).ToListAsync();
}
public async Task<bool> DeleteAync<T>(T entity)
where T : class, IOrgId
{
_context.Entry<T>(entity).State = EntityState.Deleted;
return await _context.SaveChangesAsync() > 0;
}
}
What people do I this case is create a class wrapping the DbContext and exposing methods that make sense for their business logic. In your case you can make a UserRepository/StoreRepository classes where the search methods require a origID paramamter
public class StoreRepository {
private ApplicationDBContext _context
StoreRepository(ApplicationDBContext context){
_context = context
}
public Task<Ilist<Store>> GetStores(int origID){
return _context.Stores.Where(s => s.OrgID == orgID).ToListAsync();
}
}

How does one determine the route of an Web API 2.2 Action implemented in a base class?

Assume for a moment that I have an abstract controller
public abstract class ResourceController<TResource> : ApiController where TResource: Resource,new()
{
[Route("{id}")]
[HttpGet]
public async Task<IHttpActionResult> FindById([FromUri] string id)
{
TResource resource = null;
// go fetch the resource from a database or something
return Ok(resource)
}
[Route("")]
[HttpPost]
public async Task<IHttpActionResult> Create(TResource resource)
{
TResource resource = null;
// go create the resource or something
return CreatedAtRoute("XXXXX", new { id = resource.Id }, resource);
}
// more methods
}
[RoutePrefix("foo")]
public class FooResourceController : ResourceController<Foo>
{
}
[RoutePrefix("baa")]
public class BaaResourceController : ResourceController<Baa>
{
}
public class Resource
{
public string Id { get; set; }
// some other properties all resources shared
}
At this stage all the actions work, except for creating a new resource. Without overriding the Create method in every subclass, how do I find the correct route of the FindById of the respective controllers from the ResourceController Create method?
For example, if I create a foo resource with id 123 then it would return foo/123. If I created a resource baa with id 456, then it woulds return baa/456.
I'm unable to name the route using attributes, since only one can exist for the application.
Had the same problem. I fixed it by using the Created method in combination with the calling url.
This will only work if yout post doesn't have a dedicated template
My get:
[HttpGet("{id:int}")]
public async Task<IActionResult> GetAsync(int id)
{
try
{
var codetabel = await _reader.GetAsync(id);
var model = Mapper.Map<TCodeTabelModel>(codetabel);
return OkResult(model);
}
catch ....
}
And post:
[HttpPost()]
public async Task<IActionResult> InsertAsync(TCodeTabelModel model)
{
if (!ModelState.IsValid)
return BadRequestResult(ModelState);
try
{
var entity = Mapper.Map<TCodeTabelEntity>(model);
var insertedEntity = await _writer.InsertAsync(entity);
return Created($"{Request.Path.Value}/{insertedEntity.Id}" , Mapper.Map<TCodeTabelModel>(insertedEntity));
}
catch ....
}