首页 > 其他分享 >ABP .NET创建项目(三)

ABP .NET创建项目(三)

时间:2023-08-15 11:00:57浏览次数:41  
标签:缓存 创建 entity ABP input id using NET public

ABP.NET 创建项目(三)(进阶部分)

ABP.NET 创建项目(二)(进阶部分)的基础上增加代码

增加缓存方法。
好处:比如在多次重复的数据库查询操作中,结果相同,但利用缓存可以使得第一次同普通查询一样查询,而后续的重复操作查询可以直接用(return)缓存中存储的查询结果而非再次进行数据库查询操作。旨在缩短加载时间(在大项目数据条目极大的数据库或者同样的查询请求很多的情形下十分常见)

11.需增加(Red)&需改写(Green)


11.1:需要更改的原始文件(Green)

相关文档

一 :Practice20223CoreModule.cs:

using Abp.AutoMapper;
using Abp.Dependency;
using Abp.Localization;
using Abp.Modules;
using Abp.Reflection.Extensions;
using Abp.Runtime.Security;
using Abp.Timing;
using Abp.Zero;
using Abp.Zero.Configuration;
using Practice2023.Authorization.Roles;
using Practice2023.Authorization.Users;
using Practice2023.Configuration;
using Practice2023.Localization;
using Practice2023.MultiTenancy;
using Practice2023.Students;
using Practice2023.Students.Caches;
using Practice2023.Timing;
using System;

namespace Practice2023
{
    //AbpAutoMapperModule
    //[DependsOn(typeof(AbpAutoMapperModule))] 表示该模块依赖于 AbpAutoMapperModule。
    //AbpAutoMapperModule: 提供了与 AutoMapper 的集成,用于对象映射
    //用于下面Configuration.Modules.AbpAutoMapper()
    [DependsOn(
        typeof(AbpZeroCoreModule),
        typeof(AbpAutoMapperModule))]
    public class Practice2023CoreModule : AbpModule
    {
        public override void PreInitialize()
        {
            Configuration.Auditing.IsEnabledForAnonymousUsers = true;
            // Declare entity types
            Configuration.Modules.Zero().EntityTypes.Tenant = typeof(Tenant);
            Configuration.Modules.Zero().EntityTypes.Role = typeof(Role);
            Configuration.Modules.Zero().EntityTypes.User = typeof(User);
            Practice2023LocalizationConfigurer.Configure(Configuration.Localization);
            // Enable this line to create a multi-tenant application.
            Configuration.MultiTenancy.IsEnabled = Practice2023Consts.MultiTenancyEnabled;
            // Configure roles
            AppRoleConfig.Configure(Configuration.Modules.Zero().RoleManagement);
            Configuration.Settings.Providers.Add<AppSettingProvider>();
            Configuration.Localization.Languages.Add(new LanguageInfo("fa", "فارسی", "famfamfam-flags ir"));
            Configuration.Settings.SettingEncryptionConfiguration.DefaultPassPhrase = Practice2023Consts.DefaultPassPhrase;
            SimpleStringCipher.DefaultPassPhrase = Practice2023Consts.DefaultPassPhrase;

            //新增加项
            // Configure auto mapper
            //可以根据需要在应用程序中的不同位置使用 GetAssembly 方法来获取模块的程序集,以便进行一些反射操作或其他需要使用程序集的场景。
            //扫描程序集,寻找继承自AutoMapper.Profile的类。
            var thisAssembly = typeof(Practice2023CoreModule).GetAssembly();
            Configuration.Modules.AbpAutoMapper().Configurators.Add(cfg => cfg.AddMaps(thisAssembly));
        }
        public override void Initialize()
        {
            IocManager.RegisterAssemblyByConvention(typeof(Practice2023CoreModule).GetAssembly());
        }
        public override void PostInitialize()
        {
            IocManager.Resolve<AppTimes>().StartupTime = Clock.Now;

            //新添加部分
            // Init Caches
            CacheInitialize();
        }
        //添加的部分
        //在这段代码中,CacheInitialize() 方法用于初始化缓存配置。
        //具体来说,它配置了一个名为StudentCache.CacheKey的缓存项,并设置了默认的滑动过期时间为1天。
        //通过调用 Configuration.Caching.Configure() 方法,并传入缓存项的键(StudentCache.CacheKey)和一个回调函数,可以对指定的缓存项进行配置
        //在这里,回调函数中设置了默认的滑动过期时间为1天,表示存储在这个缓存项中的数据将在最后一次访问后的1天内过期,过期后将不再有效。
        public void CacheInitialize()
        {
            Configuration.Caching.Configure(StudentCache.CacheKey, cache =>
            {
                cache.DefaultSlidingExpireTime = TimeSpan.FromDays(1);
            });
        }
    }
}

