本文主要通过逐步构建一个CRUD示例程序来介绍 ABP 框架的基础知识。它涉及到应用开发的多个方面。在本章结束时,您将了解ABP 框架的基本开发方式。建议入门人员学习,老手不要浪费您宝贵时间。
 创建解决方案

第1步是为产品管理解决方案(如果您在前面已经创建过了ProductManagement解决方案,可以继续使用它)。在这里,我们运行以下ABP CLI 来进行创建:
  1. abp new ProductManagement -t app
我们使用自己熟悉的 IDE 中打开解决方案,创建数据库,然后运行 ​​Web 项目。如果您在运行解决方案时遇到问题,请参阅上一章,或者在知识星球里留言。
现在我们有一个正在可运行的解决方案。下一步创建领域对象来正式启动编码。

定义领域对象

该应用的领域很简单,有ProductCategory两个实体以及一个ProductStockState枚举,如图所示:

实体在解决方案的领域层中定义,它分为两个项目:
  • .Domain用于定义您的实体、值对象、领域服务、存储库接口和其他与领域相关的核心类。
  • .Domain.Shared用于定义一些可用于其他层的共享类型。通常,我们在这里定义枚举和一些常量。

产品类别实体(Category)

Category实体用于对产品进行分类。在ProductManagement.Domain项目中创建一个Categories文件夹,并在其中创建一个Category类:
  1. using System;
  2. using Volo.Abp.Domain.Entities.Auditing;
  3. namespace ProductManagement.Categories
  4. {
  5. public class Category : AuditedAggregateRoot<Guid>
  6. {
  7. public string Name { get; set; }
  8. }
  9. }
Category类派生自AuditedAggregateRoot<Guid>,这里Guid是实体的主键 (Id) 。您可以使用任何类型的主键(例如intlongstring)。
AggregateRoot是一种特殊的实体,用于创建聚合的根实体。它是一个领域驱动设计(DDD) 概念,我们将在接下来的章节中更详细地讨论。
相比AggregateRoot类,AuditedAggregateRoot添加了更多属性:CreationTime、、CreatorIdLastModificationTimeLastModifierId
当您将实体插入数据库时​​,ABP 会自动给这些属性赋值,CreationTime会设置为当前时间,CreatorId会自动设置为当前用户的Id属性。
关于充血领域模型
在本章中,我们使用公共的 getter 和 setter 来保持实体的简单性。如果您想创建更丰富的领域模型并应用 DDD 原则和其他最佳实践,我们将在接下来的章节中讨论它们。

产品库存状态枚举(ProductStockState)

ProductStockState是一个简单的枚举,用来设置和跟踪产品库存。
我们在*.Domain.Shared项目中创建一个Products*文件夹和一个枚举ProductStockState
  1. namespace ProductManagement.Products
  2. {
  3. public enum ProductStockState : byte
  4. {
  5. PreOrder,
  6. InStock,
  7. NotAvailable,
  8. Stopped
  9. }
  10. }
我们将在数据传输对象(DTO) 和界面层复用该枚举。

产品实体(Product)

在.Domain项目中创建一个Products文件夹,并在其中创建一个类Product
  1. using System;
  2. using Volo.Abp.Domain.Entities.Auditing;
  3. using ProductManagement.Categories;
  4. namespace ProductManagement.Products
  5. {
  6. public class Product : FullAuditedAggregateRoot<Guid>
  7. {
  8. public Category Category { get; set; }
  9. public Guid CategoryId { get; set; }
  10. public string Name { get; set; }
  11. public float Price { get; set; }
  12. public bool IsFreeCargo { get; set; }
  13. public DateTime ReleaseDate { get; set; }
  14. public ProductStockState StockState { get; set; }
  15. }
  16. }

这一次,我继承自FullAuditedAggregateRoot,相比Categoryd的AuditedAggregateRoot类,它还增加了IsDeletedDeletionTimeDeleterId属性。

FullAuditedAggregateRoot实现了ISoftDelete接口,用于实体的软删除。即它永远不会从数据库中做物理删除,而只是标记为已删除。ABP 会自动处理所有的软删除逻辑。包括下次查询时,已删除的实体会被自动过滤,除非您有意请求它们,否则它不会在查询结果中显示。

