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

ABP.NET创建项目(一)

时间:2023-08-10 13:14:42浏览次数:46  
标签:MyProject 创建 System ABP input AssignedPersonId using NET public

ABP.NET 创建项目

相关文档1(下半部分)
相关文档2(MySql部分)

1.按照相关文档1的上半部分下载ABP

2.需要额外安装的NuGet包

3.需要自己建立的文件(Red)&需要更改的原始文件(Green)

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

一 :MyProjectDbContext.cs:

using Microsoft.EntityFrameworkCore;
using Abp.Zero.EntityFrameworkCore;
using MyProject.Authorization.Roles;
using MyProject.Authorization.Users;
using MyProject.MultiTenancy;
using MyProject.MyTest;

namespace MyProject.EntityFrameworkCore
{
    public class MyProjectDbContext : AbpZeroDbContext<Tenant, Role, User, MyProjectDbContext>
    {
        /* Define a DbSet for each entity of the application */
        public DbSet<Simple> Simples { get; set; }
        public virtual DbSet<Task> Tasks { get; set; }
        public virtual DbSet<Person> People { get; set; }

        public MyProjectDbContext(DbContextOptions<MyProjectDbContext> options)
            : base(options)
        {
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Simple>(p =>
            {
                p.ToTable("Simples");
                p.Property(x => x.Name).IsRequired(true).HasMaxLength(20);
                p.Property(x => x.Details).HasMaxLength(100);
            });
        }
    }
}
    这里我并没有覆写OnModelCreating方法中显式配置Task和Person实体的映射和属性。但是在Swagger上进行的Task,Person字段操作都成功地作用于本地的MySql库中了。这个问题在[6.DeBug与问题解答](#6)中提到。

二 :MyProjectDbContextConfigurer.cs:

由于使用的是MySql而不是默认的SqlServer,根据相关文档2进行MySql内容的替换。

using System.Data.Common;
using Microsoft.EntityFrameworkCore;
namespace MyProject.EntityFrameworkCore
{
    public static class MyProjectDbContextConfigurer
    {
        public static void Configure(DbContextOptionsBuilder<MyProjectDbContext> builder, string connectionString)
        {
            var serverVersion = ServerVersion.AutoDetect(connectionString);
            builder.UseMySql(connectionString, serverVersion);
        }

        public static void Configure(DbContextOptionsBuilder<MyProjectDbContext> builder, DbConnection connection)
        {
            var serverVersion = ServerVersion.AutoDetect(connection.ConnectionString);
            builder.UseMySql(connection, serverVersion);
        }
    }
}

这里ConnectionString引用的是

因此需要进行更改:

3.2:需要自己建立的文件(Red)

一 :Task.cs:这是一个属性,定义了<任务>的各个属性字段

using Abp.Domain.Entities;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;

namespace MyProject.MyTest
{
    public class Task : Entity<long>
    {
        [ForeignKey("AssignedPersonId")]
        public virtual Person AssignedPerson { get; set; }
        public virtual int? AssignedPersonId { get; set; }
        public virtual string Description { get; set; }
        public virtual DateTime CreationTime { get; set; }
        public virtual TaskState State { get; set; }
        public Task()
        {
            CreationTime = DateTime.Now;
            State = TaskState.Active;
        }
    }
    public enum TaskState
    {
        Active,
        Inactive,
    }
}

二 :ITaskRepository.cs,TaskRepository.cs:Task的仓储接口以及实现例。用于被TaskAppService调用以进行增删改查或自定义操作

由于ITaskRepository继承了原生的IRepository因此具有基类中早已定义好的各类增删改查函数。

using Abp.Domain.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyProject.MyTest.Method
{

    /*通过仓储模式,可以更好把业务代码与数据库操作代码更好的分离,可以针对不同的数据库有不同的实现类,而业务代码不需要修改。
定义仓储接口的代码写到Core项目中,因为仓储接口是领域层的一部分.
我们先定义Task的仓储接口*/
    public interface ITaskRepository : IRepository<Task, long>
    {
        List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state);
    }
    //接口待实现
}