这里增加的代码为

//AbpAutoMapperModule
//[DependsOn(typeof(AbpAutoMapperModule))] 表示该模块依赖于 AbpAutoMapperModule。
//AbpAutoMapperModule: 提供了与 AutoMapper 的集成,用于对象映射
//用于下面Configuration.Modules.AbpAutoMapper()
[DependsOn(
    typeof(AbpZeroCoreModule),
    typeof(AbpAutoMapperModule))]
//新增加项
// Configure auto mapper
//可以根据需要在应用程序中的不同位置使用 GetAssembly 方法来获取模块的程序集,以便进行一些反射操作或其他需要使用程序集的场景。
var thisAssembly = typeof(Practice2023CoreModule).GetAssembly();
Configuration.Modules.AbpAutoMapper().Configurators.Add(cfg => cfg.AddMaps(thisAssembly));

依赖模块AbpAutoMapperModule.扫描程序集,寻找继承自AutoMapper.Profile的类。找到所有的CreatMap.这里主要找的是我们新创建的StudentCoreMapProfile.cs.

//新添加部分
// Init Caches
CacheInitialize();
public void CacheInitialize()
{
    //这里的StudentCache.CacheKey是在StudentCache.cs中定义的"Students"
    Configuration.Caching.Configure(StudentCache.CacheKey, cache =>
    {
        //设置缓存滑动过期时长
        cache.DefaultSlidingExpireTime = TimeSpan.FromDays(1);
    });
}

这里应该是新建一个缓存空间(有点像是新建名为CacheKey也就是名为"Students"的文件夹,里面的缓存的文件被命名为"Students_{id}")
在后面利用自定义的PrintCacheContent()函数可以看到确实新增了一个缓存名为"Students"的空间。(剩余四个是模板原生类的缓存空间)

实际上发现似乎在`CacheInitialize()`后并没有新建该Students(ConsolewriteLine不出来)而是在真正往缓存里放入东西时才会看到该名为Students的缓存名.

11.2:需要新建的文件(Red)

一:IStudentCache.cs,StudentCache.cs,StudentCacheItem.cs,StudentCoreMapProfile.cs,Practice2023CacheExtensions:

第二者内定义了与缓存相关的函数方法.
第三者定义了缓存类的属性(正常情况与Student类没区别,但若想自定义缓存的字段的话就会有区别).
第四者定义了缓存类(第三者)与Student类的映射.
第五者