导航属性

在这个例子中,Product.Category是一个导航属性为Category的实体。如果您使用 MongoDB 或想要真正实现 DDD,则不应将导航属性添加到其他聚合中。但是,对于关系数据库,它可以完美运行并为我们的代码提供灵活性。
解决方案中的新文件如图所示:

我们已经创建了领域对象。接下来是常量值。

常量值

这些常量将在输入验证和数据库映射阶段进行使用。
首先,在.Domain.Shared项目中创建一个 Categories 文件夹并在里面添加一个类CategoryConsts
  1. namespace ProductManagement.Categories
  2. {
  3. public static class CategoryConsts
  4. {
  5. public const int MaxNameLength = 128;
  6. }
  7. }
在这里,MaxNameLength值将用于CategoryName属性的约束。
然后,在.Domain.Shard的 Products 文件夹中创建一个ProductConsts类:
  1. namespace ProductManagement.Products
  2. {
  3. public static class ProductConsts
  4. {
  5. public const int MaxNameLength = 128;
  6. }
  7. }
MaxNameLength值将用于约束ProductName属性。

现在,领域层已经完成定义,接下来将为 EF Core 配置数据库映射。

EF  Core和数据库映射

我们在该应用中使用EF Core。EF Core 是一个由微软提供的对象关系映射(ORM) 提供程序。ORM 提供了抽象,让您感觉像是在使用代码实体对象而不是数据库表。我们将在[第 6 章] 《使用数据访问基础架构》中介绍 ABP 的 EF Core 集成。现在,我们先了解如何使用它。
  1. 首先,我们将实体添加到DbContext类并定义实体和数据库表之间的映射;
  2. 然后,我们将使用 EF Core 的Code First方法创建对应的数据库表;
  3. 接下来,我们再看 ABP 的种子数据系统,并插入一些初始数据;
  4. 最后,我们会将数据库表结构和种子数据迁移到数据库中,以便为应用程序做好准备。
让我们从定义DbSet实体的属性开始。

将实体添加到 DbContext 类

EF的DbContext有两个主要用途:
  1. 定义实体和数据库表之间映射;
  2. 访问数据库和执行数据库相关实体的操作。
在.EntityFrameworkCore项目中打开ProductManagementDbContext该类,添加以下属性:
  1. public DbSet<Product> Products { get; set; }
  2. public DbSet<Category> Categories { get; set; }

EF Core 可以使用基于属性名称和类型的约定进行大部分映射。如果要自定义默认的映射配置或额外的配置,有两种方法:数据注释(属性)和Fluent API

在数据注释方法中,我们向实体属性添加特性,例如[Required][StringLength],非常方便,也很容易理解。
与Fluent API相比,数据注释容易受限,比如,当你需要使用EF Core的自定义特性时,他会让你的领域层依赖EF Core的NuGet包,比如[Index][Owned]
在本章中,我更倾向 Fluent API 方法,它使实体更干净,并将所有 ORM 逻辑放在基础设施层中。

将实体映射到数据库表

ProductManagementDbContext(在*.EntityFrameworkCore*项目中)包含一个OnModelCreating方法用来配置实体到数据库表的映射。当你首先创建您的解决方案时,此方法看起来如下所示:
  1. protected override void OnModelCreating(ModelBuilder builder)
  2. {
  3. base.OnModelCreating(builder);
  4. builder.ConfigurePermissionManagement();
  5. builder.ConfigureSettingManagement();
  6. builder.ConfigureIdentity();
  7. ...configuration of the other modules
  8. /* Configure your own tables/entities here */
  9. }

再添加CategoryProduct实体的配置和映射关系:

  1. builder.Entity<Category>(b =>
  2. {
  3. b.ToTable("Categories");
  4. b.Property(x => x.Name)
  5. .HasMaxLength(CategoryConsts.MaxNameLength)
  6. .IsRequired();
  7. b.HasIndex(x => x.Name);
  8. });
  9. builder.Entity<Product>(b =>
  10. {
  11. b.ToTable("Products");
  12. b.Property(x => x.Name)
  13. .HasMaxLength(ProductConsts.MaxNameLength)
  14. .IsRequired();
  15. b.HasOne(x => x.Category)
  16. .WithMany()
  17. .HasForeignKey(x => x.CategoryId)
  18. .OnDelete(DeleteBehavior.Restrict)
  19. .IsRequired();
  20. b.HasIndex(x => x.Name).IsUnique();
  21. });