由于TaskRepository继承了原生的MyProjectRepositoryBase因此具有基类中早已定义好的GetAll()等query处理函数

using MyProject.EntityFrameworkCore.Repositories;
using MyProject.MyTest.Method;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Abp.EntityFrameworkCore;
using MyProject.EntityFrameworkCore;

namespace MyProject.MyTest
{
    public class TaskRepository : MyProjectRepositoryBase<Task, long>, ITaskRepository
    {
        public TaskRepository(IDbContextProvider<MyProjectDbContext> dbContextProvider)
        : base(dbContextProvider)
        {
        }
        public List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state)
        {
            //在仓储方法中,不用处理数据库连接、DbContext和数据事务,ABP框架会自动处理。
            var query = GetAll(); //GetAll() 返回一个 IQueryable<T>接口类型
            //添加一些Where条件
            if (assignedPersonId.HasValue)
            {
                query = query.Where(task => task.AssignedPerson.Id == assignedPersonId.Value);
            }
            if (state.HasValue)
            {
                query = query.Where(task => task.State == state);
            }
            return query
                .OrderByDescending(task => task.CreationTime)
                .Include(task => task.AssignedPerson)
                .ToList();
        }
    }
}

三 :ITaskAppService.cs,TaskAppService.cs:前者接口负责定义Task的增(删)改查,后者负责具体实现

using Abp.Application.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MyProject.MyTest.Dto;
namespace MyProject.MyTest
{
    public interface ITaskAppService : IApplicationService
    {
        GetTasksOutput GetTasks(GetTasksInput input);
        void UpdateTask(UpdateTaskInput input);
        void CreateTask(CreateTaskInput input);
    }
}
using Abp.Application.Services;
using Abp.Domain.Repositories;
using AutoMapper;
using Microsoft.Extensions.Logging;
using MyProject.MyTest.Method;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MyProject.MyTest.Dto;

namespace MyProject.MyTest
{
    public class TaskAppService : ApplicationService, ITaskAppService
    {
        private readonly ITaskRepository _taskRepository;
        private readonly IRepository<Person> _personRepository;
        /// <summary>
        /// 构造函数自动注入我们所需要的类或接口
        /// </summary>
        public TaskAppService(ITaskRepository taskRepository, IRepository<Person> personRepository)
        {
            _taskRepository = taskRepository;
            _personRepository = personRepository;
        }
        public GetTasksOutput GetTasks(GetTasksInput input)
        {
            //调用Task仓储的特定方法GetAllWithPeople
            var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State);

            //用AutoMapper自动将List<Task>转换成List<TaskDto>
            return new GetTasksOutput
            {
                Tasks = ObjectMapper.Map<List<TaskDto>>(tasks)
                //Tasks = Mapper.Map<List<TaskDto>>(tasks)
            };
        }
        public void UpdateTask(UpdateTaskInput input)
        {
            //可以直接Logger,它在ApplicationService基类中定义的
            Logger.Info("Updating a task for input: " + input);
            //通过仓储基类的通用方法Get,获取指定Id的Task实体对象
            var task = _taskRepository.Get(input.TaskId);
            //修改task实体的属性值
            if (input.State.HasValue)
            {
                task.State = input.State.Value;
            }
            if (input.AssignedPersonId.HasValue)
            {
                task.AssignedPerson = _personRepository.Load(input.AssignedPersonId.Value);
            }
            //我们都不需要调用Update方法
            //因为应用服务层的方法默认开启了工作单元模式(Unit of Work)
            //ABP框架会工作单元完成时自动保存对实体的所有更改,除非有异常抛出。有异常时会自动回滚,因为工作单元默认开启数据库事务。
        }
        public void CreateTask(CreateTaskInput input)
        {
            // TODO:
            var persons_query_count = _personRepository.GetAll()
                                         .Where(x => x.Id == input.AssignedPersonId)
                                         .Count();
            if (persons_query_count > 0)
            {
                //存在该委托人,可以创建任务
                Logger.Info("Creating a task for input: " + input);
            }
            else
            {
                //不存在该人,不能创建对应任务
                throw new InvalidOperationException();
            }
            //通过输入参数,创建一个新的Task实体
            var task = new Task { Description = input.Description };

            if (input.AssignedPersonId.HasValue)
            {
                task.AssignedPersonId = input.AssignedPersonId.Value;
            }
            //调用仓储基类的Insert方法把实体保存到数据库中
            _taskRepository.Insert(task);
        }
    }
}

  在TaskAppService这个类中每定义一个函数(哪怕空函数),Swagger界面就会显示一个对应的接口。(可能是基类ApplicationService的带来的方法)
  ITaskRepository taskRepository``IRepository<Person> personRepository两个成员用于使用原生或者自制的增删改查操作。