using System.Threading.Tasks;
namespace Practice2023.Students.Caches
{
    public interface IStudentCache
    {
        Task<StudentCacheItem> GetAsync(long id);
        void Remove(long id);
    }
}
using Abp.Dependency;
using Abp.Domain.Repositories;
using Abp.Events.Bus.Entities;
using Abp.Events.Bus.Handlers;
using Abp.ObjectMapping;
using Abp.Runtime.Caching;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace Practice2023.Students.Caches
{
    public class StudentCache :
        IEventHandler<EntityCreatedEventData<Student>>,
        IEventHandler<EntityUpdatedEventData<Student>>,
        IEventHandler<EntityDeletedEventData<Student>>, 
        ITransientDependency, IStudentCache
    
    {
        private readonly IRepository<Student, long> _repository;
        private readonly ICacheManager _cacheManager;
        private readonly IObjectMapper _objectMapper;

        //=>换成=是一样的
        public static string CacheKey => "Students";

        public StudentCache(
            IRepository<Student, long> repository, 
            ICacheManager cacheManager, 
            IObjectMapper objectMapper)
        {
            _repository = repository;
            _cacheManager = cacheManager;
            _objectMapper = objectMapper;
        }

        //用于打印当前缓存的缓存名
        public void PrintCacheContent()
        {
            foreach (var cacheName in _cacheManager.GetAllCaches().Select(x => x.Name))
            {
                Console.WriteLine($"cacheName: {cacheName}");
            }
        }
        //调用了原生cacheManager.GetStudentCacheAsync().GetAsync()
        public async Task<StudentCacheItem> GetAsync(long id)
        {
            PrintCacheContent();
            //第一个参数$"Student_{id}"应该是该缓存子文件的命名,后面是该缓存存储的内容
            return await _cacheManager.GetStudentCacheAsync().GetAsync($"Student_{id}", async () =>
            {
                var student = await _repository.GetAll()
                    .Include(x => x.Extra)
                    .Include(x => x.ClassGroups).ThenInclude(x => x.ClassGroup)
                    .FirstOrDefaultAsync(x => x.Id == id);

                if (student == null) return null;

                return _objectMapper.Map<StudentCacheItem>(student);
                //StudentCacheItem<--Student
                //这里面的return是返回StudentCacheItem类型给async()
                //看起来查询的语句和Student的GetAsync没什么区别
            });
        }
        //通过`Practice2023CacheExtensions.cs`间接调用了原生cacheManager.cacheManager.GetCache()
        public void Remove(long id)
        {
            _cacheManager.GetStudentCacheAsync().Remove($"Student_{id}");
        }
        //在这里,IEventHandler<EntityxxxxEventData>的作用是当表记录有所更新时,将触发事件,
        //程序里面主要是移除数据缓存
        //这三个HandleEvent是重载了三个输入类型吗,为什么不用泛型呢
        public void HandleEvent(EntityCreatedEventData<Student> eventData)
        {
            Remove(eventData.Entity.Id);
        }

        public void HandleEvent(EntityUpdatedEventData<Student> eventData)
        {
            Remove(eventData.Entity.Id);
        }

        public void HandleEvent(EntityDeletedEventData<Student> eventData)
        {
            Remove(eventData.Entity.Id);
        }
    }
}

其中CacheKey => "Students"是用于前面CacheInitialize()调用的名.
HandleEvent是重载的时间触发器,对应数据库实体发生增,改,删时触发对应的事件.这里对应的事件均为缓存的清除操作(利用原生CacheManager中的缓存方法)
这里实现缓存的是CacheManager.GetCache.GetAsync($"Student_{id}", async () =>...).在第二次执行该函数时会检测是否存在"Student_{id}",存在则直接调取该缓存.

using Practice2023.ClassGroups;
using System.Collections.Generic;

namespace Practice2023.Students.Caches
{
    public class StudentCacheItem//和Student基本无差,所以可做map映射
    {
        /// <summary>
        /// ID
        /// </summary>
        public long Id { get; set; }
        /// <summary>
        /// 姓名
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 性别
        /// </summary>
        public string Sex { get; set; }
        /// <summary>
        /// 身份证号码
        /// </summary>
        public string IdCardNumber { get; set; }
        /// <summary>
        /// 学号
        /// </summary>
        public string Number { get; set; }
        /// <summary>
        /// 额外信息
        /// </summary>
        public StudentExtraCacheItem Extra { get; set; } = new StudentExtraCacheItem();
        /// <summary>
        /// 所在班组
        /// </summary>
        public ICollection<StudentClassGroupCacheItem> ClassGroups { get; set; } = new List<StudentClassGroupCacheItem>();
    }
    public class StudentExtraCacheItem//和StudentExtra基本无差,所以可做map映射
    {
        public string Extra1 { get; set; }
        public string Extra2 { get; set; }
        public string Extra3 { get; set; }
    }
    public class StudentClassGroupCacheItem//和StudentClassGroup基本无差,所以可做map映射
    {
        /// <summary>
        /// 班组ID
        /// </summary>
        public long ClassGroupId { get; set; }
        /// <summary>
        /// 班组名称
        /// </summary>
        public string ClassGroupName { get; set; }
        /// <summary>
        /// 班组类型
        /// </summary>
        public ClassGroupType ClassGroupType { get; set; }
        /// <summary>
        /// 班组类型
        /// </summary>
        public string ClassGroupTypeDescription { get; set; }
    }
}

