skip to Main Content

The EmployeeLinks class in the Repository layer.

using Contracts;
using Microsoft.AspNetCore.Routing;
using Entities.LinkModels;
using Entities.Models;
using Shared.DataTransferObjects;
using Microsoft.AspNetCore.Mvc.Formatters;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

namespace Repository.Extensions.Utility
{
    public class EmployeeLinks : IEmployeeLinks
    {
        private readonly LinkGenerator _linkGenerator;
        private readonly IDataShaper<EmployeeDto> _dataShaper;

        public EmployeeLinks(LinkGenerator linkGenerator, IDataShaper<EmployeeDto> dataShaper)
        {
            _linkGenerator = linkGenerator;
            _dataShaper = dataShaper;
        }

        public LinkResponse TryGenerateLinks(IEnumerable<EmployeeDto> employeesDto, string fields, Guid companyId, HttpContext httpContext)
        {
            var shapedEmployees = ShapeData(employeesDto, fields);
            if (ShouldGenerateLinks(httpContext))
                return ReturnLinkdedEmployees(employeesDto, fields, companyId, httpContext, shapedEmployees);

            return ReturnShapedEmployees(shapedEmployees);
        }
        private List<Entity> ShapeData(IEnumerable<EmployeeDto> employeesDto, string fields) =>
             _dataShaper.ShapeData(employeesDto, fields)
             .Select(e => e.Entity)
             .ToList();

        private bool ShouldGenerateLinks(HttpContext httpContext)
        {
            var mediaType = (MediaTypeHeaderValue)httpContext.Items["AcceptHeaderMediaType"];
            return mediaType.SubTypeWithoutSuffix.EndsWith("hateoas", StringComparison.InvariantCultureIgnoreCase);
        }

        private LinkResponse ReturnShapedEmployees(List<Entity> shapedEmployees) =>
         new LinkResponse { ShapedEntities = shapedEmployees };

        private LinkResponse ReturnLinkdedEmployees(IEnumerable<EmployeeDto> employeeDto, string fields, Guid companyId,
            HttpContext httpContext, List<Entity> shapedEmployees)
        {
            var employeeDtoList = employeeDto.ToList();

            for(var index = 0; index < shapedEmployees.Count; index++)
            {
                var employeeLinks = CreateLinksForEmployee(httpContext, companyId, employeeDtoList[index].Id, fields);
                shapedEmployees[index].Add("Links", employeeLinks);
            }

            var employeeCollection = new LinkCollectionWrapper<Entity>(shapedEmployees);
            var linkedEmployees = CreateLinksForEmployees(httpContext, employeeCollection);

            return new LinkResponse { HasLinks = true, LinkedEntities = linkedEmployees };
        }

        private List<Link> CreateLinksForEmployee(HttpContext httpContext, Guid companyId, Guid id, string fields = "")
        {
            var links = new List<Link>
            {
                new Link(_linkGenerator.GetUriByAction(httpContext, "GetEmployeeForCompany", "Employees", values: new {companyId, id, fields}),
                    "self",
                    "GET"),
                new Link(_linkGenerator.GetUriByAction(httpContext,"DeleteEmployeeForCompany", "Employees", values: new { companyId, id }),
                    "delete_employee",
                    "DELETE"),
                new Link(_linkGenerator.GetUriByAction(httpContext, "UpdateEmployeeForCompany", "Employees", values: new { companyId, id }),
                    "update_employee",
                    "PUT"),
                new Link(_linkGenerator.GetUriByAction(httpContext, "PartiallyUpdateEmployeeForCompany", "Employees", values: new { companyId, id }),
                    "partially_update_employee",
                    "PATCH")
            };
            return links;
        }

        private LinkCollectionWrapper<Entity> CreateLinksForEmployees(HttpContext httpContext, LinkCollectionWrapper<Entity> employeesWrapper)
        {
            employeesWrapper.Links.Add(new Link(_linkGenerator.GetUriByAction(httpContext, "GetEmployeesForCompany", values: new { }),
            "self",
            "GET"));
            return employeesWrapper;
        }
    }
}

When I try LinkGenerator class in Presentation layer i.e. Controller, ‘GetUriByAction’ works without any exception. But it doesn’t work in EmployeeLinks class in Repository layer. It shows the following error:
‘LinkGenerator’ does not contain a definition for ‘GetUriByAction’ and no accessible extension method ‘GetUriByAction’ accepting a first argument of type ‘LinkGenerator’ could be found (are you missing a using directive or an assembly reference?).

How can I fix it?

2

Answers


  1. Link generator is part of the Microsoft.AspNetCore.App.Ref , but you won’t be able to install it in your Repository project.

    Instead modify your Repository .csproj file and add:

    <ItemGroup>
        <FrameworkReference Include="Microsoft.AspNetCore.App" />
    </ItemGroup>
    

    This should resolve your issue.

    Login or Signup to reply.
  2. According to the source of GetUriByAction, we can see that HttpContext is necessary for this method, while HttpContext is not able to be accessed directly in Repository layer. That’s the reason you got this error. In the meanwhile, this looks not to be a good idea to access Httpcontext in the Repository layer based on this design. I also found this question which had the same idea.

    public static string? GetUriByAction(
        this LinkGenerator generator,
        HttpContext httpContext,
        string? action = default,
        string? controller = default,
        object? values = default,
        string? scheme = default,
        HostString? host = default,
        PathString? pathBase = default,
        FragmentString fragment = default,
        LinkOptions? options = default)
    

    If we want to do what you want to achieve, then we need to get HttpContext there, IHttpContextAccessor should be the answer. Related document. Let’s add the service in Program.cs builder.Services.AddHttpContextAccessor();, and then inject it into the Repository layer, then use _httpContextAccessor.HttpContext as the method parameter.

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search