namespace Zto.BookStore.Books
{
public static class BookConsts
{
public const int MaxNameLength = 256; //名字最大长度
}
}
本地化
官方文档
创建本地化资源
开始的UI开发之前,我们首先要准备本地化的文本(这是通常在开发应用程序时需要做的).
本地化资源用于将相关的本地化字符串组合在一起,并将它们与应用程序的其他本地化字符串分开,
通常一个模块会定义自己的本地化资源. 本地化资源就是一个普通的类. 例如:
在文件夹Localization下,新建BookStoreResource.cs类
[LocalizationResourceName("BookStore")]
public class BookStoreResource
{
}
[LocalizationResourceName("BookStore")]标记资源名
在文件夹Localization/BookStore,添加两个语言资源json文件,
en.json
{
"Culture": "en",
"Texts": {
"Menu:Home": "Home",
"Welcome": "Welcome",
"LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io.",
"Menu:BookStore": "Book Store",
"Menu:Books": "Books",
"Actions": "Actions",
"Edit": "Edit",
"PublishDate": "Publish date",
"NewBook": "New book",
"Name": "Name",
"Type": "Type",
"Price": "Price",
"CreationTime": "Creation time",
"AreYouSureToDelete": "Are you sure you want to delete this item?",
"Enum:BookType:0": "Undefined",
"Enum:BookType:1": "Adventure",
"Enum:BookType:2": "Biography",
"Enum:BookType:3": "Dystopia",
"Enum:BookType:4": "Fantastic",
"Enum:BookType:5": "Horror",
"Enum:BookType:6": "Science",
"Enum:BookType:7": "Science fiction",
"Enum:BookType:8": "Poetry",
"BookDeletionConfirmationMessage": "Are you sure to delete the book '{0}'?",
"SuccessfullyDeleted": "Successfully deleted!",
"Permission:BookStore": "Book Store",
"Permission:Books": "Book Management",
"Permission:Books.Create": "Creating new books",
"Permission:Books.Edit": "Editing the books",
"Permission:Books.Delete": "Deleting the books",
"BookStore:00001": "There is already an author with the same name: {name}",
"Permission:Authors": "Author Management",
"Permission:Authors.Create": "Creating new authors",
"Permission:Authors.Edit": "Editing the authors",
"Permission:Authors.Delete": "Deleting the authors",
"Menu:Authors": "Authors",
"Authors": "Authors",
"AuthorDeletionConfirmationMessage": "Are you sure to delete the author '{0}'?",
"BirthDate": "Birth date",
"NewAuthor": "New author"
}
}
using Volo.Abp.Modularity;
namespace Zto.BookStore
{
[DependsOn(typeof(BookStoreDomainSharedModule))]
public class BookStoreDomainModule : AbpModule
{
}
}
创建Book领域模型
创建文件夹Books,在该文件夹下新建Book.cs
using Volo.Abp.Domain.Entities.Auditing;
using System;
namespace Zto.BookStore.Books
{
public class Book : AuditedAggregateRoot
{
public Guid AuthorId { get; set; }
public String Name { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
}
}
项目常量值类BookStoreConsts
在根目录下创建BookStoreConsts.cs,用于保存项目中常量数据值
namespace Zto.BookStore
{
public static class BookStoreConsts
{
public const string DbTablePrefix = "Bks"; //常量值:表前缀
public const string DbSchema = null; //常量值:表的架构
}
}
using Volo.Abp.Modularity;
namespace Zto.BookStore.EntityFrameworkCore
{
[DependsOn(typeof(BookStoreDomainModule))]
public class BookStoreEntityFrameworkCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext(options =>
{
/* Remove "includeAllEntities: true" to create
* default repositories only for aggregate roots */
options.AddDefaultRepositories(includeAllEntities: true);
});
Configure(options =>
{
/* The main point to change your DBMS.
* See also BookStoreMigrationsDbContextFactory for EF Core tooling. */
options.UseSqlServer();
});
}
}
}
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Zto.BookStore.Books;
namespace Zto.BookStore.EntityFrameworkCore
{
[ConnectionStringName("BookStoreConnString")]
public class BookStoreDbContext : AbpDbContext
{
public DbSet Books { get; set; }
public BookStoreDbContext(DbContextOptions options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Configure the shared tables (with included modules) here */
// 配置从其它modules引入的模型
/* Configure your own tables/entities inside the ConfigureBookStore method */
// 配置本项目自己的表和实体模型
builder.ConfigureBookStore();
}
}
}
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Modeling;
using Zto.BookStore.Books;
namespace Zto.BookStore.EntityFrameworkCore
{
public static class BookStoreDbContextModelCreatingExtensions
{
public static void ConfigureBookStore(this ModelBuilder builder)
{
Check.NotNull(builder, nameof(builder));
/* Configure your own tables/entities inside here */
builder.Entity(e =>
{
e.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema);
e.ConfigureByConvention(); //auto configure for the base class props ,优雅的配置和映射继承的属性,应始终对你所有的实体使用它.
e.Property(p => p.Name).HasMaxLength(BookConsts.MaxNameLength);
});
}
}
}
Unable to create an object of type 'BookStoreDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace Zto.BookStore.EntityFrameworkCore
{
///
/// This DbContext is only used for database migrations.
/// It is not used on runtime. See BookStoreDbContext for the runtime DbContext.
/// It is a unified model that includes configuration for
/// all used modules and your application.
///
/// 这个DbContext只用于数据库迁移。
/// 它不在运行时使用。有关运行时DbContext,请参阅BookStoreDbContext。
/// 它是一个统一配置所有使用的模块和您的应用程序的模型
///
[ConnectionStringName("BookStoreConnString")]
public class BookStoreMigrationsDbContext : AbpDbContext
{
public BookStoreMigrationsDbContext(DbContextOptions options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
/* Configure the shared tables (with included modules) here */
// 配置从其它modules引入的模型
/* Configure your own tables/entities inside the ConfigureBookStore method */
// 配置本项目自己的表和实体模型
builder.ConfigureBookStore();
}
}
}
Unable to create an object of type 'BookStoreDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
PM> add-migration initDb
Build started...
Build succeeded.
To undo this action, use Remove-Migration.
把挂起的migration更新到数据库
update-database
这时,命令行提示:
PM> update-database
Build started...
Build succeeded.
Security Warning: The negotiated TLS 1.0 is an insecure protocol and is supported for backward compatibility only. The recommended protocol version is TLS 1.2 and later.
Security Warning: The negotiated TLS 1.0 is an insecure protocol and is supported for backward compatibility only. The recommended protocol version is TLS 1.2 and later.
Security Warning: The negotiated TLS 1.0 is an insecure protocol and is supported for backward compatibility only. The recommended protocol version is TLS 1.2 and later.
Applying migration '20201207183001_initDb'.
Done.
PM>
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Zto.BookStore.Data;
namespace Zto.BookStore.EntityFrameworkCore
{
public class EntityFrameworkCoreBookStoreDbSchemaMigrator : IBookStoreDbSchemaMigrator, ITransientDependency
{
private readonly IServiceProvider _serviceProvider;
public EntityFrameworkCoreBookStoreDbSchemaMigrator(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task MigrationAsync()
{
/*
* 我们有意从IServiceProvider解析BookStoreMigrationsDbContext(而不是直接注入它),
* 是为了能正确获取当前的范围、当前租户的连接字符串
*/
var dbContext = _serviceProvider.GetRequiredService();
var database = dbContext.Database;
//var connString = database.GetConnectionString();
/*
* Asynchronously applies any pending migrations for the context to the database.
* Will create the database if it does not already exist.
*/
await database.MigrateAsync();
}
}
}
using Volo.Abp.Autofac;
using Zto.BookStore.EntityFrameworkCore;
using Volo.Abp.Modularity;
namespace Zto.BookStore.DbMigrator
{
[DependsOn(
typeof(AbpAutofacModule),
typeof(BookStoreEntityFrameworkCoreDbMigrationsModule)
)]
public class BookStoreDbMigratorModule : AbpModule
{
}
}
namespace Microsoft.Extensions.Hosting
{
//
// 摘要:
// Defines methods for objects that are managed by the host.
public interface IHostedService
{
Task StartAsync(CancellationToken cancellationToken);
Task StopAsync(CancellationToken cancellationToken);
}
}
[13:54:12 INF] Started database migrations...
[13:54:12 INF] Migrating schema for host database...
Security Warning: The negotiated TLS 1.0 is an insecure protocol and is supported for backward compatibility only. The recommended protocol version is TLS 1.2 and later.
[13:54:14 INF] Successfully completed host database migrations.
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Zto.BookStore.Books;
namespace Zto.BookStore
{
public class BookStoreDataSeederContributor
: IDataSeedContributor, ITransientDependency
{
private readonly IRepository _bookRepository;
public BookStoreDataSeederContributor(IRepository bookRepository)
{
_bookRepository = bookRepository;
}
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepository.GetCountAsync() <= 0)
{
await _bookRepository.InsertAsync(
new Book
{
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
},
autoSave: true
);
await _bookRepository.InsertAsync(
new Book
{
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
},
autoSave: true
);
}
}
}
}
using System;
using System.Threading.Tasks;
namespace Volo.Abp.Data
{
public static class DataSeederExtensions
{
public static Task SeedAsync(this IDataSeeder seeder, Guid? tenantId = null)
{
return seeder.SeedAsync(new DataSeedContext(tenantId));
}
}
}
DataSeedContext
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.Data
{
public class DataSeedContext
{
public Guid? TenantId { get; set; }
///
/// Gets/sets a key-value on the .
///
/// Name of the property
///
/// Returns the value in the dictionary by given .
/// Returns null if given is not present in the dictionary.
///
[CanBeNull]
public object this[string name]
{
get => Properties.GetOrDefault(name);
set => Properties[name] = value;
}
///
/// Can be used to get/set custom properties.
///
[NotNull]
public Dictionary Properties { get; }
public DataSeedContext(Guid? tenantId = null)
{
TenantId = tenantId;
Properties = new Dictionary();
}
///
/// Sets a property in the dictionary.
/// This is a shortcut for nested calls on this object.
///
public virtual DataSeedContext WithProperty(string key, object value)
{
Properties[key] = value;
return this;
}
}
}
DataSeeder
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow;
namespace Volo.Abp.Data
{
//TODO: Create a Volo.Abp.Data.Seeding namespace?
public class DataSeeder : IDataSeeder, ITransientDependency
{
protected IServiceScopeFactory ServiceScopeFactory { get; }
protected AbpDataSeedOptions Options { get; }
public DataSeeder(
IOptions options,
IServiceScopeFactory serviceScopeFactory)
{
ServiceScopeFactory = serviceScopeFactory;
Options = options.Value;
}
[UnitOfWork]
public virtual async Task SeedAsync(DataSeedContext context)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
foreach (var contributorType in Options.Contributors)
{
var contributor = (IDataSeedContributor) scope
.ServiceProvider
.GetRequiredService(contributorType);
await contributor.SeedAsync(context);
}
}
}
}
}
using Volo.Abp.Modularity;
namespace Zto.BookStore
{
[DependsOn(
typeof(BookStoreDomainSharedModule)
)]
public class BookStoreApplicationContractsModule : AbpModule
{
}
}
DTO
在文件夹Books下创建Dto:
BooksDto
using System;
using Volo.Abp.Application.Dtos;
namespace Zto.BookStore.Books
{
public class BookDto : AuditedEntityDto
{
public Guid AuthorId { get; set; }
public string AuthorName { get; set; }
public string Name { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
}
}
DTO类被用来在 表示层 和 应用层传递数据.查看DTO文档查看更多信息.
为了在页面上展示书籍信息,BookDto被用来将书籍数据传递到表示层.
BookDto继承自 AuditedEntityDto.跟上面定义的 Book 实体一样具有一些审计属性.
CreateUpdateBookDto
using System;
using System.ComponentModel.DataAnnotations;
namespace Zto.BookStore.Books
{
public class CreateUpdateBookDto
{
public Guid AuthorId { get; set; }
[Required]
[StringLength(BookConsts.MaxNameLength)]
public string Name { get; set; }
[Required]
public BookType Type { get; set; } = BookType.Undefined;
[Required]
[DataType(DataType.Date)]
public DateTime PublishDate { get; set; } = DateTime.Now;
[Required]
public float Price { get; set; }
}
}
这个DTO类被用于在创建或更新书籍的时候从用户界面获取图书信息.
它定义了数据注释属性(如[Required])来定义属性的验证. DTO由ABP框架自动验证.
IBookAppService
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Zto.BookStore.Books
{
public interface IBookAppService:
ICrudAppService< //Defines CRUD methods
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting
CreateUpdateBookDto> //Used to create/update a book
{
}
}
继承ICrudAppService<>
1.7 *.BookStore.Application
基本设置
修改默认命名空间为Zto.BookStore
创建文件夹Books
项目引用
*.Application.Contracts
依赖包
Volo.Abp.Ddd.Application
创建AbpModule
在文件夹Books下创建AbpModule:
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
namespace Zto.BookStore
{
[DependsOn(
typeof(BookStoreDomainModule),
typeof(BookStoreApplicationContractsModule),
typeof(AbpLocalizationModule)
)]
public class BookStoreApplicationModule : AbpModule
{
}
}
特别指出的是,依赖模块AbpLocalizationModule,支持本地化
对象映射
知识点 AutoMap
文档
AutoMapper——Map之实体的桥梁
AutoMapper官网
官方文档
基本使用
Setup
var config = new MapperConfiguration(cfg => {
cfg.AddProfile();
cfg.CreateMap();
});
var mapper = config.CreateMapper();
// or
IMapper mapper = new Mapper(config);
var dest = mapper.Map(new Source());
Starting with 9.0, the static API is no longer available.
Gathering configuration before initialization
AutoMapper also lets you gather configuration before initialization:
var cfg = new MapperConfigurationExpression();
cfg.CreateMap();
cfg.AddProfile();
MyBootstrapper.InitAutoMapper(cfg);
var mapperConfig = new MapperConfiguration(cfg);
IMapper mapper = new Mapper(mapperConfig);
Profile Instances
A good way to organize your mapping configurations is with profiles. Create classes that inherit from Profile and put the configuration in the constructor:
(通过自定义``Profile的子类,设置映射配置)
// This is the approach starting with version 5
public class OrganizationProfile : Profile
{
public OrganizationProfile()
{
CreateMap();
// Use CreateMap... Etc.. here (Profile methods are the same as configuration methods)
}
}
Assembly Scanning for auto configuration
Profiles can be added to the main mapper configuration in a number of ways, either directly:
// Scan for all profiles in an assembly
// ... using instance approach:
var config = new MapperConfiguration(cfg => {
cfg.AddMaps(myAssembly);
});
var configuration = new MapperConfiguration(cfg => cfg.AddMaps(myAssembly));
// Can also use assembly names:
var configuration = new MapperConfiguration(cfg =>
cfg.AddMaps(new [] {
"Foo.UI",
"Foo.Core"
});
);
// Or marker types for assemblies:
var configuration = new MapperConfiguration(cfg =>
cfg.AddMaps(new [] {
typeof(HomeController),
typeof(Entity)
});
);
AutoMapper will scan the designated assemblies for classes inheriting from Profile and add them to the configuration.
using Volo.Abp.AutoMapper;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
namespace Zto.BookStore
{
[DependsOn(
...
typeof(AbpAutoMapperModule)
)]
public class BookStoreApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure(options =>
{
//通过扫描程序集的方式搜索`Porfile`类,并添加到AutoMapper配置中
options.AddMaps();
});
}
}
}
源码代码分析
以下代码:
options.AddMaps();
调用源码:
public class AbpAutoMapperOptions
{
public AbpAutoMapperOptions()
{
Configurators = new List>();
ValidatingProfiles = new TypeList();
}
public void AddMaps(bool validate = false)
{
var assembly = typeof(TModule).Assembly;
Configurators.Add(context =>
{
context.MapperConfiguration.AddMaps(assembly);
});
......
}
这里使用
context.MapperConfiguration.AddMaps(assembly);
扫描程序集的方式搜索Profile类添加到AutoMapper配置中
对象转换
配置对象映射关系后,可以使用如下代码进行对象转换:
var bookDto = ObjectMapper.Map(book);
var bookDtos = ObjectMapper.Map, List>(books)
var config = new MapperConfiguration(cfg => {
cfg.AddProfile();
cfg.CreateMap();
});
IMapper mapper = config.CreateMapper();
var dest = mapper.Map(new Source());
BookStoreAppService
在文件夹Books下创建BookStoreAppService.cs
这是一个抽象类,其它xxxApplicationService都将继续自它:
///
/// Inherit your application services from this class.
///
public abstract class BookStoreAppService : ApplicationService
{
protected BookStoreAppService()
{
LocalizationResource = typeof(BookStoreResource);
}
}
设置本地化资源
LocalizationResource = typeof(BookStoreResource);
BookAppService.cs
BookAppService继承上一节定义的抽象类BookStoreAppService,
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace Zto.BookStore.Books
{
public class BookAppService :
CrudAppService<
Book, //The Book entity
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting
CreateUpdateBookDto>, //Used to create/update a book
IBookAppService //implement the IBookAppService
{
public BookAppService(IRepository repository)
: base(repository)
{
}
}
}