我们使用CategoryConsts.MaxNameLength设置表CategoryName字段的最大长度。Name字段也是必填属性。最后,我们为属性定义了一个唯一的数据库索引,因为它有助于按Name字段搜索。

Product映射类似于Category。此外,它还定义了Category实体与Product实体之间的关系;一个Product实体属于一个Category实体,而一个Category实体可以有多个Product实体。
您可以参考 EF Core 文档进一步了解 Fluent API 的所有详细信息和其他选项。
映射配置完成后,我们就可以创建数据库迁移,把我们新加的实体转换成数据库结构。

添加迁移命令

当你创建一个新的实体或对现有实体进行更改,还应该同步到数据库中。EF Core 的Code First就是用来同步数据库和实体结构的强大工具。通常,我们需要先生成迁移脚本,然后执行迁移命令。迁移会对数据库的架构进行增量更改。有两种方法可以生成新迁移:

1 使用 Visual Studio

如果你正在使用Visual Studio,请打开视图|包管理器控制台菜单:
选择.EntityFrameworkCore项目作为默认项目,并右键设置.Web项目作为启动项目
现在,您可以在 控制台中键入以下命令:
  1. Add-Migration "Added_Categories_And_Products"

此命令的输出应类似于:


如果你得到一个诸如No DbContext was found in assembly... 之类的错误,请确保您已将*.EntityFrameworkCore*项目设置为默认项目。
如果一切顺利,会在.EntityFrameworkCore项目的Migrations文件夹中添加一个新的迁移类。

2 在命令行中

如果您不使用Visual Studio,你可以使用 EF Core命令行工具。如果尚未安装,请在命令行终端中执行以下命令:
  1. dotnet tool install --global dotnet-ef
现在,在.EntityFrameworkCore项目的根目录中打开一个命令行终端,然后输入以下命令:
  1. dotnet ef migrations add "Added_Categories_And_Products"

一个新的迁移类会添加到.EntityFrameworkCore项目的Migrations文件夹中。

种子数据

种子数据系统用于迁移数据库时添加一些初始数据。例如,身份模块在数据库中创建一个管理员用户,该用户具有登录应用程序的所有权限。
虽然种子数据在我们的场景中不是必需的,这里我想将一些产品类别和产品的初始化数据添加到数据库中,以便更轻松地开发和测试应用程序。
关于 EF Core 种子数据
本节使用 ABP 的种子数据系统,而 EF Core 有自己的种子数据功能。ABP 种子数据系统允许您在代码中注入运行时服务并实现高级逻辑,适用于开发、测试和生产环境。但是,对于简单的开发和测试,使用 EF Core 的种子数据基本够用。请查看官方文档。
在ProductManagement.Domain项目的Data文件夹中创建一个ProductManagementDataSeedContributor类:
  1. using ProductManagement.Categories;
  2. using ProductManagement.Products;
  3. using System;
  4. using System.Threading.Tasks;
  5. using Volo.Abp.Data;
  6. using Volo.Abp.DependencyInjection;
  7. using Volo.Abp.Domain.Repositories;
  8. namespace ProductManagement.Data
  9. {
  10. public class ProductManagementDataSeedContributor :
  11. IDataSeedContributor, ITransientDependency
  12. {
  13. private readonly IRepository<Category, Guid>_categoryRepository;
  14. private readonly IRepository<Product, Guid>_productRepository;
  15. public ProductManagementDataSeedContributor(
  16. IRepository<Category, Guid> categoryRepository,
  17. IRepository<Product, Guid> productRepository)
  18. {
  19. _categoryRepository = categoryRepository;
  20. _productRepository = productRepository;
  21. }
  22. public async Task SeedAsync(DataSeedContext context)
  23. {
  24. /***** TODO: Seed initial data here *****/
  25. }
  26. }
  27. }
