zoukankan      html  css  js  c++  java
  • ABP教程(三)- 开始一个简单的任务管理系统 – 后端编码

    上一篇 我们介绍了什么是ABP,这一篇我们通过原作者的”简单任务系统”例子,演示如何运用ABP开发项目

    创建实体

    一般来说任务是需要分配给人来做的,所以我们创建两个实体模型类:Task和Persion

    
    using Abp.Domain.Entities;
    using Abp.Domain.Entities.Auditing;
    using System.ComponentModel.DataAnnotations.Schema;
    using System;
    
    namespace SimpleTaskSystem
    {
        public class Task : Entity, IHasCreationTime
        {
            [ForeignKey("AssignedPersonId")]
            public Person AssignedPerson { get; set; }
            public long? AssignedPersonId { get; set; }
            public string Description { get; set; }
            public TaskState State { get; set; }
            public DateTime CreationTime { get; set; }
    
            public Task()
            {
                State = TaskState.Active;
            }
        }
    } 
    
    
    using System;
    using Abp.Domain.Entities;
    using Abp.Domain.Entities.Auditing;
    
    namespace SimpleTaskSystem
    {
        public class Person : Entity, IHasCreationTime
        {
            public string Name { get; set; }
            public DateTime CreationTime { get; set; }
        }
    }
    

    创建DbContext

    使用EntityFramework需要先定义DbContext类,ABP的模板已经创建了DbContext文件,我们只需要把Task和Person类添加到IDbSet

    
    using Abp.EntityFramework;
    using System.Data.Entity;
    
    namespace SimpleTaskSystem.EntityFramework
    {
        public class SimpleTaskSystemDbContext : AbpDbContext
        {
            //TODO: Define an IDbSet for each Entity...
    
            //Example:
            //public virtual IDbSet Users { get; set; }
            public virtual IDbSet Tasks { get; set; }
            public virtual IDbSet People { get; set; }
    
            /* NOTE: 
             *   Setting "Default" to base class helps us when working migration commands on Package Manager Console.
             *   But it may cause problems when working Migrate.exe of EF. If you will apply migrations on command line, do not
             *   pass connection string name to base classes. ABP works either way.
             */
            public SimpleTaskSystemDbContext()
                : base("Default")
            {
    
            }
    
            /* NOTE:
             *   This constructor is used by ABP to pass connection string defined in SimpleTaskSystemDataModule.PreInitialize.
             *   Notice that, actually you will not directly create an instance of SimpleTaskSystemDbContext since ABP automatically handles it.
             */
            public SimpleTaskSystemDbContext(string nameOrConnectionString)
                : base(nameOrConnectionString)
            {
    
            }
        }
    }
    

    通过Database Migrations创建数据库

    我们使用EntityFramework的Code First模式创建数据库架构。ABP模板生成的项目已经默认开启了数据迁移功能,我们添加一些默认数据进去。

    
    using System.Data.Entity.Migrations;
    
    namespace SimpleTaskSystem.Migrations
    {
        internal sealed class Configuration : DbMigrationsConfiguration
        {
            public Configuration()
            {
                AutomaticMigrationsEnabled = false;
                ContextKey = "SimpleTaskSystem";
            }
    
            protected override void Seed(SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext context)
            {
                // This method will be called every time after migrating to the latest version.
                // You can add any seed data here...
                context.People.AddOrUpdate(
                    p => p.Name,
                    new Person { Name = "张三" },
                    new Person { Name = "王二" },
                    new Person { Name = "李四" },
                    new Person { Name = "老王" }
                );
            }
        }
    }
    

    然后打开 程序包管理器控制台 ,选择默认项目并执行命令”Add-Migration InitialCreate”,此时会在Migrations目录下生成一个以 当前时间_InitialCreate.cs 的文件。

    然后继续在 程序包管理器控制台 执行 Update-Database,等执行完毕则会在数据库自动创建相应的数据表

    定义仓储接口

    通过仓储模式,可以更好把业务代码与数据库操作代码更好的分离。我们把仓储的代码写到 Core 项目中

    
    using Abp.Domain.Repositories;
    using System.Collections.Generic;
    
    namespace SimpleTaskSystem.Repositorys
    {
        public interface ITaskRepository : IRepository<Task, long>
        {
            List GetAllWithPeople(long? assignedPersonId, TaskState? state);
        }
    }
    

    我们可以为Persion类定义一个仓储,也可以不定义,这里我选择定义,虽然ABP默认提供的仓储在本例中已经够用了,但考虑到以后扩展方便,我们还是也给它定义一个。

    
    using Abp.Domain.Repositories;
    
    namespace SimpleTaskSystem.Repositorys
    {
        public interface IPersionRepository : IRepository<Person, long>
        {
    
        }
    }
    

    这样后期我们要加一个自定义方法直接加在这里面就行了,不用改动太多。这里也介绍一下ABP自带的仓储方法,我们可以直接使用。

    实现仓储类

    因为本例用的是EntityFramework,所以我们将在EntityFramework项目中实现上面定义的ITaskRepository仓储接口。如果后期想换个ORM框架,比如NHibernate,我们就只要更换仓储实现的这个项目即可。

    TaskRepository.cs

    
    using System.Collections.Generic;
    using Abp.EntityFramework;
    using SimpleTaskSystem.EntityFramework.Repositories;
    using SimpleTaskSystem.Repositorys;
    using System.Linq;
    using System.Data.Entity;
    
    namespace SimpleTaskSystem.EntityFramework.Repositorys
    {
        public class TaskRepository : SimpleTaskSystemRepositoryBase<Task, long>, ITaskRepository
        {
            public TaskRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider)
            {
            }
    
            public List GetAllWithPeople(long? assignedPersonId, TaskState? state)
            {
                //在仓储方法中,不用处理数据库连接、DbContext和数据事务,ABP框架会自动处理。
                var query = GetAll(); //返回一个 IQueryable接口类型
    
                //添加一些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();
            }
        }
    }
    

    PersionRepository.cs

    
    using SimpleTaskSystem.Repositorys;
    using Abp.EntityFramework;
    
    namespace SimpleTaskSystem.EntityFramework.Repositories
    {
        public class PersionRepository : SimpleTaskSystemRepositoryBase<Person, long>, IPersionRepository
        {
            public PersionRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider)
            {
    
            }
        }
    }
    

    创建服务(Services)

    首先在Application项目中定义Task的应用服务层的接口,各dto的类请下载源码查看。

    ITaskAppService.cs

    
    using Abp.Application.Services;
    
    namespace SimpleTaskSystem.Services
    {
        public interface ITaskAppService : IApplicationService
        {
            GetTasksOutput GetTasks(GetTasksInput input);
            void UpdateTask(UpdateTaskInput input);
            void CreateTask(CreateTaskInput input);
        }
    }
    

    然后,我们写TaskAppService类来实现ITaskAppService接口

    TaskAppService.cs

    
    using Abp.Application.Services;
    using AutoMapper;
    using SimpleTaskSystem.Repositorys;
    using System.Collections.Generic;
    
    namespace SimpleTaskSystem.Services
    {
        public class TaskAppService : ApplicationService, ITaskAppService
        {
            private readonly ITaskRepository _taskRepository;
            private readonly IPersionRepository _personRepository;
    
            /// 
            /// 构造函数自动注入我们所需要的类或接口
            /// 
            public TaskAppService(ITaskRepository taskRepository, IPersionRepository personRepository)
            {
                _taskRepository = taskRepository;
                _personRepository = personRepository;
            }
    
            public void CreateTask(CreateTaskInput input)
            {
                Logger.Info("Creating a task for input: " + input);
    
                //通过输入参数,创建一个新的Task实体
                var task = new Task { Description = input.Description };
    
                if (input.AssignedPersonId.HasValue)
                {
                    task.AssignedPersonId = input.AssignedPersonId.Value;
                }
    
                //调用仓储基类的Insert方法把实体保存到数据库中
                _taskRepository.Insert(task);
            }
    
            public GetTasksOutput GetTasks(GetTasksInput input)
            {
                //调用Task仓储的特定方法GetAllWithPeople
                var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State);
    
                //用AutoMapper自动将List转换成List
                return new GetTasksOutput
                {
                    Tasks = Mapper.Map<list>(tasks)
                };
            }
    
            public void UpdateTask(UpdateTaskInput input)
            {
                //可以直接Logger,它在ApplicationService基类中定义的
                Logger.Info("Updating a task for input: " + input);
    
                //通过仓储基类的通用方法Get,获取指定Id的Task实体对象
                var task = _taskRepository.Get(input.Id);
    
                //修改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框架会工作单元完成时自动保存对实体的所有更改,除非有异常抛出。有异常时会自动回滚,因为工作单元默认开启数据库事务。
            }
        }
    }
    

    数据验证

    如果应用服务(Application Service)方法的参数对象实现了IInputDto或IValidate接口,ABP会自动进行参数有效性验证。

    CreateTaskInput.cs

    
    using Abp.Application.Services.Dto;
    using System.ComponentModel.DataAnnotations;
    
    namespace SimpleTaskSystem.Services
    {
        public class CreateTaskInput : IInputDto
        {
            [Required]
            public string Description { get; set; }
    
            public long? AssignedPersonId { get; set; }
        }
    }
    

    如果你想使用自定义验证,你可以实现ICustomValidate 接口

    UpdateTaskInput.cs

    
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using Abp.Application.Services.Dto;
    using Abp.Runtime.Validation;
    
    namespace SimpleTaskSystem.Services
    {
        public class UpdateTaskInput : IInputDto, ICustomValidate
        {
            public int Id { get; set; }
    
            public string Description { get; set; }
    
            public long? AssignedPersonId { get; set; }
    
            public TaskState? State { get; set; }
    
            public void AddValidationErrors(List results)
            {
                if (AssignedPersonId == null && State == null)
                {
                    results.Add(new ValidationResult("AssignedPersonId和State不能同时为空!", new[] { "AssignedPersonId", "State" }));
                }
            }
        }
    }
    

    创建Web Api服务

    ABP默认已经开户了动态API,我们不需要任何设置即可非常轻松地把Application Service的public方法发布成Web Api接口,可以供客户端通过ajax调用。

    下一篇我们将在此基础上构建前台页面,实现在浏览器中对任务进行简单的增删改查,静请期待……

    本节源码链接: http://pan.baidu.com/s/1jIvZPSM 密码: njk5

  • 相关阅读:
    Hadoop编码解码【压缩解压缩】机制详解(1)
    Hadoop编码解码【压缩解压缩】机制详解(1)
    Hadoop IO 特性详解(2)【文件校验】
    Hadoop IO 特性详解(2)
    spring mvc default-servlet mvc:resources mvc:default-servlet-handler区别
    spring mvc default-servlet mvc:resources mvc:default-servlet-handler区别
    Hadoop IO 特性详解(1)
    Hadoop IO 特性详解(1)【数据完整性】
    189. Rotate Array
    435. Non-overlapping Intervals
  • 原文地址:https://www.cnblogs.com/webplus/p/5605975.html
Copyright © 2011-2022 走看看