【问题标题】:How to resolve problem with error mapping types?如何解决错误映射类型的问题?
【发布时间】:2021-01-25 11:30:15
【问题描述】:

我正在尝试在 Blazor wasm 中创建一个表单,它允许我将 Recipes 添加到数据库中。我有 3 个域对象:IngredientRecipeIngredientRecipe。我正在使用 DTO 概念,并且我有对应于域对象 3 个 DTO 的类:IngredientDtoRecipeDtoIngredientRecipeDto。添加Ingredients 的表单可以正常工作。问题是当我尝试创建一个新的Recipe 时。错误消息显示 2 个类中的错误:RecipeMapper 和 RecipeController。我的错误与将域对象映射到 DTO 有关,我不知道如何解决这个问题。请给我一些建议如何修复此问题

完全错误

blazor.webassembly.js:1 crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: AutoMapper.AutoMapperMappingException: Error mapping types.
      
      Mapping types:
      RecipeDto -> Recipe
      CookBook.Shared.Data.Dto.RecipeDto -> CookBook.Shared.Entities.Recipe
      
      Type Map configuration:
      RecipeDto -> Recipe
      CookBook.Shared.Data.Dto.RecipeDto -> CookBook.Shared.Entities.Recipe
      
      Destination Member:
      ListOfIngredients
      
       ---> AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping.
      
      Mapping types:
      IngredientRecipeDto -> IngredientRecipe
      CookBook.Shared.Data.Dto.IngredientRecipeDto -> CookBook.Shared.Entities.IngredientRecipe
         at lambda_method83(Closure , IngredientRecipeDto , IngredientRecipe , ResolutionContext )
         at lambda_method82(Closure , Object , Recipe , ResolutionContext )
         --- End of inner exception stack trace ---
         at lambda_method82(Closure , Object , Recipe , ResolutionContext )
         at CookBook.Server.Mappers.RecipeMapper.Map(RecipeDto recipeDto) in C:\Users\48510\source\repos\CookBook\CookBook\Server\Mappers\RecipeMapper.cs:line 32
         at CookBook.Server.Controllers.RecipesController.Post(RecipeDto recipeDto) in C:\Users\48510\source\repos\CookBook\CookBook\Server\Controllers\RecipesController.cs:line 58
         at lambda_method42(Closure , Object )
         at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
      --- End of stack trace from previous location ---
         at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
         at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
         at Microsoft.AspNetCore.Builder.Extensions.MapWhenMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Builder.Extensions.MapMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
      
      HEADERS
      =======
      Accept: */*
      Accept-Encoding: gzip, deflate, br
      Accept-Language: pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7
      Connection: close
      Content-Length: 164
      Content-Type: application/json; charset=utf-8
      Host: localhost:44332
      Referer: https://localhost:44332/recipes/create
      User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
      origin: https://localhost:44332
      sec-fetch-site: same-origin
      sec-fetch-mode: cors
      sec-fetch-dest: empty
      
System.ApplicationException: AutoMapper.AutoMapperMappingException: Error mapping types.

Mapping types:
RecipeDto -> Recipe
CookBook.Shared.Data.Dto.RecipeDto -> CookBook.Shared.Entities.Recipe

Type Map configuration:
RecipeDto -> Recipe
CookBook.Shared.Data.Dto.RecipeDto -> CookBook.Shared.Entities.Recipe

Destination Member:
ListOfIngredients

 ---> AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping.

Mapping types:
IngredientRecipeDto -> IngredientRecipe
CookBook.Shared.Data.Dto.IngredientRecipeDto -> CookBook.Shared.Entities.IngredientRecipe
   at lambda_method83(Closure , IngredientRecipeDto , IngredientRecipe , ResolutionContext )
   at lambda_method82(Closure , Object , Recipe , ResolutionContext )
   --- End of inner exception stack trace ---
   at lambda_method82(Closure , Object , Recipe , ResolutionContext )
   at CookBook.Server.Mappers.RecipeMapper.Map(RecipeDto recipeDto) in C:\Users\48510\source\repos\CookBook\CookBook\Server\Mappers\RecipeMapper.cs:line 32
   at CookBook.Server.Controllers.RecipesController.Post(RecipeDto recipeDto) in C:\Users\48510\source\repos\CookBook\CookBook\Server\Controllers\RecipesController.cs:line 58
   at lambda_method42(Closure , Object )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at Microsoft.AspNetCore.Builder.Extensions.MapWhenMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Builder.Extensions.MapMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

HEADERS
=======
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: pl-PL,pl;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close
Content-Length: 164
Content-Type: application/json; charset=utf-8
Host: localhost:44332
Referer: https://localhost:44332/recipes/create
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36
origin: https://localhost:44332
sec-fetch-site: same-origin
sec-fetch-mode: cors
sec-fetch-dest: empty

   at CookBook.Client.Repositories.RecipeRepository.CreateRecipe(RecipeDto recipeDto) in C:\Users\48510\source\repos\CookBook\CookBook\Client\Repositories\RecipeRepository.cs:line 30
   at CookBook.Client.Pages.Recipe.RecipeTest.SaveRecipe() in C:\Users\48510\source\repos\CookBook\CookBook\Client\Pages\Recipe\RecipeTest.razor:line 121
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Microsoft.AspNetCore.Components.Forms.EditForm.HandleSubmitAsync()
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle)

我的域类

 public class Ingredient : BaseEntity
    {
        public string Name { get; set; }
        public int Kcal { get; set; }
        public virtual List<IngredientRecipe> IngredientRecipe { get; set; }
    }

public class Recipe : BaseEntity
    {
        public string Name { get; set; }
        public virtual List<IngredientRecipe> ListOfIngredients { get; set; }
    }
public class IngredientRecipe : BaseEntity
    {
        public int IngredientId { get; set; }
        public int RecipeId { get; set; }
        public int Amount { get; set; }
        public virtual Ingredient Ingredient { get; set; }
        public virtual Recipe Recipe { get; set; }
    }

我的 DTO

public class IngredientDto : BaseEntity
    {
        public string Name { get; set; }
        public int Kcal { get; set; }
        public List<IngredientRecipe> IngredientRecipe { get; set; }
    }

 public class RecipeDto : BaseEntity
    {
        public string Name { get; set; }

        public List<IngredientRecipeDto> ListOfIngredients { get; set; } = new List<IngredientRecipeDto>();
    }

 public class IngredientRecipeDto : BaseEntity
    {
        public int IngredientId { get; set; }
        public int Amount { get; set; }
        public IngredientDto Ingredient { get; set; }
        public RecipeDto Recipe { get; set; }
    }

RecipeMapper,出现错误

 public class RecipeMapper
    {
        private IMapper mapper;

        public RecipeMapper()
        {
            mapper = new MapperConfiguration(config =>
            {
                config.CreateMap<Recipe, RecipeDto>()
                      .ForMember(dest => dest.ListOfIngredients, conf => conf.MapFrom(src => src.ListOfIngredients))
                      .ReverseMap();
            }).CreateMapper();

        }
        public RecipeDto Map(Recipe recipe)
        {
            return mapper.Map<RecipeDto>(recipe);
        }

        public Recipe Map(RecipeDto recipeDto)
        {
            //at this line is error
            return mapper.Map<Recipe>(recipeDto);
        }

        public List<RecipeDto> Map(List<Recipe> recipes)
        {
            return mapper.Map<List<Recipe>, List<RecipeDto>>(recipes);
        }
    }

还有RecipeController,Post方法出错

 public class RecipesController
    {
        private readonly AppDbContext context;
        private readonly RecipeMapper mapper;

        public RecipesController(AppDbContext context, RecipeMapper mapper)
        {
            this.context = context;
            this.mapper = mapper;
        }

        [HttpGet]
        public async Task<ActionResult<List<RecipeDto>>> Get()
        {
            var ListOfRecipes = await context.Recipes
                .Include(x => x.ListOfIngredients)
                .ThenInclude(x => x.Ingredient)
                .ToListAsync();

            var ListOfRecipesDto = mapper.Map(ListOfRecipes);

            return ListOfRecipesDto;
        }

        [HttpGet("{id}")]
        public async Task<ActionResult<RecipeDto>> Get(int id)
        {
            var recipe = await context.Recipes
                .Include(x => x.ListOfIngredients)
                .ThenInclude(x => x.Ingredient)
                .FirstOrDefaultAsync();

            var recipeDto = mapper.Map(recipe);

            if (recipe == null) { return new NotFoundResult(); }

            return recipeDto;
        }

        [HttpPost]
        public async Task<ActionResult<int>> Post(RecipeDto recipeDto)
        {
            //here is error
            var recipe = mapper.Map(recipeDto);

            context.Recipes.Add(recipe);
            await context.SaveChangesAsync();

            return recipe.Id;
        }

        [HttpDelete("{id}")]
        public async Task Delete(int id)
        {
            var recipeToRemove = context.Recipes.FirstOrDefault(x => x.Id == id);

            context.Recipes.Remove(recipeToRemove);
            await context.SaveChangesAsync();
        }
    }

也许问题出在我的表单上,所以我提供了一个 github 链接 https://github.com/szymonJag/CookBook/blob/master/CookBook/Client/Pages/Recipe/RecipeTest.razor

【问题讨论】:

标签: c# asp.net entity-framework automapper blazor


【解决方案1】:

对我来说,感觉就像您正在迁移旧项目并使用 AutoMapper 的旧方式。在文档中说:“从 9.0 开始,静态 API 不再可用。”异常还表明映射器无法找到映射。 执行此操作的新方法是创建配置文件并在那里配置映射。配置文件应在使用过的 DI 中注册。

个人资料示例:

public class RecipeProfile: Profile
{
    public RecipeProfile()
    {
        CreateMap<Recipe,RecipeDto>();
    }
}

在 DI 中注册配置文件(在 Startup.cs 中): services.AddAutoMapper(profileAssembly1, profileAssembly2 /*, ...*/);

之后您可以使用ProjectToIMapper _mapper。如果您使用的是 EF Core,我推荐 ProjectTo。这会稍微修改您的查询 SELECT 语句以仅查询映射的属性。

我使用 AutoMapper 文档作为参考。

Configuring AutoMapper

AutoMapper DI

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-21
    • 2021-08-25
    • 1970-01-01
    • 2022-11-30
    • 2022-01-01
    • 2021-11-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多