该类实现了IDataSeedContributor接口,ABP 会自动发现并调用其SeedAsync方法。您也可以实现构造函数注入并使用类中的任何服务(例如本示例中的存储库)。
然后,在SeedAsync方法内部编码:
  1. if (await _categoryRepository.CountAsync() > 0)
  2. {
  3. return;
  4. }
  5. var monitors = new Category { Name = "Monitors" };
  6. var printers = new Category { Name = "Printers" };
  7. await _categoryRepository.InsertManyAsync(new[] { monitors, printers });
  8. var monitor1 = new Product
  9. {
  10. Category = monitors,
  11. Name = "XP VH240a 23.8-Inch Full HD 1080p IPS LED Monitor",
  12. Price = 163,
  13. ReleaseDate = new DateTime(2019, 05, 24),
  14. StockState = ProductStockState.InStock
  15. };
  16. var monitor2 = new Product
  17. {
  18. Category = monitors,
  19. Name = "Clips 328E1CA 32-Inch Curved Monitor, 4K UHD",
  20. Price = 349,
  21. IsFreeCargo = true,
  22. ReleaseDate = new DateTime(2022, 02, 01),
  23. StockState = ProductStockState.PreOrder
  24. };
  25. var printer1 = new Product
  26. {
  27. Category = monitors,
  28. Name = "Acme Monochrome Laser Printer, Compact All-In One",
  29. Price = 199,
  30. ReleaseDate = new DateTime(2020, 11, 16),
  31. StockState = ProductStockState.NotAvailable
  32. };
  33. await _productRepository.InsertManyAsync(new[] { monitor1, monitor2, printer1 });

我们创建了两个类别和三种产品并将它们插入到数据库中。每次您运行DbMigrator应用时都会执行此类。同时,我们检查if (await _categoryRepository.CountAsync() > 0)以防止数据重复插入。

种子数据和数据库表结构准备就绪, 下面进入正式迁移。

迁移数据库

EF Core 和 ABP 迁移有何区别?
ABP 启动模板中包含一个在开发和生产环境中非常有用的DbMigrator控制台项目。当您运行它时,所有待处理的迁移都将应用到数据库中,并执行数据初始化。
支持多租户/多数据库的场景,这是使用Update-Database无法实现的。
为什么要从主应用中分离出迁移项目?
在生产环境中部署和执行时,通常作为持续部署(CD) 管道的一个环节。从主应用中分离出迁移功能有个好处,主应用不需要更改数据库的权限。此外,如果不做分离可能会遇到数据库迁移和执行的并发问题。
将.DbMigrator项目设置为启动项目,然后按 Ctrl+F5 运行该项目,待应用程序退出后,您可以检查CategoriesProducts表是否已插入数据库中(如果您使用 Visual Studio,则可以使用SQL Server 对象资源管理器连接到LocalDB并浏览数据库)。
数据库已准备好了。接下来我们将在 UI 上显示产品数据。

定义应用服务

思路

我更倾向逐个功能地推进应用开发。本节将说明如何在 UI 上显示产品列表。
  1. 首先,我们会为Product实体定义一个ProductDto
  2. 然后,我们将创建一个向表示层返回产品列表的应用服务方法;
  3. 此外,我们将学习如何自动映射ProductProductDto
在创建 UI 之前,我将向您展示如何为应用服务编写自动化测试。这样,在开始 UI 开发之前,我们就可以确定应用服务是否正常工作。
在整个在开发过程中,我们将探索 ABP 框架的一些能力,例如自动 API 控制器和动态 JavaScript 代理系统。
最后,我们将创建一个新页面,并在其中添加一个数据表,然后从服务端获取产品列表,并将其显示在 UI 上。
梳理完思路,我们从创建一个ProductDto类开始。

ProductDto 类

DTO 用于在应用层和表示层之间传输数据。最佳实践是将 DTO 返回到表示层而不是实体,因为将实体直接暴露给表示层可能导致序列化和安全问题,有了DTO,我们不但可以抽象实体,对接口展示内容也更加可控。
为了在 UI 层中可复用,DTO 规定在Application.Contracts项目中进行定义。我们首先在*.Application.Contracts项目的Products文件夹中创建一个ProductDto类:
  1. using System;
  2. using Volo.Abp.Application.Dtos;
  3. namespace ProductManagement.Products
  4. {
  5. public class ProductDto : AuditedEntityDto<Guid>
  6. {
  7. public Guid CategoryId { get; set; }
  8. public string CategoryName { get; set; }
  9. public string Name { get; set; }
  10. public float Price { get; set; }
  11. public bool IsFreeCargo { get; set; }
  12. public DateTime ReleaseDate { get; set; }
  13. public ProductStockState StockState { get; set; }
  14. }
  15. }