四 :UpdateTasksInput.cs,CreateTasksInput.cs,GetTasksInput,GetTasksOutput:这四个是对接Swagger的输入/输出字段

using Abp.Runtime.Validation;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyProject.MyTest.Dto
{
    public class UpdateTaskInput //, ICustomValidate
    {
        [Range(1, long.MaxValue)]
        public long TaskId { get; set; }
        public int? AssignedPersonId { get; set; }
        public TaskState? State { get; set; }
        //public void AddValidationErrors(CustomValidationContext context)
        //{
        //    if (AssignedPersonId == null && State == null)
        //    {
        //        context.Results.Add(new ValidationResult("AssignedPersonId和State不能同时为空!", new[] { "AssignedPersonId", "State" }));
        //    }
        //    throw new NotImplementedException();
        //}
    }
}
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyProject.MyTest.Dto
{
    public class CreateTaskInput : IInputDto
    {
        public int? AssignedPersonId { get; set; }
        [Required]//说明Description为必填项
        public string Description { get; set; }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyProject.MyTest.Dto
{
    public class GetTasksInput
    {
            public int? AssignedPersonId { get; set; }
            public TaskState? State { get; set; }

    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyProject.MyTest.Dto
{
    public class GetTasksOutput
    {
        public List<TaskDto> Tasks { get; set; }
        // 其他属性
    }
}

五:TaskDto.cs,TaskMapProfile.cs:Dto的实现用于被后者文件调用,随后即可使用ObjectMapper.Map实现"AutoMapper自动将List转换成List"

using Abp.Application.Services.Dto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MyProject.MyTest;

namespace MyProject.MyTest.Dto
{
    public class TaskDto : EntityDto<int>
    {
        public virtual Person AssignedPerson { get; set; }
        public int AssignedPersonId { get; set; }
        public string Description { get; set; }
        public virtual DateTime CreationTime { get; set; }
        public TaskState State { get; set; }
        // 其他属性
    }
}
using AutoMapper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyProject.MyTest.Dto
{
    public class TaskMapProfil : Profile
    {
        public TaskMapProfil()
        {
            CreateMap<Task, TaskDto>();
            CreateMap<TaskDto, Task>();
        }
    }
}

六:其余诸如 Simple,Person类也是同样类似Task的同上方法。

这里需要注意的是外键约束问题(主次表连接键)

5.数据库的连接&Swagger UI

  键入Add-Migration DIY_Name生成数据迁移文件(注意默认项目选择MyProject.EntityFrameworkCore,因为MyProjectDbContext在此类库下)(参考相关文档2)
(若所生成的迁移文件为空,可能是已经生成过了,此时反复进行Add,Remove操作,或者删掉原有的Migration文件夹内的文件即可)。
  键入Update-Database更新数据库数据(若第一次可能会自动建库建表)

  最后运行项目即可打开Swagger网页(浏览器可能会认为是不安全网站.在键盘下按顺序敲下"Thisisunsafe"即可打开)

6.DeBug与问题解答

Q1:这里的Create数据可以有两种方法(以Student类):
(需要提前在Student的定义中写构造函数)

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);
       }

或者是上面代码中的不写构造函数,用一种特殊的语法直接赋值

public void CreateTask(CreateTaskInput input)
        {
            // TODO:
            var persons_query_count = _personRepository.GetAll()
                                         .Where(x => x.Id == input.AssignedPersonId)
                                         .Count();
            if (persons_query_count > 0)
            {
                //存在该委托人,可以创建任务
                Logger.Info("Creating a task for input: " + input);
            }
            else
            {
                //不存在该人,不能创建对应任务
                throw new InvalidOperationException();
            }
            //通过输入参数,创建一个新的Task实体
            var task = new Task { Description = input.Description };

            if (input.AssignedPersonId.HasValue)
            {
                task.AssignedPersonId = input.AssignedPersonId.Value;
                
            }
            //调用仓储基类的Insert方法把实体保存到数据库中
            _taskRepository.Insert(task);
        }

(不知道是C#的一种方法还是基类Entity的方法,待研究)


Q1:若SWAGGER出错,但是VS内没报错。
A1:
  可尝试去E:\Visual studio\6.5.0\aspnet-core\src\MyProject.Web.Host\App_Data\Logs 日志内查找错误(ctrl+F找ERROR关键词)
  比如我就出错了,在日志上找到了
MySqlConnector.MySqlException (0x80004005): Cannot add or update a child row: a foreign key constraint fails (`myprojectdb`.`tasks`, CONSTRAINT `FK_Tasks_People_AssignedPersonId` FOREIGN KEY (`AssignedPersonId`) REFERENCES `people` (`Id`) ON DELETE RESTRICT)
  结果是外键约束产生的附带问题


Q2:我想知道为什么我没有modelBuilder.Entity用于Task和Person,但我的数据库仍然能接受到数据,即进行正常的数据库操作
A2:
  在这个示例中,尽管你没有在OnModelCreating方法中显式配置Task和Person实体的映射和属性,但EF Core会根据约定进行默认的配置。根据约定,EF Core会将实体类名称作为表名,并使用实体类的属性作为表的列。
  因此,即使你没有提供显式的配置,EF Core仍然可以根据约定将Task和Person实体映射到数据库中的对应表。这也是为什么你能够正常地进行数据库操作。
  但是,如果你希望对这些实体类的映射和属性进行更详细的配置,你可以在OnModelCreating方法中使用modelBuilder.Entity<T>语法进行显式配置,就像你已经对Simple实体进行的配置一样。这样做可以提供更多的灵活性并允许你自定义表名、列名、数据类型,以及其他高级配置选项。


Q3:使用以下办法将仅含一条数据的List<Person>转换为Person类型
A3:

List<Person> personsList = GetPersons();

if (personsList.Count > 0)
{
    // 获取列表中的第一个元素,并将其转换为Person类型
    Person person = personsList[0];

    Console.WriteLine(person.Name); // 输出:John Doe
}
else
{
    Console.WriteLine("未找到匹配的Person对象");
}

Q4:什么叫异步方法
A4:
  而异步方法是一种在执行过程中可以让出线程并异步等待某个操作完成的方法。让出线程:将耗时的任务放在最后执行的意思是为了先执行那些不耗时的任务,以提高整体的执行效率和响应性。通过先执行不耗时的任务,可以让程序快速响应用户的操作或请求,并在后台执行耗时的任务,避免阻塞主线程或其他任务的执行。
  这种方式也可以更好地利用计算资源,因为在执行耗时任务的同时,其他的任务可以继续执行,从而提高系统的并发性和效率。总的执行时长可能不会改变,但整体的用户体验会更好。


Q5:我想知道
接口A.cs在B.cs中实现后,若在C.cs中创建了A的实例,能否调用在B.cs中实现的A内函

A5:
  是的,如果接口 A 在类 B 中实现,而在类 C 中创建了 A 的实例,那么你可以通过该实例调用在类 B 中实现的 A 的内部函数。
  这也是接口文件的实现方式

// A.cs
public interface A
{
    void MyMethod();
}
// B.cs
public class B : A
{
    public void MyMethod()
    {
        Console.WriteLine("Hello from B");
    }
}
// C.cs
public class C
{
    public void DoSomething()
    {
        A instance = new B();
        instance.MyMethod(); // 调用在 B 类中实现的 MyMethod 方法
    }
}

标签:MyProject,创建,System,ABP,input,AssignedPersonId,using,NET,public
From: https://www.cnblogs.com/DoubiCan/p/17617310.html

相关文章

  • 用户空间协议栈设计和netmap综合指南
    本文分享自华为云社区《用户空间协议栈设计和netmap综合指南,将网络效率提升到新高度》,作者:LionLong。协议概念1.1、七层网络模型和五层网络模型应用层: 最接近用户的一层,为用户程序提供网络服务。主要协议有HTTP、FTP、TFTP、SMTP、DNS、POP3、DHCP等。表示层: 数据的表示......
  • .NET周刊【8月第1期 2023-08-06】
    国内文章NativeBuferring,一种零分配的数据类型(上篇)https://www.cnblogs.com/artech/p/17586781.html之前一个项目涉及到针对海量(千万级)实时变化数据的计算,由于对性能要求非常高,我们不得不将参与计算的数据存放到内存中,并通过检测数据存储的变化实时更新内存的数据。存量的数据......
  • 观察混合云环境中 Kubernetes 可观测性的 6 种有效策略
    在混合云环境中观察Kubernetes需要理解分布式系统的行为和性能。我下面这篇文章中的六个策略可以帮助实现这一目标。2023年,原生云应用和平台迅速增长。组织不断努力最大化其应用程序的潜力,确保无缝的用户体验,并推动业务增长。混合云环境的兴起和容器化技术(如Kubernetes)的采用,彻底改......
  • abp-vnext-pro 实战(六,vue 前端状态pinia)
    在login的时候把所有写入全局store,console.log('----------------从数据库获取字典--------------------');constappStore=useAppStore();constdataDictionaryServiceProxy=newDataDictionaryServiceProxy();constdetailInput=new......
  • .NET和Azure:构建云原生应用程序
    在现代软件开发中,云原生应用程序已经成为一种越来越受欢迎的架构风格。它们可以在云环境中实现高度可伸缩性、弹性和灵活性,同时充分利用云服务的优势。在本篇博客中,我们将探讨如何使用.NET技术和Azure云平台来构建云原生应用程序。我们将以一个简单的示例应用程序为例,演示如何将.NE......
  • 开发基于RESTful API的ASP.NET Web应用程序
    当开发基于RESTfulAPI的ASP.NETWeb应用程序时,您将构建一个可以通过HTTP请求进行交互的应用程序,它可以提供数据和功能给客户端应用程序或其他服务。在本博客中,我将为您提供一个基本的教程,演示如何创建一个简单的ASP.NETWeb应用程序,并实现基于RESTfulAPI的功能。步骤1:设置开发环......
  • DEVICENET转MODBUS-TCP协议网关ethercat和profinet的区别
     DEVICENET转MODBUS-TCP协议网关JM-DNT-TCP1.产品功能JM–DNT-TCP是自主研发的一款DEVICENET从站功能的通讯网关。该产品主要功能是将DEVICENET总线和MODBUS-TCP网络连接起来。本网关连接到DEVICENET总线中做为从站使用,连接到MODBUS-TCP总线中做为主站或从站使用。2.拓扑......
  • 残差网络ResNet(超详细代码解析) :你必须要知道backbone模块成员之一
      本文主要贡献代码模块(文末),在本文中对resnet进行了复现,是一份原始版本模块,里面集成了权重文件pth的载入模块(如函数:init_weights(self,pretrained=None)),layers的冻结模块(如函数:_freeze_stages(self)),更是将其改写成可读性高的代码,若你需要执行该模块,可直接将其代码模块粘......
  • .Net Core gRpc调用
    目录简介创建gRPC创建服务端创建控制台测试创建自定义服务服务器流式处理方法custom.protoCustomGreeterService.csgRpcRequest.cs客户端流式处理方法custom.protoCustomGreeterService.csgRpcRequest.cs双向流式处理方法custom.protoCustomGreeterService.csgRpcRequest.cs.Net......
  • 06 llvm IR测试创建struct和Array
    见下文代码,方式一样的#include<vector>#include"llvm/IR/Module.h"#include"llvm/IR/LLVMContext.h"#include"llvm/Support/raw_ostream.h"#include"llvm/IR/Function.h"#include"llvm/IR/Verifier.h"#include&......