书香校园网站建设,网站结构的规划,深圳石岩网站建设,加强网站安全建设说明报告范文Abp vnext 4.4出现了一个比较重大的变更#xff1a;在Startup template中移除了EF Core Migrations项目#xff0c;本文翻译自community.abp.io/articl由于本文发布的时候Abp vnext的版本还没有到4.4#xff0c;所以本文演示了如何从4.4以前的版本移除EntityFrameworkCore.D… Abp vnext 4.4出现了一个比较重大的变更在Startup template中移除了EF Core Migrations项目本文翻译自community.abp.io/articl由于本文发布的时候Abp vnext的版本还没有到4.4所以本文演示了如何从4.4以前的版本移除EntityFrameworkCore.DbMigrations这个项目并且使用唯一的一个DbContext来进行数据库的映射和基于Code-First模式的迁移。该项目的github地址如下github.com/abpframework动机/背景如果你使用Ef core作为数据库provider创建一个解决方案那么会有两个与ef core有关的项目EntityFrameworkCore这个项目包含了你的应用的真正的DbContext它包含了所有的数据库映射和你的Repository的实现。另一方面EntityFrameworkCore.DbMigrations 项目包含了另一个DbContext用来创建和施行数据库迁移。它包含了你所使用的所有模块的数据库映射所以你有一个单独并统一的数据库架构/方案。当时这么做主要有两个原因你真正的DbContext保持了简单和集中focused。它只包含了你自己应用中的实体相关的内容并且不包含你所使用的关于其他模块的内容。你可以创建自己的类映射到依赖模块的表。例如AppUser实体(包含在下载的解决方案中)映射到数据库中的AbpUsers表而AbpUsers表实际上映射到Identity Module的IdentityUser实体。这意味着它们共享相同的数据库表。与IdentityServer相比AppUser包含的属性更少。您只添加您需要的属性而不是更多。这还允许您根据自定义需求向AppUser添加新的标准(类型安全)属性只要您仔细地管理数据库映射。对于这个方面的说明我们在官方的文档中有详细的说明。然而当你重用那些你依赖的模块的表时会存在一些问题那就是这样的架构会导致你的数据库映射变得复杂。许多开发者在做一些诸如映射这些类/实体的工作时会变得迷茫和犯错特别是当他们想要将这些实体和其他实体联系起来时。所以我们决定在4.4的版本中取消这种分离删除EntityFrameworkCore.DbMigrations这个项目。新版本的abp vnext中将只包含EntityFrameworkCore这个项目并且只拥有一个DbContext。如果你今天就想尝试这么干请接着往下看。警告新的设计有一个缺点(软件开发中的一切都是一种权衡)。我们需要删除AppUser实体因为EF Core不能在没有继承关系的情况下将两个类映射到单个表。我将在本文后面介绍这一点并提供处理它的建议。步骤我们的目标是在EntityFrameworkCore项目中启用数据库迁移移除EntityFrameworkCore.DbMigrations项目并根据该包重新访问代码。第一步为EntityFrameworkCore添加Microsoft.EntityFrameworkCore.Tools包在EntityFrameworkCore.csproj文件中添加如下代码ItemGroupPackageReference IncludeMicrosoft.EntityFrameworkCore.Tools Version5.0.*IncludeAssetsruntime; build; native; contentfiles; analyzers/IncludeAssetsPrivateAssetscompile; contentFiles; build; buildMultitargeting; buildTransitive; analyzers; native/PrivateAssets/PackageReference
/ItemGroup
第二步创建design time DbContext factory在EntityFrameworkCore项目中创建一个实现了IDesignTimeDbContextFactoryT的类using System.IO;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;namespace UnifiedContextsDemo.EntityFrameworkCore
{public class UnifiedContextsDemoDbContextFactory : IDesignTimeDbContextFactoryUnifiedContextsDemoDbContext{public UnifiedContextsDemoDbContext CreateDbContext(string[] args){UnifiedContextsDemoEfCoreEntityExtensionMappings.Configure();var configuration BuildConfiguration();var builder new DbContextOptionsBuilderUnifiedContextsDemoDbContext().UseSqlServer(configuration.GetConnectionString(Default));return new UnifiedContextsDemoDbContext(builder.Options);}private static IConfigurationRoot BuildConfiguration(){var builder new ConfigurationBuilder().SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), ../UnifiedContextsDemo.DbMigrator/)).AddJsonFile(appsettings.json, optional: false);return builder.Build();}}
}
这些代码基本上是从EntityFrameworkCore.DbMigrations这个项目中粘贴过来的重命名了一下并且将里面的DbContext替换成了EntityFrameworkCore项目中的那个DbContext。第三步创建数据库方案迁移类将EntityFrameworkCore...DbSchemaMigrator...代表了你项目的名字类复制到EntityFrameworkCore项目下并且将其中的DbContext替换成EntityFrameworkCore项目中的那个真正的DbContext在我的示例项目中代码是这样的using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using UnifiedContextsDemo.Data;
using Volo.Abp.DependencyInjection;namespace UnifiedContextsDemo.EntityFrameworkCore
{public class EntityFrameworkCoreUnifiedContextsDemoDbSchemaMigrator: IUnifiedContextsDemoDbSchemaMigrator, ITransientDependency{private readonly IServiceProvider _serviceProvider;public EntityFrameworkCoreUnifiedContextsDemoDbSchemaMigrator(IServiceProvider serviceProvider){_serviceProvider serviceProvider;}public async Task MigrateAsync(){/* We intentionally resolving the UnifiedContextsDemoMigrationsDbContext* from IServiceProvider (instead of directly injecting it)* to properly get the connection string of the current tenant in the* current scope.*/await _serviceProvider.GetRequiredServiceUnifiedContextsDemoDbContext().Database.MigrateAsync();}}
}
第四步转移模块的配置迁移DbContext在迁移项目中定义的那个DbContext通常包含你使用的每个模块的builder.ConfigureXXX()这样的代码行。我们可以将这些行移动到EntityFrameworkCore项目中的实际DbContext中。另外删除AppUser的数据库映射(我们将删除这个实体)。或者你可以将你自己的实体的数据库映射代码从… DbContextModelCreatingExtensions类放在实际DbContext的OnModelCreating方法中并删除静态扩展类。注上文提到的AppUser数据库映射这些代码是包含在EntityFramworkCore的DbContext中具体如下 /* Configure the shared tables (with included modules) here */builder.EntityAppUser(b {b.ToTable(AbpIdentityDbProperties.DbTablePrefix Users); //Sharing the same table AbpUsers with the IdentityUserb.ConfigureByConvention();b.ConfigureAbpUser();/* Configure mappings for your additional properties* Also see the BlazorEfCoreEntityExtensionMappings class*/});
最终修改后的DbContext是下面这个样子的using Microsoft.EntityFrameworkCore;
using UnifiedContextsDemo.Users;
using Volo.Abp.AuditLogging.EntityFrameworkCore;
using Volo.Abp.BackgroundJobs.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Identity.EntityFrameworkCore;
using Volo.Abp.IdentityServer.EntityFrameworkCore;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.TenantManagement.EntityFrameworkCore;namespace UnifiedContextsDemo.EntityFrameworkCore
{[ConnectionStringName(Default)]public class UnifiedContextsDemoDbContext: AbpDbContextUnifiedContextsDemoDbContext{public DbSetAppUser Users { get; set; }/* Add DbSet properties for your Aggregate Roots / Entities here.* Also map them inside UnifiedContextsDemoDbContextModelCreatingExtensions.ConfigureUnifiedContextsDemo*/public UnifiedContextsDemoDbContext(DbContextOptionsUnifiedContextsDemoDbContext options): base(options){}protected override void OnModelCreating(ModelBuilder builder){base.OnModelCreating(builder);builder.ConfigurePermissionManagement();builder.ConfigureSettingManagement();builder.ConfigureBackgroundJobs();builder.ConfigureAuditLogging();builder.ConfigureIdentity();builder.ConfigureIdentityServer();builder.ConfigureFeatureManagement();builder.ConfigureTenantManagement();/* Configure your own tables/entities inside here *///builder.EntityYourEntity(b //{// b.ToTable(UnifiedContextsDemoConsts.DbTablePrefix YourEntities, UnifiedContextsDemoConsts.DbSchema);// b.ConfigureByConvention(); //auto configure for the base class props// //...//});}}
}
第五步从解决方案中移除EntityFrameworkCore.DbMigrations 项目将EntityFrameworkCore.DbMigrations移除并且将一切引用该项目替换为引用EntityFrameWorkCore项目。同时EntityFrameworkCore.DbMigrations项目的作用现在也变更为了EntityFrameworkCore项目。在这个例子中我需要将DbMigrator,Web和EntityFrameworkCore.Tests 这三个项目的对EntityFrameworkCore.DbMigrations的引用变更为EntityframeworkCore项目。第六步删除AppUser实体类你需要删除AppUser实体类因为Abp没有办法在两个没有继承关系的类上面映射同一张表。所以应该删除它以及和他相关的内容如果你要查询有关用户的内容你应该用IdentityUser来代替。可以在官方文档中查看与自定义属性和AppUser相关的内容。第七步创建或者移动迁移内容现在我们已经删除了EntityFrameworkCore.DbMigrations项目。接下来我们要考虑关于数据库迁移的事情了。如果你要保持数据库的迁移历史你需要从EntityFrameworkCore.DbMigrations项目吧Migrations目录中的内容拷贝到EntityFrameworkCore并且将内容中的DbContext手工的替换为EntityFrameworkCore项目中定义的DbContext。另一种做法是清除项目中的迁移历史并在数据库中的已提交的迁移历史上继续那你需要做的是在EntityFrameworkCore项目中创建一个数据库迁移并在该项目的根目录下面执行下面的命令dotnet ef migrations add InitialUnified你无疑需要为这个迁移命令起一个全新的名字这个迁移肯定会生成一堆内容你需要小心的将Up和Down这两个方法中的内容全部删除然后就可以将这个迁移实际上是一个空的迁移应用到数据库了dotnet ef database update这个操作不会对数据库造成任何更改毕竟你已经将Up和Down方法里面的内容全删除了。接下来你就可以像平常一样进行接下来的操作了。AppUser 实体和自定义扩展属性现在数据库映射逻辑、解决方案结构、迁移以及我们接下来要做的事情变得更简单了。作为缺点来说我们需要删除AppUser实体它和Identity Module中定义的IdentityUser共享了数据库中的同一张表。幸运的是当你需要在已存在的实体上比如Identity module中定义的IdentityUser增加一些自定义的属性时Abp提供了一个相当灵活的系统。在这一节中我将演示如何在IdentityUser上面增加一些自定义的属性并在程序编码和数据库查询上应用这些自定义的字段。关于这些内容我已经作为单独的pr发布到github上你可以点击这个链接进行查看https://github.com/abpframework/abp-samples/pull/89github.com声明一个自定义的属性启动模板中有一个关于在已存在实体上自定义属性的入口这个入口在Domain.Share项目下面...ModuleExtensionConfigurator.cs...代表你项目的名称这个文件中。打开这个文件并在ConfigureExtraProperties方法中下如下代码ObjectExtensionManager.Instance.Modules().ConfigureIdentity(identity {identity.ConfigureUser(user {user.AddOrUpdatePropertystring( //property type: stringSocialSecurityNumber, //property nameproperty {//validation rulesproperty.Attributes.Add(new RequiredAttribute());property.Attributes.Add(new StringLengthAttribute(64));});});});
完事儿后运行程序并在User table上面你可以看到这个属性新的SocialSecurityNumber属性也会在创建和更新Modal中显示并应用校验规则。查看如下链接了解关于扩展属性的一切信息https://docs.abp.io/en/abp/latest/Module-Entity-Extensionsdocs.abp.io映射到数据库表默认情况下Abp将所有自定义的属性保存在数据库表中的ExtraProperties属性上作为一个JSON保存 。如果你想要将自定义的字段作为单独的表字段保存你需要在EntityFrameworkCore项目中定义的...EfCoreEntityExtensionMappings.cs文件...代表你项目的名字上进行编码定义在OneTimeRunner.Run方法中ObjectExtensionManager.Instance.MapEfCorePropertyIdentityUser, string(SocialSecurityNumber,(entityBuilder, propertyBuilder) {propertyBuilder.HasMaxLength(64).IsRequired().HasDefaultValue();});
这个完事儿后你需要定义新的数据库迁移方案将你的新扩展的属性进行迁移在EntityframeworkCore项目下dotnet ef migrations add Added_SocialSecurityNumber_To_IdentityUser这会在EntityframeworkCore项目下面新增一个迁移文件然后你要将这个迁移应用到数据库dotnet ef database update你也可以运行.DbMigrator项目来应用迁移这个项目的作用就在于此。这会在数据库AbpUsers表上创建一个SocialSecurityNumber字段。在应用程序代码中使用自定义字段现在我们可以在IdentityUser实体上使用GetProperty和SetProperty这两个方法来使用我们自定义的属性public class MyUserService : ITransientDependency
{private readonly IRepositoryIdentityUser, Guid _userRepository;public MyUserService(IRepositoryIdentityUser, Guid userRepository){_userRepository userRepository;}public async Task SetSocialSecurityNumberDemoAsync(string userName, string number){var user await _userRepository.GetAsync(u u.UserName userName);user.SetProperty(SocialSecurityNumber, number);await _userRepository.UpdateAsync(user);}public async Taskstring GetSocialSecurityNumberDemoAsync(string userName){var user await _userRepository.GetAsync(u u.UserName userName);return user.GetPropertystring(SocialSecurityNumber);}
}
上面的代码中我们使用了”SocialSecurityNumber“硬编码来直接调用更好的做法是我们可以定义一些扩展方法来包装这种调用。下面我们改进这种做法:public static class MyUserExtensions
{public const string SocialSecurityNumber SocialSecurityNumber;public static void SetSocialSecurityNumber(this IdentityUser user, string number){user.SetProperty(SocialSecurityNumber, number);}public static string GetSocialSecurityNumber(this IdentityUser user){return user.GetPropertystring(SocialSecurityNumber);}
}
定义后扩展方法后我们改进一开始的那种调用public async Task SetSocialSecurityNumberDemoAsync(string userName, string number)
{var user await _userRepository.GetAsync(u u.UserName userName);user.SetSocialSecurityNumber(number); //Using the new extension propertyawait _userRepository.UpdateAsync(user);
}public async Taskstring GetSocialSecurityNumberDemoAsync(string userName)
{var user await _userRepository.GetAsync(u u.UserName userName);return user.GetSocialSecurityNumber(); //Using the new extension property
}
自定义属性的查询你可能会基于自定义的属性做一些查询我们会使用Entity Framework的API来完成基于此我们这里给出两个解决方案1、引用Microsoft.EntityFrameworkCore包到你的项目中Domain项目或者Application项目具体看你的需求。2、在Domain中创建一个repository接口并在EntityFrameworkCore项目中实现它。我更倾向于第二个方案所以我在repository接口中定义一些方法先using System;
using System.Threading.Tasks;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;namespace UnifiedContextsDemo.Users
{public interface IMyUserRepository : IRepositoryIdentityUser, Guid{TaskIdentityUser FindBySocialSecurityNumber(string number);}
}
然后在EntityframeworkCore项目中实现它using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using UnifiedContextsDemo.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Identity;namespace UnifiedContextsDemo.Users
{public class MyUserRepository: EfCoreRepositoryUnifiedContextsDemoDbContext, IdentityUser, Guid,IMyUserRepository{public MyUserRepository(IDbContextProviderUnifiedContextsDemoDbContext dbContextProvider): base(dbContextProvider){}public async TaskIdentityUser FindBySocialSecurityNumber(string number){var dbContext await GetDbContextAsync();return await dbContext.SetIdentityUser().Where(u EF.Propertystring(u, SocialSecurityNumber) number).FirstOrDefaultAsync();}}
}注意使用一个常量而不是字符串硬编码来搞这样更好一些。现在我们可以在Service里面注入repository来使用了:)public class MyUserService : ITransientDependency
{private readonly IMyUserRepository _userRepository;public MyUserService(IMyUserRepository userRepository){_userRepository userRepository;}//...other methodspublic async TaskIdentityUser FindBySocialSecurityNumberDemoAsync(string number){return await _userRepository.FindBySocialSecurityNumber(number);}
}
总结这篇文章描述了如何删除EntityFrameworkCore.DbMigrations项目来简化你的数据库映射、数据库迁移以及应用程序编码。在4.4这个版本中我们已经在启动模板中移除了这个项目了。源码https://github.com/abpframework/abp-samples/tree/master/UnifiedEfCoreMigrations