ProductDto与实体类基本相似,但又有以下区别:
  • 它派生自AuditedEntityDto<Guid>,它定义了IdCreationTimeCreatorIdLastModificationTimeLastModifierId属性(我们不需要做删除审计DeletionTime,因为删除的实体不是从数据库中读取的)。
  • 我们没有向实体Category添加导航属性,而是使用了一个string类型的CategoryName的属性,用以在 UI 上显示。
我们将使用使用ProductDto类从IProductAppService接口返回产品列表。

产品应用服务

应用服务实现了应用的业务逻辑,UI 调用它们用于用户交互。通常,应用服务方法返回一个 DTO。

1 应用服务与 API 控制器

ABP的应用服务和MVC 中的 API 控制器有何区别?
您可以将应用服务与 ASP.NET Core MVC 中的 API 控制器进行比较。虽然它们有相似之处,但是:
  1. 应用服务更适合 DDD ,它们不依赖于特定的 UI 技术。
  2. 此外,ABP 可以自动将您的应用服务公开为 HTTP API。
我们在*.Application.Contracts项目的Products文件夹中创建一个IProductAppService接口:
  1. using System.Threading.Tasks;
  2. using Volo.Abp.Application.Dtos;
  3. using Volo.Abp.Application.Services;
  4. namespace ProductManagement.Products
  5. {
  6. public interface IProductAppService : IApplicationService
  7. {
  8. Task<PagedResultDto<ProductDto>> GetListAsync(PagedAndSortedResultRequestDto input);
  9. }
  10. }
我们可以看到一些预定义的 ABP 类型:
  • IProductAppService约定从IApplicationService接口,这样ABP 就可以识别应用服务。
  • GetListAsync方法的入参PagedAndSortedResultRequestDto是 ABP 框架的标准 DTO 类,它定义了MaxResultCountSkipCountSorting属性。
  • GetListAsync方法返回PagedResultDto<ProductDto>,其中包含一个TotalCount属性和一个ProductDto对象集合,这是使用 ABP 框架返回分页结果的便捷方式。
当然,您可以使用自己的 DTO 代替这些预定义的 DTO。但是,当您想要标准化一些常见问题,避免到处都使用相同的命名时,它们非常有用。

2 异步方法

将所有应用服务方法定义为异步方法是最佳实践。如果您定义为同步方法,在某些情况下,某些 ABP 功能(例如工作单元)可能无法按预期工作。
现在,我们可以实现IProductAppService接口来执行用例。

3 产品应用服务

我们在ProductManagement.Application项目中创建一个ProductAppService类:
  1. using System.Linq.Dynamic.Core;
  2. using System.Threading.Tasks;
  3. using Volo.Abp.Application.Dtos;
  4. using Volo.Abp.Domain.Repositories;
  5. namespace ProductManagement.Products
  6. {
  7. public class ProductAppService : ProductManagementAppService, IProductAppService
  8. {
  9. private readonly IRepository<Product, Guid> _productRepository;
  10. public ProductAppService(IRepository<Product, Guid> productRepository)
  11. {
  12. _productRepository = productRepository;
  13. }
  14. public async Task<PagedResultDto<ProductDto>> GetListAsync(PagedAndSortedResultRequestDto input)
  15. {
  16. /* TODO: Implementation */
  17. }
  18. }
  19. }