这里和Student.cs的定义差不多,因为缓存也可以只缓存部分信息,所以可以自定义

using Abp.Runtime.Caching;
using Practice2023.Students.Caches;

namespace Practice2023
{
    public static class Practice2023CacheExtensions
    {
        public static ITypedCache<string, StudentCacheItem> GetStudentCacheAsync(this ICacheManager cacheManager)
        {
            return cacheManager.GetCache<string, StudentCacheItem>(StudentCache.CacheKey);
        }
    }
}

这部分是关键的缓存空间位置获取.
在上面可以看到ITypedCache类型存在GetAsync(string,StudentCacheItem),Remove(string)方法.


11.3:这里还需要对StudentAppService.cs进行修改(以及IStudentAppService.cs),增加GetCacheAsync()方法进去Swagger接口

一 :StudentAppService.cs:

using Abp.Application.Services.Dto;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.Linq.Extensions;
using Abp.Runtime.Caching;
using Abp.UI;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Practice2023.Students.Caches;
using Practice2023.Students.Dto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Practice2023.Students
{
    public class StudentAppService : Practice2023AppServiceBase, IStudentAppService
    {
        private readonly IRepository<Student, long> _studentRepository;
        private readonly IRepository<StudentExtra, long> _studentExtraRepository;
        private readonly IRepository<StudentClassGroup, long> _studentClassGroupRepository;
        //新加项
        private readonly IStudentCache _studentCache;

        public StudentAppService(
            IRepository<Student, long> studentRepository,
            IRepository<StudentExtra, long> studentExtraRepository,
            IRepository<StudentClassGroup, long> studentClassGroupRepository,
            IStudentCache studentCache)
        {
            _studentRepository = studentRepository;
            _studentExtraRepository = studentExtraRepository;
            _studentClassGroupRepository = studentClassGroupRepository;
            _studentCache = studentCache;
        }
        //新增部分
        /// <summary>
        /// 获取学生信息缓存
        /// </summary>
        public async Task<StudentCacheItem> GetCacheAsync(long id)//缓存获取操作
        {
            return await _studentCache.GetAsync(id);
        }

        /// <summary>
        /// 获取学生信息
        /// </summary>
        public async Task<StudentDto> GetAsync(long id)
        {
            var entity = await _studentRepository.GetAll()
                .Include(x => x.Extra)
                .Include(x => x.ClassGroups).ThenInclude(x => x.ClassGroup)
                .FirstOrDefaultAsync(x => x.Id == id);

            if (entity == null) return null;

            return ObjectMapper.Map<StudentDto>(entity);
        }

        /// <summary>
        /// 获取学生信息分页列表
        /// </summary>
        [HttpPost]
        public async Task<PagedResultDto<StudentDto>> GetPagedListAsync(PagedStudentResultRequestDto input)
        {
            var query = _studentRepository.GetAll()
                .Include(x => x.Extra)
                .Include(x => x.ClassGroups).ThenInclude(x => x.ClassGroup)
                .WhereIf(!input.Keyword.IsNullOrWhiteSpace(), x =>
                    x.Name.Contains(input.Keyword) ||
                    x.Number.Contains(input.Keyword) ||
                    x.IdCardNumber.Contains(input.Keyword));

            var test = query.OrderByDescending(x => x.Id).PageBy(input).ToQueryString();

            var totalCount = await query.CountAsync();
            var queryResult = await query.OrderByDescending(x => x.Id).PageBy(input).ToListAsync();

            return new PagedResultDto<StudentDto>(totalCount, ObjectMapper.Map<List<StudentDto>>(queryResult));
        }

        private async Task CheckIdCardNumberAsync(string idCardNumber, long? id = null)
        {
            if (idCardNumber.IsNullOrWhiteSpace()) return;
            var isExists = await _studentRepository.GetAll().WhereIf(id.HasValue, x => x.Id != id).AnyAsync(x => x.IdCardNumber == idCardNumber);
            if (isExists) throw new UserFriendlyException($"身份证号码“{idCardNumber}”已存在!");
        }

        private async Task CheckNumberAsync(string number, long? id = null)
        {
            if (number.IsNullOrWhiteSpace()) return;
            var isExists = await _studentRepository.GetAll().WhereIf(id.HasValue, x => x.Id != id).AnyAsync(x => x.Number == number);
            if (isExists) throw new UserFriendlyException($"学号“{number}”已存在!");
        }

        /// <summary>
        /// 创建学生
        /// </summary>
        public async Task CreateAsync(CreateStudentDto input)
        {
            await CheckIdCardNumberAsync(input.IdCardNumber);
            await CheckNumberAsync(input.Number);

            var entity = new Student(input.Name, input.Sex, input.IdCardNumber, input.Number); // 也可以使用AutoMapper

            entity.AddExtra(input.Extra.Extra1, input.Extra.Extra2, input.Extra.Extra3);

            foreach (var classGroupId in input.ClassGroupIds)
            {
                entity.AddToClassGroup(classGroupId);
            }

            await _studentRepository.InsertAsync(entity);
        }

        /// <summary>
        /// 更新学生
        /// </summary>
        public async Task UpdateAsync(UpdateStudentDto input)
        {
            await CheckIdCardNumberAsync(input.IdCardNumber, input.Id);
            await CheckNumberAsync(input.Number, input.Id);

            var entity = await _studentRepository.GetAll()
                .Include(x => x.Extra)
                .Include(x => x.ClassGroups).ThenInclude(x => x.ClassGroup)
                .FirstOrDefaultAsync(x => x.Id == input.Id);

            if (entity == null) return;

            entity.Name = input.Name;
            entity.Sex = input.Sex;
            entity.IdCardNumber = input.IdCardNumber;
            entity.Number = input.Number;

            ObjectMapper.Map(input.Extra, entity.Extra);

            var oriClassGroupIds = entity.ClassGroups.Select(x => x.ClassGroupId).ToList();
            var newClassGroupIds = input.ClassGroupIds.Except(oriClassGroupIds);
            var delClassGroupIds = oriClassGroupIds.Except(input.ClassGroupIds);

            foreach (var newClassGroupId in newClassGroupIds)
            {
                entity.AddToClassGroup(newClassGroupId);
            }

            foreach(var delClassGroupId in delClassGroupIds)
            {
                entity.RemoveToClassGroup(delClassGroupId);
            }

            await _studentRepository.UpdateAsync(entity);
        }

        /// <summary>
        /// 删除学生
        /// </summary>
        public async Task DeleteAsync(long id)
        {
            var entity = await _studentRepository.FirstOrDefaultAsync(x => x.Id == id);
            if (entity == null) return;

            await _studentClassGroupRepository.DeleteAsync(x => x.StudentId == entity.Id);
            await _studentExtraRepository.DeleteAsync(x => x.StudentId == entity.Id);
            await _studentRepository.DeleteAsync(x => x.Id == entity.Id);
        }
    }
}

