abp學習日誌九(總結)

AutoMaper

abp支持從Service映射到API,在Service 中有一個CurdAppService的泛型類,泛型類用到了幾個參數
代碼如下
(舉例代碼)

/*SystemApp:集合跟(實體)
*SystemAppDto:查詢結果dto
*Guid:Key
*PagedAndSortedResultRequestDto:abp給的默認分頁排序查詢RequestDto
*CreateSystemAppDto:創建對象的Dto
*UpdateSystemAppDto:更新對象的Dto
*/
 public class SystemAppService : CrudAppService<SystemApp, SystemAppDto, Guid, PagedAndSortedResultRequestDto, CreateSystemAppDto, UpdateSystemAppDto>, ISystemAppService
    {
        private readonly IRepository<SystemApp, Guid> productRepository;
        private readonly ICurrentTenant currentTenant;
        public SystemAppService(IRepository<SystemApp, Guid> productRepository, ICurrentTenant currentTenant) : base(productRepository)
        {
            this.productRepository = productRepository;
            this.currentTenant = currentTenant;
        }
    }

看到上面的代碼有那麼多Dto,dto和聚合根(實體)是如何實現屬性對應的呢?Abp使用的是AutoMap。
使用方法就是在項目中(Application項目)有一個XXXXAutoMapperProfile.cs的文件,直接在這裏面寫上
CreateMap<SystemApp,SystemAppDto>()
如果需要更多那就都寫上。

官方文檔也是這樣說的。

坑1

如果你的項目使用的是Module模板,並且是2.2.1以上的版本,請注意,不能使用默認的,除非你的Dto和實體的屬性絲毫不差,否則會出現異常。

  • 請添加參數 MemberList.None。關於MemberList枚舉類型有三個值,有興趣可以都試試

坑2(這個不算坑)

如果項目存在很多個Dto和實體,那麼寫Create會寫到懷疑人生。可以試試反射,反射的前提是做好namespace,否則反射的方法寫起來比較麻煩,我的namespace規則,Dto都在XXXx.Dto命名空間。我的聚合根都在XXX.Model命名空間。

 public VShopApplicationAutoMapperProfile()
        {
            /* You can configure your AutoMapper mapping configuration here.
             * Alternatively, you can split your mapping configurations
             * into multiple profile classes for a better organization. */
            var types = Assembly.Load("xxxr.xxx.Application.Contracts").GetTypes();
            var domainTypes = Assembly.Load("xxx.xxx.Domain").GetTypes();
            foreach (var type in types)
            {
                if (type.Namespace == "xxx.xxx.Dto")
                {
                    if (type.Name.StartsWith("Create") || type.Name.StartsWith("Update"))
                    {
                        var domainTypeName = type.Name.Replace("Create", "").Replace("Update", "").Replace("Dto", "");
                        if (domainTypes.Any(t => t.Name == domainTypeName))
                            CreateMap(type, domainTypes.First(t => t.Name == domainTypeName), MemberList.None);
                    }
                    else
                    {
                        var domainTypeName = type.Name.Replace("Dto", "");
                        if (domainTypes.Any(t => t.Name == domainTypeName))
                            CreateMap(domainTypes.First(t => t.Name == domainTypeName), type, MemberList.None);
                    }
                }
            }
        }

Application

爲什麼會定位到這個項目呢,因爲錯誤是發生在Service裏面的。錯誤內容如下

2020-03-29 11:25:38.867 +08:00 [ERR] {
  "code": null,
  "message": "對不起,在處理你的請求期間,產生了一個服務器內部錯誤!",
  "details": null,
  "validationErrors": null
}
2020-03-29 11:25:38.868 +08:00 [ERR] An exception was thrown while activating Castle.Proxies.SystemRoleServiceProxy.
Autofac.Core.DependencyResolutionException: An exception was thrown while activating Castle.Proxies.SystemRoleServiceProxy.
 ---> Autofac.Core.DependencyResolutionException: None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Castle.Proxies.SystemRoleServiceProxy' can be invoked with the available services and parameters:
Cannot resolve parameter 'Volo.Abp.Domain.Repositories.IRepository`2[Sugar.VShop.Model.SystemRole,System.Guid] productRepository' of constructor 'Void .ctor(Castle.DynamicProxy.IInterceptor[], Volo.Abp.Domain.Repositories.IRepository`2[Sugar.VShop.Model.SystemRole,System.Guid], Volo.Abp.MultiTenancy.ICurrentTenant)'.
   at Autofac.Core.Activators.Reflection.ReflectionActivator.GetValidConstructorBindings(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters, Object& decoratorTarget)
   --- End of inner exception stack trace ---
   at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters, Object& decoratorTarget)
   at Autofac.Core.Resolving.InstanceLookup.Execute()
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.ResolveOperation.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
   at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType)
   at Autofac.Extensions.DependencyInjection.AutofacServiceProvider.GetRequiredService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.AspNetCore.Mvc.Controllers.ServiceBasedControllerActivator.Create(ControllerContext actionContext)
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)
   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 where exception was thrown ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

大致的意思就是無法反射出對應的構造函數。

網上找到了一個文章很有啓發意義 https://www.codetd.com/article/7994226

這個文章的意思是說沒有做DbSet 導致實體無法映射,也就是說聚合根對應的倉儲無法反射,這個倒是挺符合道理的。

但是同樣的代碼放到 有UI的項目下可用,放到–no-ui的項目下不可用。

坑1

填坑。
需要在EntityFramework項目中添加如下代碼

namespace Sugar.VShop.EntityFrameworkCore
{
    [DependsOn(
        typeof(VShopDomainModule),
        typeof(AbpEntityFrameworkCoreModule)
    )]
    public class VShopEntityFrameworkCoreModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            context.Services.AddAbpDbContext<VShopDbContext>(options =>
            {
                //這裏是需要添加的代碼
                options.AddDefaultRepositories();
                /* Add custom repositories here. Example:
                 * options.AddRepository<Question, EfCoreQuestionRepository>();
                 */
            });
        }

        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            base.OnApplicationInitialization(context);
        }
    }
}

因爲abp的模板文件就是添加到這裏的,但是這個不合理,所以我把這段代碼添加到了啓動項目下。

坑2

不要讓Service中包含App或者Application等關鍵字,比如有個服務叫SystemAppService,映射到API後會沒有APP關鍵字,直接變成了XXX/System/{1}

https://www.codetd.com/article/7994226

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章