ProductAppService派生自ProductManagementAppService,它在启动模板中定义,可用作应用服务的基类。它实现了之前定义的IProductAppService接口,并注入IRepository<Product, Guid>服务。这就是通用默认存储库,方面我们对数据库执行操作(ABP 自动为所有聚合根实体提供默认存储库实现)。
我们实现GetListAsync方法,如下代码块所示:
  1. public async Task<PagedResultDto<ProductDto>> GetListAsync(PagedAndSortedResultRequestDto input)
  2. {
  3. var queryable = await _productRepository.WithDetailsAsync(x => x.Category);
  4. queryable = queryable
  5. .Skip(input.SkipCount)
  6. .Take(input.MaxResultCount)
  7. .OrderBy(input.Sorting ?? nameof(Product.Name));
  8. var products = await AsyncExecuter.ToListAsync(queryable);
  9. var count = await _productRepository.GetCountAsync();
  10. return new PagedResultDto<ProductDto>(
  11. count,
  12. ObjectMapper.Map<List<Product>, List<ProductDto>>(products)
  13. );
  14. }
这里,_productRepository.WithDetailsAsync返回一个包含产品类别的IQueryable<Product>对象,(WithDetailsAsync方法类似于 EF Core 的Include扩展方法,用于将相关数据加载到查询中)。于是,我们就可以方便地使用标准的(LINQ) 扩展方法,比如SkipTakeOrderBy等。
AsyncExecuter服务(基类中预先注入)用于执行IQueryable对象,这使得可以使用异步 LINQ 扩展方法执行数据库查询,而无需依赖应用程序层中的 EF Core 包。(我们将在[第 6 章 ] 中对AsyncExecuter进行更详细的探讨)
最后,我们使用ObjectMapper服务(在基类中预先注入)将Product集合映射到ProductDto集合。

对象映射

ObjectMapperIObjectMapper)会自动使用AutoMapper库进行类型转换。它要求我们在使用之前预先定义映射关系。启动模板包含一个配置文件类,您可以在其中创建映射。
在ProductManage.Application项目中打开ProductManagementApplicationAutoMapperProfile类,并将其更改为以下内容:
  1. using AutoMapper;
  2. using ProductManagement.Products;
  3. namespace ProductManagement
  4. {
  5. public class ProductManagementApplicationAutoMapperProfile : Profile
  6. {
  7. public ProductManagementApplicationAutoMapperProfile()
  8. {
  9. CreateMap<Product, ProductDto>();
  10. }
  11. }
  12. }
CreateMap所定义的映射。它可以自动将Product转换为ProductDto对象。
AutoMapper中有一个有趣的功能:Flattening,它默认会将复杂的对象模型展平为更简单的模型。在这个例子中,Product类有一个Category属性,而Category类也有一个Name属性。因此,如果要访问产品的类别名称,则应使用Product.Category.Name表达式。但是,ProductDtoCategoryName可以直接使用ProductDto.CategoryName表达式进行访问。AutoMapper 会通过展平Category.Name来自动映射成CategoryName
应用层服务已经基本完成。在开始 UI 之前,我们先介绍如何为应用层编写自动化测试。
 
 
 
 

ABP应用开发(Step by Step)-上篇的更多相关文章

  1. Step by Step: 基于MFC下的COM组件开发-Helloworld

    http://blog.csdn.net/sybifei/article/details/45008745 [这篇文章有问题, 仅供参考] http://blog.csdn.net/define_us ...

  2. Step by step Dynamics CRM 2011升级到Dynamics CRM 2013

    原创地址:http://www.cnblogs.com/jfzhu/p/4018153.html 转载请注明出处 (一)检查Customizations 从2011升级到2013有一些legacy f ...

  3. Step by step Install a Local Report Server and Remote Report Server Database

    原创地址:http://www.cnblogs.com/jfzhu/p/4012097.html 转载请注明出处 前面的文章<Step by step SQL Server 2012的安装 &g ...

  4. WPF Step By Step 系列-Prism框架在项目中使用

    WPF Step By Step 系列-Prism框架在项目中使用 回顾 上一篇,我们介绍了关于控件模板的用法,本节我们将继续说明WPF更加实用的内容,在大型的项目中如何使用Prism框架,并给予Pr ...

  5. WPF Step By Step 完整布局介绍

    WPF Step By Step 完整布局介绍 回顾 上一篇,我们介绍了基本控件及控件的重要属性和用法,我们本篇详细介绍WPF中的几种布局容器及每种布局容器的使用场景,当 然这些都是本人在实际项目中的 ...

  6. WPF Step By Step 控件介绍

    WPF Step By Step 控件介绍 回顾 上一篇,我们主要讨论了WPF的几个重点的基本知识的介绍,本篇,我们将会简单的介绍几个基本控件的简单用法,本文会举几个项目中的具体的例子,结合这些 例子 ...

  7. WPF Step By Step 系列 - 开篇 ·

    WPF Step By Step 系列 - 开篇 公司最近要去我去整理出一个完整的WPF培训的教程,我刚好将自己学习WPF的过程和经验总结整理成笔记的方式来讲述,这里就不按照书上面的东西来说了,书本上 ...

  8. WinForm RDLC SubReport Step by step

    最近在做的一个PO管理系统,因为要用到订单打印,没有用水晶报表,直接使用VS2010的Reporting.参考了网上的一些文章,但因为找到的数据是用于WebForm的,适配到WinForm有点区别,竟 ...

  9. Struts2+Spring+Hibernate step by step 11 ssh拦截验证用户登录到集成

    注意:该系列文章从教师王健写了一部分ssh集成开发指南 引言: 之前没有引入拦截器之前,我们使用Filter过滤器验证用户是否登录,在使用struts2之后,全然能够使用拦截器,验证用户是否已经登录, ...

  10. 持续交付工具ThoughtWorks Go部署step by step

    持续交付工具ThoughtWorks Go部署step by step http://blogs.360.cn/360cloud/2014/05/13/%E6%8C%81%E7%BB%AD%E4%BA ...