新增部分

private readonly IStudentCache _studentCache;

public StudentAppService(
            IRepository<Student, long> studentRepository,
            IRepository<StudentExtra, long> studentExtraRepository,
            IRepository<StudentClassGroup, long> studentClassGroupRepository,
            IStudentCache studentCache)
        {
            _studentRepository = studentRepository;
            _studentExtraRepository = studentExtraRepository;
            _studentClassGroupRepository = studentClassGroupRepository;
            _studentCache = studentCache;
        }
public async Task<StudentCacheItem> GetCacheAsync(long id)//缓存获取操作
        {
            return await _studentCache.GetAsync(id);
        }

12.测试效果

以下依次是第一次使用使用查询时间,第二次使用查询时间

在数据量很大的情况下,第一次与后面几次的运行时间会相差很大.这里由于数据并不大因此...

13.DeBug与问题解答

Q1&A1:在Dbcontext中并没有设置Dbset,因此自然没有Cache表

标签:缓存,创建,entity,ABP,input,id,using,NET,public
From: https://www.cnblogs.com/DoubiCan/p/17630742.html

相关文章

  • “未能创建此平台,因为已存在同名的解决方案平台”
    1.解释:使用2010新建了一个工程,后来加入了另外一个64位工程,联合编译的时候发现主工程的平台是win32的。但是怎么都修改不成64的,未能创建此平台,因为已存在同名的解决方案平台。 于是移除掉新的工程;发现配置管理器中存在win64和win32,由于已经存在了win64,所以不能创建了。需要修......
  • 基于ASP.NET企业合同信息管理设计与实现
    系统模块结构设计企业合同信息管理系统主要分为登录、系统管理、客户信息管理、客户人员管理、商业往来管理、合同信息管理、信息查询等模块。(1)登录输入用户名称和密码,如果用户名、密码正确,则允许进入主控制平台,并根据相应的用户权限,显示相应界面;如果输入错误则给出信息提示,重新......
  • git 仓库创建并导入项目
    1.初始化项目gitinit本地出现.git文件2、本地项目的文件gitadd.   添加所有的文件gitadd--all 添加所有的文件3、gitstatus显示工作目录和暂存区的状态4、gitcommit提交到版本库gitcommit-m"此处写提交备注"gitpushoriginmaster......
  • Android22.3.1创建项目
    一、创建传统Java语言项目重点:EmptyActivity不能选择java.步骤一:步骤二:二、GradleBuild速度慢修改pluginManagement{repositories{google()mavenCentral()gradlePluginPortal()}}dependencyResolutionManagement{reposito......
  • java opencv创建 空图片
    javaopencv创建空图片  packageml;importorg.opencv.core.Core;importorg.opencv.core.CvType;importorg.opencv.core.Mat;importorg.opencv.core.Scalar;importorg.opencv.highgui.HighGui;publicclassTest2{publicstaticvoidmain(String[......
  • Linux:netstat指令
    学习自:linux下netstat指令详解_linuxnetstat命令_乘凉~的博客-CSDN博客官网:netstat命令的官方文档1、简介netstat是Linux中常用网络工具,用于显示网络连接、路由表、网络接口等相关信息。它可以帮助我们监控网络活动、诊断网络问题、查看网络连接状态。2、显示所有网络连接n......
  • .net6webapi中配置Jwt实现鉴权验证
    JWT(JsonWebToken)jwt是一种用于身份验证的开放标准,他可以在网络之间传递信息,jwt由三部分组成:头部,载荷,签名。头部包含了令牌的类型和加密算法,载荷包含了用户的信息,签名则是对头部和载荷的加密结果。jwt鉴权验证是指在用户登录成功后,服务器生成一个jwt令牌并返回给客户端,客户端在......
  • asp.net core配置验证
    在开发asp.netcore时,通常会在appsettings.json中配置信息,这些信息都是以一个json的格式存储,在程序中通过Options的方式来绑定实体类使用,如下面的APIConfig和实体类。配置文件:{"Logging":{"LogLevel":{"Default":"Information","Microsoft.AspNet......
  • asp.net core配置验证
    在开发asp.netcore时,通常会在appsettings.json中配置信息,这些信息都是以一个json的格式存储,在程序中通过Options的方式来绑定实体类使用,如下面的APIConfig和实体类。配置文件:{"Logging":{"LogLevel":{"Default":"Information","Microsoft.As......
  • asp.net core配置验证
    在开发asp.netcore时,通常会在appsettings.json中配置信息,这些信息都是以一个json的格式存储,在程序中通过Options的方式来绑定实体类使用,如下面的APIConfig和实体类。配置文件:{"Logging":{"LogLevel":{"Default":"Information","Microsoft.As......