随机推荐

  1. 【flareon6】 overlong-通过动调改内存修改程序

    程序分析 无壳,32位程序 运行后结果 程序比较简单一共三个函数 根据题目和运行结果可以看出来是a3太小了,没法完全解密密钥 解决该问题可以通过写脚本或动调解决 方法一:动调改内存 定位到a3入栈的位 ...

  2. Java代码查错部分?

    1. abstract class Name { private String name; public abstract boolean isStupidName(String name) {} } ...

  3. 什么是 Spring Profiles?

    Spring Profiles 允许用户根据配置文件(dev,test,prod 等)来注册 bean.因此,当应用程序在开发中运行时,只有某些 bean 可以加载,而在 PRODUCTION中,某些 ...

  4. VMware ESXi安装NVIDIA GPU显卡硬件驱动和配置vGPU

    一.驱动软件准备:从nvidia网站下载驱动,注意,和普通显卡下载驱动地址不同. 按照ESXi对应版本不同下载不同的安装包.安装包内含ESXi主机驱动和虚拟机驱动. GPU显卡和物理服务器兼容查询:( ...

  5. ros系统21讲—前六讲

    课程介绍(第一讲) linux介绍安装(第二讲) linux的基础操作(第三讲) ROS中语言c++与python介绍(第四讲) 安装ROS系统(第五讲) 第一个: sudo sh -c echo d ...

  6. Pandas怎样新增数据列

    Pandas怎样新增数据列? 在进行数据分析时,经常需要按照一定条件创建新的数据列,然后进行进一步分析. 直接赋值 df.apply方法 df.assign方法 按条件选择分组分别赋值 0.读取csv ...

  7. 使用Vue2+webpack+Es6快速开发一个移动端项目,封装属于自己的jsonpAPI和手势响应式组件

    导语 最近看到不少使用vue制作的音乐播放器,挺好玩的,本来工作中也经常使用Vue,一起交流学习,好的话点个star哦 本项目特点如下 : 1. 原生js封装自己的跨域请求函数,支持promise调用 ...

  8. ES6-11学习笔记--const

    新声明方式:const 1.不属于顶层对象 window 2.不允许重复声明 3.不存在变量提升 4.暂时性死区 5.块级作用域   以上特性跟let声明一样,特性可看 let 的学习笔记:链接跳转 ...

  9. springboot插件

    目前spring官网(http://spring.io/tools/sts/all)上可下载的spring插件只有:springsource-tool-suite-3.8.4(sts-3.8.4).但 ...

  10. MySQL 中 SQL语句大全(详细)

    sql语句总结 总结内容 1. 基本概念 2. SQL列的常用类型 3. DDL简单操作 3.1 数据库操作 3.2 表操作 4. DML操作 4.1 修改操作(UPDATE SET) 4.2 插入操 ...