一、基础层搭建

二、PM.Core

三、PM.EntityFramework

四、PM.Application

五、PM.WebApi

六、PM.Web(MPA)

七、PM.Web(SPA)

八、单元测试

一、基础层搭建

1,创建一个空解决方案 

2,层结构

PM.Core[v:4.6]:类库

PM.EntityFramework[v:4.6]:类库(引用PM.Core)

PM.Application[v:4.6]:类库(引用PM.Core)

PM.WebApi[v:4.6]:类库(引用PM.Application、PM.Core)

PM.Web[v:4.6]:WebMvc5.X(引用PM.Core、PM.EntityFramework、PM.Application、PM.WebApi)

PM.Test[v:4.6]:(引用PM.EntityFramework、PM.Core、PM.Application)

二、PM.Core

1,NuGet安装Abp.Zero2.1.3

程序集引用:System.ComponentModel.DataAnnotations

2,基本结构

Authorization

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.MultiTenancy;
using Abp.Zero.Configuration; namespace PM.Core.Authorization.Roles
{
public class PMRoleConfig
{
public static void Configure(IRoleManagementConfig roleManagementConfig)
{
//静态主机角色
roleManagementConfig.StaticRoles.Add(new StaticRoleDefinition(
StaticRoleNames.Host.Admin
, MultiTenancySides.Host)); //静态租户角色
roleManagementConfig.StaticRoles.Add(new StaticRoleDefinition(
StaticRoleNames.Tenants.Admin,
MultiTenancySides.Tenant));
}
}
}

PMRoleConfig

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Authorization.Roles;
using PM.Core.Users; namespace PM.Core.Authorization.Roles
{
public class Role : AbpRole<User>
{ }
}

Role

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Authorization;
using Abp.Authorization.Roles;
using Abp.Domain.Uow;
using Abp.Runtime.Caching;
using Abp.Zero.Configuration;
using PM.Core.Users; namespace PM.Core.Authorization.Roles
{
public class RoleManager:AbpRoleManager<Role,User>
{
public RoleManager(
RoleStore store,
IPermissionManager permissionManager,
IRoleManagementConfig roleManagementConfig,
ICacheManager cacheManager,
IUnitOfWorkManager unitOfWorkManager)
: base(store,
permissionManager,
roleManagementConfig,
cacheManager,
unitOfWorkManager)
{
}
}
}

RoleManager

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Authorization.Roles;
using Abp.Authorization.Users;
using Abp.Domain.Repositories;
using PM.Core.Users; namespace PM.Core.Authorization.Roles
{
public class RoleStore:AbpRoleStore<Role,User>
{
public RoleStore(
IRepository<Role> roleRepository,
IRepository<UserRole, long> userRoleRepository,
IRepository<RolePermissionSetting, long> rolePermissionSettingRepository)
: base(roleRepository,
userRoleRepository,
rolePermissionSettingRepository)
{
}
}
}

RoleStore

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace PM.Core.Authorization.Roles
{
public class StaticRoleNames
{
public static class Host
{
public const string Admin = "Admin";
}
public static class Tenants
{
public const string Admin = "Admin";
}
}
}

StaticRoleNames

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Authorization;
using Abp.Authorization.Roles;
using Abp.Authorization.Users;
using Abp.Configuration;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.IdentityFramework;
using Abp.Localization;
using Abp.Organizations;
using Abp.Runtime.Caching;
using PM.Core.Authorization.Roles; namespace PM.Core.Users
{
public class UserManager : AbpUserManager<Role, User>
{
public UserManager(
UserStore userStore,
RoleManager roleManager,
IPermissionManager permissionManager,
IUnitOfWorkManager unitOfWorkManager,
ICacheManager cacheManager,
IRepository<OrganizationUnit, long> organizationUnitRepository,
IRepository<UserOrganizationUnit, long> userOrganizationUnitRepository,
IOrganizationUnitSettings organizationUnitSettings,
ILocalizationManager localizationManager,
IdentityEmailMessageService emailService,
ISettingManager settingManager,
IUserTokenProviderAccessor userTokenProviderAccessor)
: base(
userStore,
roleManager,
permissionManager,
unitOfWorkManager,
cacheManager,
organizationUnitRepository,
userOrganizationUnitRepository,
organizationUnitSettings,
localizationManager,
emailService,
settingManager,
userTokenProviderAccessor)
{
}
}
}

UserManager

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Authorization.Users;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using PM.Core.Authorization.Roles; namespace PM.Core.Users
{
public class UserStore:AbpUserStore<Role,User>
{
public UserStore(
IRepository<User, long> userRepository,
IRepository<UserLogin, long> userLoginRepository,
IRepository<UserRole, long> userRoleRepository,
IRepository<Role> roleRepository,
IRepository<UserPermissionSetting, long> userPermissionSettingRepository,
IUnitOfWorkManager unitOfWorkManager,
IRepository<UserClaim, long> userClaimRepository)
: base(
userRepository,
userLoginRepository,
userRoleRepository,
roleRepository,
userPermissionSettingRepository,
unitOfWorkManager,
userClaimRepository)
{
}
}
}

UserStore

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Authorization;
using Abp.Authorization.Roles;
using Abp.Authorization.Users;
using Abp.Configuration;
using Abp.Configuration.Startup;
using Abp.Dependency;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.Zero.Configuration;
using PM.Core.Authorization.Roles;
using PM.Core.MultiTenant;
using PM.Core.Users; namespace PM.Core.Authorization
{
public class LogInManager : AbpLogInManager<Tenant,Role,User>
{
public LogInManager(
UserManager userManager,
IMultiTenancyConfig multiTenancyConfig,
IRepository<Tenant> tenantRepository,
IUnitOfWorkManager unitOfWorkManager,
ISettingManager settingManager,
IRepository<UserLoginAttempt, long> userLoginAttemptRepository,
IUserManagementConfig userManagementConfig,
IIocResolver iocResolver,
RoleManager roleManager)
: base(
userManager,
multiTenancyConfig,
tenantRepository,
unitOfWorkManager,
settingManager,
userLoginAttemptRepository,
userManagementConfig,
iocResolver,
roleManager)
{
}
}
}

LogInManager

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Authorization;
using Abp.Authorization.Users;
using PM.Core.Authorization.Roles;
using PM.Core.Users; namespace PM.Core.Authorization
{
public class PermissionChecker:PermissionChecker<Role,User>
{
public PermissionChecker(UserManager userManager) : base(userManager)
{
}
}
}

PermissionChecker

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace PM.Core.Authorization
{
public class PermisstionNames
{
public const string Pages_Tenants = "Pages.Tenants";
public const string Pages_Users = "Pages.Users";
public const string Pages_Roles = "Pages.Roles";
}
}

PermisstionNames

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Authorization;
using Abp.Localization;
using Abp.Localization.Sources;
using Abp.MultiTenancy; namespace PM.Core.Authorization
{
public class PMProjectNameAuthorizationProvider: AuthorizationProvider
{
public override void SetPermissions(IPermissionDefinitionContext context)
{ context.CreatePermission(PermisstionNames.Pages_Users, L("Users"));
context.CreatePermission(PermisstionNames.Pages_Roles, L("Roles"));
context.CreatePermission(PermisstionNames.Pages_Tenants, L("Tenants"),multiTenancySides:MultiTenancySides.Host); } private static ILocalizableString L(string name)
{
return new LocalizableString(name, PMProjectNameConsts.LocalizationSourceName);
} }
}

PMProjectNameAuthorizationProvider

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Authorization.Users;
using Abp.Extensions;
using Microsoft.AspNet.Identity; namespace PM.Core.Users
{
public class User:AbpUser<User>
{
public const string DefaultPassword = "123qwe"; /// <summary>
/// 创建随机密码
/// </summary>
/// <returns></returns>
public static string CreateRandomPassword()
{
//截断16位
return Guid.NewGuid().ToString("N").Truncate();
} public static User CreateTenantAdminUser(int tenantId, string emailAddress, string password)
{
return new User()
{
TenantId = tenantId,
UserName = AdminUserName,
Name = AdminUserName,
Surname = AdminUserName,
EmailAddress = emailAddress,
Password = new PasswordHasher().HashPassword(password),
IsEmailConfirmed = true,
IsActive = true
};
} }
}

User

Configuration

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace PM.Core.Configuration
{
public class PMSettingNames
{
public const string UiTheme = "App.UiTheme";
}
}

PMSettingNames

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Configuration; namespace PM.Core.Configuration
{
public class PMSettingProvider:SettingProvider
{
public override IEnumerable<SettingDefinition> GetSettingDefinitions(SettingDefinitionProviderContext context)
{
return new[]
{
new SettingDefinition(PMSettingNames.UiTheme, "red",
scopes: SettingScopes.Application | SettingScopes.Tenant | SettingScopes.User,
isVisibleToClients: true)
};
}
}
}

PMSettingProvider

Editions

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Application.Editions;
using Abp.Application.Features;
using Abp.Domain.Repositories; namespace PM.Core.Editions
{
public class EditionManager:AbpEditionManager
{
public const string DefaultEditionName = "Standard"; public EditionManager(
IRepository<Edition> editionRepository,
IAbpZeroFeatureValueStore featureValueStore)
: base(editionRepository,
featureValueStore)
{
}
}
}

EditionManager

Features

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Application.Features;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.MultiTenancy;
using Abp.Runtime.Caching;
using PM.Core.MultiTenant;
using PM.Core.Users; namespace PM.Core.Features
{
public class FeaturesValueStore:AbpFeatureValueStore<Tenant,User>
{
public FeaturesValueStore(
ICacheManager cacheManager,
IRepository<TenantFeatureSetting, long> tenantFeatureRepository,
IRepository<Tenant> tenantRepository,
IRepository<EditionFeatureSetting, long> editionFeatureRepository,
IFeatureManager featureManager,
IUnitOfWorkManager unitOfWorkManager)
: base(
cacheManager,
tenantFeatureRepository,
tenantRepository,
editionFeatureRepository,
featureManager,
unitOfWorkManager)
{
}
}
}

FeaturesValueStore

Localization

<?xml version="1.0" encoding="utf-8" ?>
<localizationDictionary culture="zh-CN">
<texts>
<text name="Users" value="用户"/>
<text name="Roles" value="角色"/>
<text name="Tenants" value="租户"/>
</texts>
</localizationDictionary>

PMProjectName-zh-CN.xml

<?xml version="1.0" encoding="utf-8" ?>
<localizationDictionary culture="en">
<texts>
<text name="Users" value="Users"/>
<text name="Roles" value="Roles"/>
<text name="Tenants" value="Tenants"/>
</texts>
</localizationDictionary>

PMProjectName.xml

MultiTenant

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.MultiTenancy;
using PM.Core.Users; namespace PM.Core.MultiTenant
{
public class Tenant:AbpTenant<User>
{ }
}

Tenant

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Application.Editions;
using Abp.Application.Features;
using Abp.Domain.Repositories;
using Abp.MultiTenancy;
using PM.Core.Users; namespace PM.Core.MultiTenant
{
public class TenantManager:AbpTenantManager<Tenant,User>
{
public TenantManager(
IRepository<Tenant> tenantRepository,
IRepository<TenantFeatureSetting, long> tenantFeatureRepository,
AbpEditionManager editionManager,
IAbpZeroFeatureValueStore featureValueStore)
: base(tenantRepository,
tenantFeatureRepository,
editionManager,
featureValueStore)
{
}
}
}

TenantManager

Validation

using Abp.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks; namespace PM.Core.Validation
{
public class ValidationHelper
{
public const string EmailRegex = @"^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$"; public static bool IsEmail(string value)
{
if (value.IsNullOrEmpty())
{
return false;
} var regex = new Regex(EmailRegex);
return regex.IsMatch(value);
}
}
}

ValidationHelper

PMProjectNameConsts 公共常量类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace PM.Core
{
public class PMProjectNameConsts
{
public const string LocalizationSourceName = "PMProjectName";
public const bool MultiTenantEnabled = true;
}
}

PMProjectNameConsts

PMVersionHepler 版本帮助类

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Reflection.Extensions; namespace PM.Core
{
/// <summary>
/// 应用版本中心
/// </summary>
public class PMVersionHepler
{
/// <summary>
/// 获取应用程序当前版本
/// 显示在网页中
/// </summary>
public const string Version = "1.0.0.0"; /// <summary>
/// 获取应用程序最后一次发布的时间
/// 它显示在网页中
/// </summary>
public static DateTime ReleaseDate
{
get { return new FileInfo(typeof(PMVersionHepler).GetAssembly().Location).LastWriteTime; }
} }
}

PMVersionHepler

PMCoreModule模块类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Abp.Localization.Dictionaries;
using Abp.Localization.Dictionaries.Xml;
using Abp.Modules;
using Abp.Zero;
using Abp.Zero.Configuration;
using PM.Core.Authorization;
using PM.Core.Authorization.Roles;
using PM.Core.Configuration;
using PM.Core.MultiTenant;
using PM.Core.Users; namespace PM.Core
{
[DependsOn(typeof(AbpZeroCoreModule))]
public class PMCoreModule:AbpModule
{
public override void PreInitialize()
{
//如果当前用户未登录,请设置为true以启用保存审核日志。默认值:
Configuration.Auditing.IsEnabledForAnonymousUsers = true; //声明实体类型
Configuration.Modules.Zero().EntityTypes.Tenant = typeof (Tenant);
Configuration.Modules.Zero().EntityTypes.Role = typeof (Role);
Configuration.Modules.Zero().EntityTypes.User = typeof (User); //开启多租户
Configuration.MultiTenancy.IsEnabled = PMProjectNameConsts.MultiTenantEnabled; //添加删除本地化源
Configuration.Localization.Sources.Add(
new DictionaryBasedLocalizationSource(PMProjectNameConsts.LocalizationSourceName,
new XmlEmbeddedFileLocalizationDictionaryProvider(Assembly.GetExecutingAssembly(),
"PM.Localization.Source"))); //设置静态角色
PMRoleConfig.Configure(Configuration.Modules.Zero().RoleManagement); //初始化权限
Configuration.Authorization.Providers.Add<PMProjectNameAuthorizationProvider>(); //初始化设置
Configuration.Settings.Providers.Add<PMSettingProvider>(); } public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
}
}

PMCoreModule

三、PM.EntityFramework

1,NuGet安装Abp.Zero2.1.3、Abp.Zero.EntityFramework2.13

2,基本结构

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Domain.Entities;
using Abp.EntityFramework;
using Abp.EntityFramework.Repositories; namespace PM.EntityFramework.EntityFramework.Repositories
{
public class PMRepositoryBase<TEntity, TPrimaryKey> : EfRepositoryBase<PMDBContext, TEntity, TPrimaryKey>
where TEntity:class,IEntity<TPrimaryKey>
{
public PMRepositoryBase(IDbContextProvider<PMDBContext> dbContextProvider) : base(dbContextProvider)
{
} //为所有存储库添加常用方法
} public class PMRepositoryBase<TEntity> : PMRepositoryBase<TEntity, int>
where TEntity : class, IEntity<int>
{
public PMRepositoryBase(IDbContextProvider<PMDBContext> dbContextProvider) : base(dbContextProvider)
{
}
//不要在这里添加任何方法,添加到上面的类(因为它继承它)
} }

PMRepositoryBase

using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Zero.EntityFramework;
using PM.Core.Authorization.Roles;
using PM.Core.MultiTenant;
using PM.Core.Users; namespace PM.EntityFramework.EntityFramework
{
public class PMDBContext:AbpZeroDbContext<Tenant,Role,User>
{
/// <summary>
/// 将“默认”设置为基类可帮助我们在Package Manager Console上执行迁移命令时使用。
/// 但是,在运行EF的Migrate.exe时可能会导致问题。 如果要在命令行上应用迁移,请不要将连接字符串名称传递给基类。 ABP工作方式。
/// </summary>
public PMDBContext() : base("Default")
{
} /// <summary>
/// ABP使用此构造函数传递PMDBContext.PreInitialize中定义的连接字符串。
/// 注意,实际上你不会直接创建一个PMDBContext的实例,因为ABP自动处理它。
/// </summary>
/// <param name="nameOrnameOrConnectionString"></param>
public PMDBContext(string nameOrnameOrConnectionString) : base(nameOrnameOrConnectionString)
{
} /// <summary>
/// 这个构造函数用于测试
/// </summary>
/// <param name="existingConnection"></param>
public PMDBContext(DbConnection existingConnection) : base(existingConnection,false)
{
} public PMDBContext(DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection)
{
} }
}

PMDBContext

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Application.Editions;
using PM.Core.Editions;
using PM.EntityFramework.EntityFramework; namespace PM.EntityFramework.Migrations.SeedData
{ /// <summary>
/// 默认版本创建者
/// </summary>
public class DefaultEditionsCreator
{
private readonly PMDBContext _context; public DefaultEditionsCreator(PMDBContext context)
{
_context = context;
} public void Create()
{
CreateEdtions();
} private void CreateEdtions()
{
var defaultEdtion = _context.Editions.FirstOrDefault(e => e.Name == EditionManager.DefaultEditionName);
if (defaultEdtion == null)
{
defaultEdtion = new Edition()
{
Name = EditionManager.DefaultEditionName,
DisplayName = EditionManager.DefaultEditionName
};
_context.Editions.Add(defaultEdtion);
_context.SaveChanges(); //TODO:如果需要,可以在标准版中添加所需的功能!
}
} }
}

DefaultEditionsCreator

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Localization;
using PM.EntityFramework.EntityFramework; namespace PM.EntityFramework.Migrations.SeedData
{
/// <summary>
/// 默认语言创建者
/// </summary>
public class DefaultLanguagesCreator
{
public static List<ApplicationLanguage> InitialLanguages { get; private set; }
private readonly PMDBContext _context; static DefaultLanguagesCreator()
{
InitialLanguages = new List<ApplicationLanguage>
{
new ApplicationLanguage(null, "en", "English", "famfamfam-flags gb"),
new ApplicationLanguage(null, "tr", "Türkçe", "famfamfam-flags tr"),
new ApplicationLanguage(null, "zh-CN", "简体中文", "famfamfam-flags cn"),
new ApplicationLanguage(null, "pt-BR", "Português-BR", "famfamfam-flags br"),
new ApplicationLanguage(null, "es", "Español", "famfamfam-flags es"),
new ApplicationLanguage(null, "fr", "Français", "famfamfam-flags fr"),
new ApplicationLanguage(null, "it", "Italiano", "famfamfam-flags it"),
new ApplicationLanguage(null, "ja", "日本語", "famfamfam-flags jp"),
new ApplicationLanguage(null, "nl-NL", "Nederlands", "famfamfam-flags nl"),
new ApplicationLanguage(null, "lt", "Lietuvos", "famfamfam-flags lt"),
new ApplicationLanguage(null, "vn", "Vietnamese", "famfamfam-flags vn")
};
} public DefaultLanguagesCreator(PMDBContext context)
{
_context = context;
} public void Create()
{
CreateLanguages();
} private void CreateLanguages()
{
foreach (var applicationLanguage in InitialLanguages)
{
AddLanguageIfNotExists(applicationLanguage);
}
} private void AddLanguageIfNotExists(ApplicationLanguage language)
{
if (_context.Languages.Any(l => l.TenantId == language.TenantId && l.Name == language.Name))
{
return;
}
_context.Languages.Add(language);
_context.SaveChanges();
} }
}

DefaultLanguagesCreator

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Configuration;
using Abp.Localization;
using Abp.Net.Mail;
using PM.EntityFramework.EntityFramework; namespace PM.EntityFramework.Migrations.SeedData
{
/// <summary>
/// 默认设置创建者
/// </summary>
public class DefaultSettingsCreator
{
private readonly PMDBContext _context; public DefaultSettingsCreator(PMDBContext context)
{
_context = context;
} public void Create()
{
//邮箱
AddSettingIfNotExists(EmailSettingNames.DefaultFromAddress, "qq962410314@163.com");
AddSettingIfNotExists(EmailSettingNames.DefaultFromDisplayName, "qq962410314@163.com"); //语言
AddSettingIfNotExists(LocalizationSettingNames.DefaultLanguage, "zh-CN");
} private void AddSettingIfNotExists(string name, string value, int? tenantId = null)
{
if (_context.Settings.Any(s => s.Name == name && s.TenantId == tenantId && s.UserId == null))
{
return;
}
_context.Settings.Add(new Setting(tenantId, null, name, value));
_context.SaveChanges();
} }
}

DefaultSettingsCreator

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PM.Core.MultiTenant;
using PM.EntityFramework.EntityFramework; namespace PM.EntityFramework.Migrations.SeedData
{
/// <summary>
/// 默认租户创建者
/// </summary>
public class DefaultTenantCreator
{
private readonly PMDBContext _context; public DefaultTenantCreator(PMDBContext context)
{
_context = context;
} public void Create()
{
CreateUserAndRoles();
} private void CreateUserAndRoles()
{
//默认租户
var defaultTenant = _context.Tenants.FirstOrDefault(t => t.TenancyName == Tenant.DefaultTenantName);
if (defaultTenant == null)
{
_context.Tenants.Add(new Tenant()
{
TenancyName = Tenant.DefaultTenantName,
Name = Tenant.DefaultTenantName
});
_context.SaveChanges();
} } }
}

DefaultTenantCreator

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Authorization;
using Abp.Authorization.Roles;
using Abp.Authorization.Users;
using Abp.MultiTenancy;
using Microsoft.AspNet.Identity;
using PM.Core.Authorization;
using PM.Core.Authorization.Roles;
using PM.Core.Users;
using PM.EntityFramework.EntityFramework; namespace PM.EntityFramework.Migrations.SeedData
{
/// <summary>
/// 主机admin创建者
/// </summary>
public class HostRoleAndUserCreator
{
private readonly PMDBContext _context; public HostRoleAndUserCreator(PMDBContext context)
{
_context = context;
} public void Create()
{
CreateHostRoleAndUsers();
} private void CreateHostRoleAndUsers()
{
//主机角色
var adminRoleForHost= _context.Roles.FirstOrDefault(r => r.TenantId == null && r.Name == StaticRoleNames.Host.Admin);
if (adminRoleForHost == null)
{
adminRoleForHost = _context.Roles.Add(new Role()
{
Name = StaticRoleNames.Host.Admin,
DisplayName = StaticRoleNames.Host.Admin,
IsStatic = true
});
_context.SaveChanges(); //授予所有租户权限
var permisstions = PermissionFinder.GetAllPermissions(new PMProjectNameAuthorizationProvider())
.Where(p => p.MultiTenancySides.HasFlag(MultiTenancySides.Host))
.ToList();
foreach (var permisstion in permisstions)
{
_context.Permissions.Add(new RolePermissionSetting()
{
Name = permisstion.Name,
IsGranted = true,
RoleId = adminRoleForHost.Id
});
}
_context.SaveChanges();
} //主机admin
var adminUserForHost =
_context.Users.FirstOrDefault(u => u.TenantId == null && u.UserName == User.AdminUserName);
if (adminUserForHost == null)
{
adminUserForHost = _context.Users.Add(new User()
{
UserName = User.AdminUserName,
Name = "System",
Surname = "Administrator",
EmailAddress = "qq962410314@163.com",
IsEmailConfirmed = true,
Password = new PasswordHasher().HashPassword(User.DefaultPassword)
}); _context.SaveChanges();
_context.UserRoles.Add(new UserRole(null, adminUserForHost.Id, adminRoleForHost.Id));
_context.SaveChanges();
} } }
}

HostRoleAndUserCreator

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EntityFramework.DynamicFilters;
using PM.EntityFramework.EntityFramework; namespace PM.EntityFramework.Migrations.SeedData
{
/// <summary>
/// 初始化主机数据库提供者
/// </summary>
public class InitialHostDbBuilder
{
private readonly PMDBContext _context; public InitialHostDbBuilder(PMDBContext context)
{
_context = context;
} public void Create()
{
//禁用所有过滤器
_context.DisableAllFilters(); new DefaultEditionsCreator(_context).Create();
new DefaultLanguagesCreator(_context).Create();
new HostRoleAndUserCreator(_context).Create();
new DefaultSettingsCreator(_context).Create(); } }
}

InitialHostDbBuilder

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Authorization;
using Abp.Authorization.Roles;
using Abp.Authorization.Users;
using Abp.MultiTenancy;
using PM.Core.Authorization;
using PM.Core.Authorization.Roles;
using PM.Core.Users;
using PM.EntityFramework.EntityFramework; namespace PM.EntityFramework.Migrations.SeedData
{
/// <summary>
/// 租户admin创建者
/// </summary>
public class TenantRoleAndUserBuilder
{
private readonly PMDBContext _context;
private readonly int _tenantId; public TenantRoleAndUserBuilder(PMDBContext context, int tenantId)
{
_context = context;
_tenantId = tenantId;
} public void Create()
{
CreateRolesAndUsers();
} private void CreateRolesAndUsers()
{ //租户角色
var adminRole =
_context.Roles.FirstOrDefault(r => r.TenantId == _tenantId && r.Name == StaticRoleNames.Tenants.Admin);
if (adminRole==null)
{
adminRole =
_context.Roles.Add(new Role(_tenantId, StaticRoleNames.Tenants.Admin, StaticRoleNames.Tenants.Admin)
{
IsStatic = true
});
_context.SaveChanges(); //授予管理员角色的所有权限
var permisstions = PermissionFinder.GetAllPermissions(new PMProjectNameAuthorizationProvider())
.Where(p => p.MultiTenancySides.HasFlag(MultiTenancySides.Tenant))
.ToList();
foreach (var permisstion in permisstions)
{
_context.Permissions.Add(new RolePermissionSetting()
{
TenantId = _tenantId,
Name = permisstion.Name,
IsGranted = true,
RoleId = adminRole.Id
});
}
_context.SaveChanges();
} //租户admin
var adminUser =
_context.Users.FirstOrDefault(u => u.TenantId == _tenantId && u.UserName == User.AdminUserName);
if (adminUser == null)
{
adminUser = User.CreateTenantAdminUser(_tenantId, "qq962410314@163.com", User.DefaultPassword);
adminUser.IsEmailConfirmed = true;
adminUser.IsActive = true; _context.Users.Add(adminUser);
_context.SaveChanges(); _context.UserRoles.Add(new UserRole(_tenantId, adminUser.Id, adminRole.Id));
_context.SaveChanges();
}
}
}
}

TenantRoleAndUserBuilder

namespace PM.EntityFramework.Migrations
{
using System;
using System.Collections.Generic;
using System.Data.Entity.Infrastructure.Annotations;
using System.Data.Entity.Migrations; public partial class init : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.AbpAuditLogs",
c => new
{
Id = c.Long(nullable: false, identity: true),
TenantId = c.Int(),
UserId = c.Long(),
ServiceName = c.String(maxLength: ),
MethodName = c.String(maxLength: ),
Parameters = c.String(maxLength: ),
ExecutionTime = c.DateTime(nullable: false),
ExecutionDuration = c.Int(nullable: false),
ClientIpAddress = c.String(maxLength: ),
ClientName = c.String(maxLength: ),
BrowserInfo = c.String(maxLength: ),
Exception = c.String(maxLength: ),
ImpersonatorUserId = c.Long(),
ImpersonatorTenantId = c.Int(),
CustomData = c.String(maxLength: ),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_AuditLog_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id); CreateTable(
"dbo.AbpBackgroundJobs",
c => new
{
Id = c.Long(nullable: false, identity: true),
JobType = c.String(nullable: false, maxLength: ),
JobArgs = c.String(nullable: false),
TryCount = c.Short(nullable: false),
NextTryTime = c.DateTime(nullable: false),
LastTryTime = c.DateTime(),
IsAbandoned = c.Boolean(nullable: false),
Priority = c.Byte(nullable: false),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
})
.PrimaryKey(t => t.Id)
.Index(t => new { t.IsAbandoned, t.NextTryTime }); CreateTable(
"dbo.AbpFeatures",
c => new
{
Id = c.Long(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: ),
Value = c.String(nullable: false, maxLength: ),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
EditionId = c.Int(),
TenantId = c.Int(),
Discriminator = c.String(nullable: false, maxLength: ),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_TenantFeatureSetting_MustHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AbpEditions", t => t.EditionId, cascadeDelete: true)
.Index(t => t.EditionId); CreateTable(
"dbo.AbpEditions",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: ),
DisplayName = c.String(nullable: false, maxLength: ),
IsDeleted = c.Boolean(nullable: false),
DeleterUserId = c.Long(),
DeletionTime = c.DateTime(),
LastModificationTime = c.DateTime(),
LastModifierUserId = c.Long(),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_Edition_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id); CreateTable(
"dbo.AbpLanguages",
c => new
{
Id = c.Int(nullable: false, identity: true),
TenantId = c.Int(),
Name = c.String(nullable: false, maxLength: ),
DisplayName = c.String(nullable: false, maxLength: ),
Icon = c.String(maxLength: ),
IsDisabled = c.Boolean(nullable: false),
IsDeleted = c.Boolean(nullable: false),
DeleterUserId = c.Long(),
DeletionTime = c.DateTime(),
LastModificationTime = c.DateTime(),
LastModifierUserId = c.Long(),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_ApplicationLanguage_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
{ "DynamicFilter_ApplicationLanguage_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id); CreateTable(
"dbo.AbpLanguageTexts",
c => new
{
Id = c.Long(nullable: false, identity: true),
TenantId = c.Int(),
LanguageName = c.String(nullable: false, maxLength: ),
Source = c.String(nullable: false, maxLength: ),
Key = c.String(nullable: false, maxLength: ),
Value = c.String(nullable: false),
LastModificationTime = c.DateTime(),
LastModifierUserId = c.Long(),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_ApplicationLanguageText_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id); CreateTable(
"dbo.AbpNotifications",
c => new
{
Id = c.Guid(nullable: false),
NotificationName = c.String(nullable: false, maxLength: ),
Data = c.String(),
DataTypeName = c.String(maxLength: ),
EntityTypeName = c.String(maxLength: ),
EntityTypeAssemblyQualifiedName = c.String(maxLength: ),
EntityId = c.String(maxLength: ),
Severity = c.Byte(nullable: false),
UserIds = c.String(),
ExcludedUserIds = c.String(),
TenantIds = c.String(),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
})
.PrimaryKey(t => t.Id); CreateTable(
"dbo.AbpNotificationSubscriptions",
c => new
{
Id = c.Guid(nullable: false),
TenantId = c.Int(),
UserId = c.Long(nullable: false),
NotificationName = c.String(maxLength: ),
EntityTypeName = c.String(maxLength: ),
EntityTypeAssemblyQualifiedName = c.String(maxLength: ),
EntityId = c.String(maxLength: ),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_NotificationSubscriptionInfo_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id)
.Index(t => new { t.NotificationName, t.EntityTypeName, t.EntityId, t.UserId }); CreateTable(
"dbo.AbpOrganizationUnits",
c => new
{
Id = c.Long(nullable: false, identity: true),
TenantId = c.Int(),
ParentId = c.Long(),
Code = c.String(nullable: false, maxLength: ),
DisplayName = c.String(nullable: false, maxLength: ),
IsDeleted = c.Boolean(nullable: false),
DeleterUserId = c.Long(),
DeletionTime = c.DateTime(),
LastModificationTime = c.DateTime(),
LastModifierUserId = c.Long(),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_OrganizationUnit_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
{ "DynamicFilter_OrganizationUnit_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AbpOrganizationUnits", t => t.ParentId)
.Index(t => t.ParentId); CreateTable(
"dbo.AbpPermissions",
c => new
{
Id = c.Long(nullable: false, identity: true),
TenantId = c.Int(),
Name = c.String(nullable: false, maxLength: ),
IsGranted = c.Boolean(nullable: false),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
RoleId = c.Int(),
UserId = c.Long(),
Discriminator = c.String(nullable: false, maxLength: ),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_PermissionSetting_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
{ "DynamicFilter_RolePermissionSetting_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
{ "DynamicFilter_UserPermissionSetting_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AbpUsers", t => t.UserId, cascadeDelete: true)
.ForeignKey("dbo.AbpRoles", t => t.RoleId, cascadeDelete: true)
.Index(t => t.RoleId)
.Index(t => t.UserId); CreateTable(
"dbo.AbpRoles",
c => new
{
Id = c.Int(nullable: false, identity: true),
Description = c.String(),
TenantId = c.Int(),
Name = c.String(nullable: false, maxLength: ),
DisplayName = c.String(nullable: false, maxLength: ),
IsStatic = c.Boolean(nullable: false),
IsDefault = c.Boolean(nullable: false),
IsDeleted = c.Boolean(nullable: false),
DeleterUserId = c.Long(),
DeletionTime = c.DateTime(),
LastModificationTime = c.DateTime(),
LastModifierUserId = c.Long(),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_Role_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
{ "DynamicFilter_Role_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AbpUsers", t => t.CreatorUserId)
.ForeignKey("dbo.AbpUsers", t => t.DeleterUserId)
.ForeignKey("dbo.AbpUsers", t => t.LastModifierUserId)
.Index(t => t.DeleterUserId)
.Index(t => t.LastModifierUserId)
.Index(t => t.CreatorUserId); CreateTable(
"dbo.AbpUsers",
c => new
{
Id = c.Long(nullable: false, identity: true),
AuthenticationSource = c.String(maxLength: ),
UserName = c.String(nullable: false, maxLength: ),
TenantId = c.Int(),
EmailAddress = c.String(nullable: false, maxLength: ),
Name = c.String(nullable: false, maxLength: ),
Surname = c.String(nullable: false, maxLength: ),
Password = c.String(nullable: false, maxLength: ),
EmailConfirmationCode = c.String(maxLength: ),
PasswordResetCode = c.String(maxLength: ),
LockoutEndDateUtc = c.DateTime(),
AccessFailedCount = c.Int(nullable: false),
IsLockoutEnabled = c.Boolean(nullable: false),
PhoneNumber = c.String(),
IsPhoneNumberConfirmed = c.Boolean(nullable: false),
SecurityStamp = c.String(),
IsTwoFactorEnabled = c.Boolean(nullable: false),
IsEmailConfirmed = c.Boolean(nullable: false),
IsActive = c.Boolean(nullable: false),
LastLoginTime = c.DateTime(),
IsDeleted = c.Boolean(nullable: false),
DeleterUserId = c.Long(),
DeletionTime = c.DateTime(),
LastModificationTime = c.DateTime(),
LastModifierUserId = c.Long(),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_User_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
{ "DynamicFilter_User_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AbpUsers", t => t.CreatorUserId)
.ForeignKey("dbo.AbpUsers", t => t.DeleterUserId)
.ForeignKey("dbo.AbpUsers", t => t.LastModifierUserId)
.Index(t => t.DeleterUserId)
.Index(t => t.LastModifierUserId)
.Index(t => t.CreatorUserId); CreateTable(
"dbo.AbpUserClaims",
c => new
{
Id = c.Long(nullable: false, identity: true),
TenantId = c.Int(),
UserId = c.Long(nullable: false),
ClaimType = c.String(),
ClaimValue = c.String(),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserClaim_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AbpUsers", t => t.UserId, cascadeDelete: true)
.Index(t => t.UserId); CreateTable(
"dbo.AbpUserLogins",
c => new
{
Id = c.Long(nullable: false, identity: true),
TenantId = c.Int(),
UserId = c.Long(nullable: false),
LoginProvider = c.String(nullable: false, maxLength: ),
ProviderKey = c.String(nullable: false, maxLength: ),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserLogin_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AbpUsers", t => t.UserId, cascadeDelete: true)
.Index(t => t.UserId); CreateTable(
"dbo.AbpUserRoles",
c => new
{
Id = c.Long(nullable: false, identity: true),
TenantId = c.Int(),
UserId = c.Long(nullable: false),
RoleId = c.Int(nullable: false),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserRole_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AbpUsers", t => t.UserId, cascadeDelete: true)
.Index(t => t.UserId); CreateTable(
"dbo.AbpSettings",
c => new
{
Id = c.Long(nullable: false, identity: true),
TenantId = c.Int(),
UserId = c.Long(),
Name = c.String(nullable: false, maxLength: ),
Value = c.String(maxLength: ),
LastModificationTime = c.DateTime(),
LastModifierUserId = c.Long(),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_Setting_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AbpUsers", t => t.UserId)
.Index(t => t.UserId); CreateTable(
"dbo.AbpTenantNotifications",
c => new
{
Id = c.Guid(nullable: false),
TenantId = c.Int(),
NotificationName = c.String(nullable: false, maxLength: ),
Data = c.String(),
DataTypeName = c.String(maxLength: ),
EntityTypeName = c.String(maxLength: ),
EntityTypeAssemblyQualifiedName = c.String(maxLength: ),
EntityId = c.String(maxLength: ),
Severity = c.Byte(nullable: false),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_TenantNotificationInfo_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id); CreateTable(
"dbo.AbpTenants",
c => new
{
Id = c.Int(nullable: false, identity: true),
EditionId = c.Int(),
Name = c.String(nullable: false, maxLength: ),
TenancyName = c.String(nullable: false, maxLength: ),
ConnectionString = c.String(maxLength: ),
IsActive = c.Boolean(nullable: false),
IsDeleted = c.Boolean(nullable: false),
DeleterUserId = c.Long(),
DeletionTime = c.DateTime(),
LastModificationTime = c.DateTime(),
LastModifierUserId = c.Long(),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_Tenant_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.AbpUsers", t => t.CreatorUserId)
.ForeignKey("dbo.AbpUsers", t => t.DeleterUserId)
.ForeignKey("dbo.AbpEditions", t => t.EditionId)
.ForeignKey("dbo.AbpUsers", t => t.LastModifierUserId)
.Index(t => t.EditionId)
.Index(t => t.DeleterUserId)
.Index(t => t.LastModifierUserId)
.Index(t => t.CreatorUserId); CreateTable(
"dbo.AbpUserAccounts",
c => new
{
Id = c.Long(nullable: false, identity: true),
TenantId = c.Int(),
UserId = c.Long(nullable: false),
UserLinkId = c.Long(),
UserName = c.String(),
EmailAddress = c.String(),
LastLoginTime = c.DateTime(),
IsDeleted = c.Boolean(nullable: false),
DeleterUserId = c.Long(),
DeletionTime = c.DateTime(),
LastModificationTime = c.DateTime(),
LastModifierUserId = c.Long(),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserAccount_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id); CreateTable(
"dbo.AbpUserLoginAttempts",
c => new
{
Id = c.Long(nullable: false, identity: true),
TenantId = c.Int(),
TenancyName = c.String(maxLength: ),
UserId = c.Long(),
UserNameOrEmailAddress = c.String(maxLength: ),
ClientIpAddress = c.String(maxLength: ),
ClientName = c.String(maxLength: ),
BrowserInfo = c.String(maxLength: ),
Result = c.Byte(nullable: false),
CreationTime = c.DateTime(nullable: false),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserLoginAttempt_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id)
.Index(t => new { t.UserId, t.TenantId })
.Index(t => new { t.TenancyName, t.UserNameOrEmailAddress, t.Result }); CreateTable(
"dbo.AbpUserNotifications",
c => new
{
Id = c.Guid(nullable: false),
TenantId = c.Int(),
UserId = c.Long(nullable: false),
TenantNotificationId = c.Guid(nullable: false),
State = c.Int(nullable: false),
CreationTime = c.DateTime(nullable: false),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserNotificationInfo_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id)
.Index(t => new { t.UserId, t.State, t.CreationTime }); CreateTable(
"dbo.AbpUserOrganizationUnits",
c => new
{
Id = c.Long(nullable: false, identity: true),
TenantId = c.Int(),
UserId = c.Long(nullable: false),
OrganizationUnitId = c.Long(nullable: false),
CreationTime = c.DateTime(nullable: false),
CreatorUserId = c.Long(),
},
annotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserOrganizationUnit_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
})
.PrimaryKey(t => t.Id); } public override void Down()
{
DropForeignKey("dbo.AbpTenants", "LastModifierUserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpTenants", "EditionId", "dbo.AbpEditions");
DropForeignKey("dbo.AbpTenants", "DeleterUserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpTenants", "CreatorUserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpPermissions", "RoleId", "dbo.AbpRoles");
DropForeignKey("dbo.AbpRoles", "LastModifierUserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpRoles", "DeleterUserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpRoles", "CreatorUserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpSettings", "UserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpUserRoles", "UserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpPermissions", "UserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpUserLogins", "UserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpUsers", "LastModifierUserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpUsers", "DeleterUserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpUsers", "CreatorUserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpUserClaims", "UserId", "dbo.AbpUsers");
DropForeignKey("dbo.AbpOrganizationUnits", "ParentId", "dbo.AbpOrganizationUnits");
DropForeignKey("dbo.AbpFeatures", "EditionId", "dbo.AbpEditions");
DropIndex("dbo.AbpUserNotifications", new[] { "UserId", "State", "CreationTime" });
DropIndex("dbo.AbpUserLoginAttempts", new[] { "TenancyName", "UserNameOrEmailAddress", "Result" });
DropIndex("dbo.AbpUserLoginAttempts", new[] { "UserId", "TenantId" });
DropIndex("dbo.AbpTenants", new[] { "CreatorUserId" });
DropIndex("dbo.AbpTenants", new[] { "LastModifierUserId" });
DropIndex("dbo.AbpTenants", new[] { "DeleterUserId" });
DropIndex("dbo.AbpTenants", new[] { "EditionId" });
DropIndex("dbo.AbpSettings", new[] { "UserId" });
DropIndex("dbo.AbpUserRoles", new[] { "UserId" });
DropIndex("dbo.AbpUserLogins", new[] { "UserId" });
DropIndex("dbo.AbpUserClaims", new[] { "UserId" });
DropIndex("dbo.AbpUsers", new[] { "CreatorUserId" });
DropIndex("dbo.AbpUsers", new[] { "LastModifierUserId" });
DropIndex("dbo.AbpUsers", new[] { "DeleterUserId" });
DropIndex("dbo.AbpRoles", new[] { "CreatorUserId" });
DropIndex("dbo.AbpRoles", new[] { "LastModifierUserId" });
DropIndex("dbo.AbpRoles", new[] { "DeleterUserId" });
DropIndex("dbo.AbpPermissions", new[] { "UserId" });
DropIndex("dbo.AbpPermissions", new[] { "RoleId" });
DropIndex("dbo.AbpOrganizationUnits", new[] { "ParentId" });
DropIndex("dbo.AbpNotificationSubscriptions", new[] { "NotificationName", "EntityTypeName", "EntityId", "UserId" });
DropIndex("dbo.AbpFeatures", new[] { "EditionId" });
DropIndex("dbo.AbpBackgroundJobs", new[] { "IsAbandoned", "NextTryTime" });
DropTable("dbo.AbpUserOrganizationUnits",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserOrganizationUnit_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpUserNotifications",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserNotificationInfo_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpUserLoginAttempts",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserLoginAttempt_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpUserAccounts",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserAccount_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpTenants",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_Tenant_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpTenantNotifications",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_TenantNotificationInfo_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpSettings",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_Setting_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpUserRoles",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserRole_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpUserLogins",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserLogin_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpUserClaims",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_UserClaim_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpUsers",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_User_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
{ "DynamicFilter_User_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpRoles",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_Role_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
{ "DynamicFilter_Role_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpPermissions",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_PermissionSetting_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
{ "DynamicFilter_RolePermissionSetting_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
{ "DynamicFilter_UserPermissionSetting_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpOrganizationUnits",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_OrganizationUnit_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
{ "DynamicFilter_OrganizationUnit_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpNotificationSubscriptions",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_NotificationSubscriptionInfo_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpNotifications");
DropTable("dbo.AbpLanguageTexts",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_ApplicationLanguageText_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpLanguages",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_ApplicationLanguage_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
{ "DynamicFilter_ApplicationLanguage_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpEditions",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_Edition_SoftDelete", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpFeatures",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_TenantFeatureSetting_MustHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
DropTable("dbo.AbpBackgroundJobs");
DropTable("dbo.AbpAuditLogs",
removedAnnotations: new Dictionary<string, object>
{
{ "DynamicFilter_AuditLog_MayHaveTenant", "EntityFramework.DynamicFilters.DynamicFilterDefinition" },
});
}
}
}

init

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Dependency;
using Abp.Domain.Uow;
using Abp.MultiTenancy;
using Abp.Zero.EntityFramework;
using PM.EntityFramework.EntityFramework; namespace PM.EntityFramework.Migrations
{
/// <summary>
/// 执行数据库迁移
/// </summary>
public class AbpZeroDbMigrator : AbpZeroDbMigrator<PMDBContext, Configuration>
{
public AbpZeroDbMigrator(IUnitOfWorkManager unitOfWorkManager,
IDbPerTenantConnectionStringResolver connectionStringResolver, IIocResolver iocResolver)
: base(unitOfWorkManager, connectionStringResolver, iocResolver)
{
}
}
}

AbpZeroDbMigrator

using Abp.MultiTenancy;
using Abp.Zero.EntityFramework;
using EntityFramework.DynamicFilters;
using PM.EntityFramework.Migrations.SeedData; namespace PM.EntityFramework.Migrations
{
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq; public sealed class Configuration : DbMigrationsConfiguration<PM.EntityFramework.EntityFramework.PMDBContext>,IMultiTenantSeed
{
public AbpTenantBase Tenant { get; set; } public Configuration()
{
AutomaticMigrationsEnabled = false;
ContextKey = "PM";
} //Seed() 方法会在你每次你执行 Update-Database 指令时被呼叫一次
protected override void Seed(PM.EntityFramework.EntityFramework.PMDBContext context)
{
//禁用所有过滤
context.DisableAllFilters();
if (Tenant == null)
{
//主机种子
new InitialHostDbBuilder(context).Create(); //默认租户种子
new DefaultTenantCreator(context).Create();
new TenantRoleAndUserBuilder(context, ).Create();
}
else
{
//您可以为租户数据库添加种子并使用租户属性...
}
context.SaveChanges();
} }
}

Configuration

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Abp.Modules;
using Abp.Zero.EntityFramework;
using PM.Core;
using PM.EntityFramework.EntityFramework; namespace PM.EntityFramework
{
[DependsOn(typeof(PMCoreModule),typeof(AbpZeroEntityFrameworkModule))]
public class PMDataModule:AbpModule
{
public override void PreInitialize()
{
Database.SetInitializer(new CreateDatabaseIfNotExists<PMDBContext>());
Configuration.DefaultNameOrConnectionString = "Default";
} public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
}
}
}

PMDataModule

四、PM.Application

1,NuGet安装Abp.Zero2.1.3、Abp.AutoMapper2.1.3

程序集引用:System.ComponentModel.DataAnnotations

2,基本结构

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Application.Services;
using Abp.IdentityFramework;
using Abp.Runtime.Session;
using Microsoft.AspNet.Identity;
using PM.Core;
using PM.Core.MultiTenant;
using PM.Core.Users; namespace PM.Application
{
public class PMAppServiceBase:ApplicationService
{
public TenantManager TenantManager { get; set; }
public UserManager UserManager { get; set; } protected PMAppServiceBase()
{
LocalizationSourceName = PMProjectNameConsts.LocalizationSourceName;
} protected virtual Task<User> GetCurrentUserAsync()
{
var user= UserManager.FindByIdAsync(AbpSession.GetUserId());
if (user == null)
{
throw new ApplicationException("目前没有用户!");
}
return user;
} protected virtual Task<Tenant> GetCurrentTenantAsync()
{
return TenantManager.GetByIdAsync(AbpSession.GetTenantId());
} protected virtual void CheckErrors(IdentityResult identityResult)
{
identityResult.CheckErrors(LocalizationManager);
} }
}

PMAppServiceBase

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Abp.Authorization;
using Abp.Authorization.Roles;
using Abp.AutoMapper;
using Abp.Modules;
using PM.Application.Roles.Dto;
using PM.Application.Users.Dto;
using PM.Core;
using PM.Core.Authorization.Roles;
using PM.Core.Users; namespace PM.Application
{
[DependsOn(typeof(PMCoreModule),typeof(AbpAutoMapperModule))]
public class PMApplicationModule:AbpModule
{
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
Configuration.Modules.AbpAutoMapper().Configurators.Add(cfg =>
{
// Role and permission
cfg.CreateMap<Permission, string>().ConvertUsing(r => r.Name);
cfg.CreateMap<RolePermissionSetting, string>().ConvertUsing(r => r.Name); cfg.CreateMap<CreateRoleDto, Role>().ForMember(x => x.Permissions, opt => opt.Ignore());
cfg.CreateMap<RoleDto, Role>().ForMember(x => x.Permissions, opt => opt.Ignore()); cfg.CreateMap<UserDto, User>();
cfg.CreateMap<UserDto, User>().ForMember(x => x.Roles, opt => opt.Ignore()); cfg.CreateMap<CreateUserDto, User>();
cfg.CreateMap<CreateUserDto, User>().ForMember(x => x.Roles, opt => opt.Ignore());
}); }
}
}

PMApplicationModule

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Zero.Configuration;
using PM.Application.Authorization.Accounts.Dto;
using PM.Core.Authorization.Users;
using Abp.Configuration;
namespace PM.Application.Authorization.Accounts
{
public class AccountAppService:PMAppServiceBase,IAccountAppService
{
private readonly UserRegistrationManager _userRegistrationManager; public AccountAppService(UserRegistrationManager userRegistrationManager)
{
_userRegistrationManager = userRegistrationManager;
} public async Task<IsTenantAvaliableOutput> IsTenantAvaliable(IsTenantAvaliableInput input)
{
var tenant = await TenantManager.FindByTenancyNameAsync(input.TenantName);
if (tenant == null)
{
return new IsTenantAvaliableOutput(TenantAvaliablityState.NotFound);
}
if (!tenant.IsActive)
{
return new IsTenantAvaliableOutput(TenantAvaliablityState.InActive);
}
return new IsTenantAvaliableOutput(TenantAvaliablityState.Avaliable, tenant.Id);
} public async Task<RegisterOutput> Register(RegisterInput input)
{
var user =await _userRegistrationManager.RegisterAsync(input.Name, input.Surname, input.EmailAddress,
input.UserName,
input.Password, false); //电子邮件确认需要登录
var isEmailConfirmationRequiredForLogin = await SettingManager.GetSettingValueAsync<bool>(
AbpZeroSettingNames.UserManagement.IsEmailConfirmationRequiredForLogin); return new RegisterOutput()
{
CanLogin = user.IsActive && (user.IsEmailConfirmed || !isEmailConfirmationRequiredForLogin)
};
}
}
}

AccountAppService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Runtime.Session;
using PM.Application.Configuration.Dto;
using PM.Core.Configuration; namespace PM.Application.Configuration
{
public class ConfigurationAppService : PMAppServiceBase, IConfigurationAppService
{
public async Task ChangeUiTheme(ChangeUiThemeInput input)
{
await
SettingManager.ChangeSettingForUserAsync(AbpSession.ToUserIdentifier(), PMSettingNames.UiTheme,
input.Theme);
}
}
}

ConfigurationAppService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Application.Services;
using Abp.Application.Services.Dto;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.IdentityFramework;
using Abp.MultiTenancy;
using Abp.Runtime.Security;
using Microsoft.AspNet.Identity;
using PM.Application.MultiTenancy.Dto;
using PM.Core.Authorization.Roles;
using PM.Core.Editions;
using PM.Core.MultiTenant;
using PM.Core.Users; namespace PM.Application.MultiTenancy
{
public class TenantAppService : AsyncCrudAppService<Tenant, TenantDto, int, PagedResultRequestDto, CreateTenantDto, TenantDto>, ITenantAppService
{
private readonly TenantManager _tenantManager;
private readonly EditionManager _editionManager;
private readonly UserManager _userManager;
private readonly RoleManager _roleManager;
private readonly IAbpZeroDbMigrator _abpZeroDbMigrator; public TenantAppService(
IRepository<Tenant, int> repository,
TenantManager tenantManager,
EditionManager editionManager,
UserManager userManager,
RoleManager roleManager,
IAbpZeroDbMigrator abpZeroDbMigrator
) : base(repository)
{
_editionManager = editionManager;
_tenantManager = tenantManager;
_userManager = userManager;
_roleManager = roleManager;
_abpZeroDbMigrator = abpZeroDbMigrator;
} private void CheckError(IdentityResult identityResult)
{
identityResult.CheckErrors();
} /// <summary>
/// 创建租户
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public override async Task<TenantDto> Create(CreateTenantDto input)
{
//判断是否已拥有此接口的权限(Create方法),需要赋值CreatePermissionName属性
CheckCreatePermission(); var tenant = input.MapTo<Tenant>(); //加密数据库链接字符串(采用AES对称加密)
tenant.ConnectionString = input.ConnectionString.IsNullOrEmpty()
? null
: SimpleStringCipher.Instance.Encrypt(input.ConnectionString); //当前租户使用的版本(Standard标准版)
var defaultEdition = await _editionManager.FindByNameAsync(EditionManager.DefaultEditionName);
if (defaultEdition != null)
{
tenant.EditionId = defaultEdition.Id;
} //创建租户
await _tenantManager.CreateAsync(tenant);
//获得租户的id
await CurrentUnitOfWork.SaveChangesAsync(); //创建租户数据库
_abpZeroDbMigrator.CreateOrMigrateForTenant(tenant); using (CurrentUnitOfWork.SetTenantId(tenant.Id))
{
//创建静态租户角色,该静态角色通过IRoleManagementConfig配置
CheckError(await _roleManager.CreateStaticRoles(tenant.Id));
await CurrentUnitOfWork.SaveChangesAsync();//获取静态角色id //授予管理员角色所有权限(该权限通过IPermissionDefinitionContext配置)
var adminRole = _roleManager.Roles.Single(r => r.Name == StaticRoleNames.Tenants.Admin);
await _roleManager.GrantAllPermissionsAsync(adminRole); //创建租户admin用户
var adminUser= User.CreateTenantAdminUser(tenant.Id, input.AdminEmailAddress, User.DefaultPassword);
CheckError(await _userManager.CreateAsync(adminUser));
await CurrentUnitOfWork.SaveChangesAsync();//获取用户id //讲角色分配给租户admin用户
CheckError(await _userManager.AddToRoleAsync(adminUser.Id, adminRole.Name));
await CurrentUnitOfWork.SaveChangesAsync();
} return MapToEntityDto(tenant);
} protected override void MapToEntity(TenantDto updateInput, Tenant entity)
{
//手动映射,因为TenantDto也包含不可编辑的属性。
entity.Name = updateInput.Name;
entity.TenancyName = updateInput.TenancyName;
entity.IsActive = updateInput.IsActive;
} public override async Task Delete(EntityDto<int> input)
{
CheckDeletePermission();
var tenant =await _tenantManager.FindByIdAsync(input.Id);
await _tenantManager.DeleteAsync(tenant);
}
}
}

TenantAppService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Application.Services;
using Abp.Application.Services.Dto;
using Abp.Authorization.Users;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.IdentityFramework;
using Abp.UI;
using Microsoft.AspNet.Identity;
using PM.Application.Roles.Dto;
using PM.Core.Authorization.Roles;
using PM.Core.MultiTenant;
using PM.Core.Users; namespace PM.Application.Roles
{
public class RoleAppService:AsyncCrudAppService<Role,RoleDto,Int32,PagedResultRequestDto,CreateRoleDto,RoleDto>,IRoleAppService
{
private readonly RoleManager _roleManager;
private readonly UserManager _userManager;
private readonly IRepository<User, long> _userRepository;
private readonly IRepository<UserRole, long> _userRoleRepository;
private readonly IRepository<Role> _roleRepository;
public RoleAppService(
IRepository<Role, int> repository,
RoleManager roleManager,
UserManager userManager,
IRepository<User,long> userRepository,
IRepository<UserRole,long> userRoleRepository,
IRepository<Role> roleRepository
) : base(repository)
{
_roleManager = roleManager;
_userManager = userManager;
_userRepository = userRepository;
_userRoleRepository = userRoleRepository;
_roleRepository = roleRepository;
} public override async Task<RoleDto> Create(CreateRoleDto input)
{
CheckCreatePermission(); var role = input.MapTo<Role>(); CheckErrors(await _roleManager.CreateAsync(role)); //查询权限
var grantedPermissions =
PermissionManager.GetAllPermissions().Where(p => input.Permissions.Contains(p.Name)).ToList(); //给角色设置权限
await _roleManager.SetGrantedPermissionsAsync(role, grantedPermissions); return MapToEntityDto(role);
} public override async Task Delete(EntityDto<int> input)
{
CheckDeletePermission(); var role = await _roleManager.FindByIdAsync(input.Id);
if (role.IsStatic)
{
throw new UserFriendlyException("无法删除静态角色");
} //删除用户角色关联
var users = await GetUsersInRoleAsync(role.Name); foreach (var user in users)
{
CheckErrors(await _userManager.RemoveFromRoleAsync(user, role.Name));
} //删除角色
await _roleManager.DeleteAsync(role);
} private Task<List<long>> GetUsersInRoleAsync(string roleName)
{
var users= (from user in _userRepository.GetAll()
join userRole in _userRoleRepository.GetAll() on user.Id equals userRole.UserId
join role in _roleRepository.GetAll() on userRole.RoleId equals role.Id
where role.Name == roleName
select user.Id).Distinct().ToList();
return Task.FromResult(users);
} public Task<ListResultDto<PermissionDto>> GetAllPermissions()
{
var permissions = PermissionManager.GetAllPermissions();
return Task.FromResult(new ListResultDto<PermissionDto>(permissions.MapTo<List<PermissionDto>>()));
} protected override Task<Role> GetEntityByIdAsync(int id)
{
//查询角色,并包含该角色的权限
var role = Repository.GetAllIncluding(x => x.Permissions).FirstOrDefault(x => x.Id == id);
return Task.FromResult(role);
}
/// <summary>
/// 创建过滤查询
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
protected override IQueryable<Role> CreateFilteredQuery(PagedResultRequestDto input)
{
return Repository.GetAllIncluding(x => x.Permissions);
} /// <summary>
/// 应用排序
/// </summary>
/// <param name="query"></param>
/// <param name="input"></param>
/// <returns></returns>
protected override IQueryable<Role> ApplySorting(IQueryable<Role> query, PagedResultRequestDto input)
{
return query.OrderBy(r => r.DisplayName);
}
private void CheckErrors(IdentityResult identityResult)
{
identityResult.CheckErrors(LocalizationManager);
}
}
}

RoleAppService

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Auditing;
using Abp.AutoMapper;
using PM.Application.Sessions.Dto; namespace PM.Application.Sessions
{
public class SessionAppService:PMAppServiceBase,ISessionAppService
{ [DisableAuditing]
public async Task<GetCurrentLoginInformationsOutput> GetCurrentLoginInformations()
{
var output=new GetCurrentLoginInformationsOutput();
if (AbpSession.UserId.HasValue)
{
output.User = (await GetCurrentUserAsync()).MapTo<UserLoginInfoDto>();
}
if (AbpSession.TenantId.HasValue)
{
output.Tenant = (await GetCurrentTenantAsync()).MapTo<TenantLoginInfoDto>();
}
return output;
}
}
}

SessionAppService

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Application.Services;
using Abp.Application.Services.Dto;
using Abp.Authorization.Users;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.IdentityFramework;
using Microsoft.AspNet.Identity;
using PM.Application.Roles.Dto;
using PM.Application.Users.Dto;
using PM.Core.Authorization.Roles;
using PM.Core.Users; namespace PM.Application.Users
{
public class UserAppService:AsyncCrudAppService<User,UserDto,long,PagedResultRequestDto,CreateUserDto,UpdateUserDto>,IUserAppService
{
private readonly UserManager _userManager;
private readonly IRepository<Role> _roleRepository;
private readonly RoleManager _roleManager; public UserAppService(
IRepository<User, long> repository,
UserManager userManager,
IRepository<Role> roleRepository,
RoleManager roleManager
) : base(repository)
{
_userManager = userManager;
_roleRepository = roleRepository;
_roleManager = roleManager;
} public override async Task<UserDto> Get(EntityDto<long> input)
{
var user = await base.Get(input);
var userRoles = await _userManager.GetRolesAsync(user.Id);
user.Roles = userRoles.ToArray();
return user;
} public override async Task<UserDto> Create(CreateUserDto input)
{
CheckCreatePermission();
var user = input.MapTo<User>();
user.TenantId = AbpSession.TenantId;
user.Password = new PasswordHasher().HashPassword(input.Password);
user.IsEmailConfirmed = true; //分配角色(从租户的所有)
user.Roles = new Collection<UserRole>();
foreach (var roleName in input.RoleNames)
{
var role = await _roleManager.GetRoleByNameAsync(roleName);
user.Roles.Add(new UserRole(AbpSession.TenantId, user.Id, role.Id));
}
CheckErrors(await _userManager.CreateAsync(user));
return MapToEntityDto(user);
} public override async Task<UserDto> Update(UpdateUserDto input)
{
CheckUpdatePermission();
var user = await _userManager.GetUserByIdAsync(input.Id); //把有变动的属性赋值到user对象中
MapToEntity(input, user); CheckErrors(await _userManager.UpdateAsync(user)); if (input.RoleNames != null)
{
//_userManager.SetRoles方法的作用:变更角色(前提:用户表以已经创建)
CheckErrors(await _userManager.SetRoles(user, input.RoleNames));
} return await Get(input);
} public override async Task Delete(EntityDto<long> input)
{
var user = await _userManager.GetUserByIdAsync(input.Id); //admin用户不能被删除(删除用户的同时会删除UserRole)
await _userManager.DeleteAsync(user);
}
public async Task<ListResultDto<RoleDto>> GetRoles()
{
var roles = await _roleRepository.GetAllListAsync();
return new ListResultDto<RoleDto>(roles.MapTo<List<RoleDto>>());
} protected override User MapToEntity(CreateUserDto createInput)
{
var user = ObjectMapper.Map<User>(createInput);
return user;
} protected override void MapToEntity(UpdateUserDto updateInput, User entity)
{
ObjectMapper.Map(updateInput, entity);
} protected override IQueryable<User> CreateFilteredQuery(PagedResultRequestDto input)
{
return Repository.GetAllIncluding(x => x.Roles);
} protected override async Task<User> GetEntityByIdAsync(long id)
{
var user = Repository.GetAllIncluding(x => x.Roles).FirstOrDefault(x => x.Id == id);
return await Task.FromResult(user);
}
protected override IQueryable<User> ApplySorting(IQueryable<User> query, PagedResultRequestDto input)
{
return query.OrderBy(r => r.UserName);
} private void CheckErrors(IdentityResult identityResult)
{
identityResult.CheckErrors(LocalizationManager);
}
}
}

UserAppService

五、PM.WebApi

1,NuGet安装:

Abp.Zero、Abp.Web.Api、Abp.AutoMapper、Microsoft.Owin.Security.OAuth、Microsoft.AspNet.WebApi.Owin

System.ComponentModel.DataAnnotations

2,WebApiModule

using System.Web.Http;
using Abp.Application.Services;
using Abp.Configuration.Startup;
using Abp.Modules;
using Abp.WebApi; namespace MyPassword.Api
{
[DependsOn(typeof(AbpWebApiModule), typeof(MyPasswordApplicationModule))]
public class MyPasswordWebApiModule : AbpModule
{
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); Configuration.Modules.AbpWebApi().DynamicApiControllerBuilder
.ForAll<IApplicationService>(typeof(MyPasswordApplicationModule).Assembly, "app")
.Build(); //添加访问令牌类型(Access Token Types)过滤(不加这句话也支持Bearer)
Configuration.Modules.AbpWebApi().HttpConfiguration.Filters.Add(new HostAuthenticationFilter("Bearer"));
}
}
}

备注:访问令牌类型(Access Token Types)包括bearer类型或mac类型。

①bearer类型

[RFC6750]中定义的“bearer”令牌类型被简单地包含在请求中的访问令牌字符串中:

     GET /resource/1 HTTP/1.1
Host: example.com
Authorization: Bearer mF_9.B5f-4.1JqM

②mac类型

而[OAuth-HTTP-MAC]中定义的“mac”令牌类型是通过发送消息认证码(MAC)密钥与用于签署HTTP请求的某些组件的访问令牌一起使用的:

     GET /resource/1 HTTP/1.1
Host: example.com
Authorization: MAC id="h480djs93hd8",
nonce="274312:dj83hs9s",
mac="kDZvddkndxvhGRXZhvuDjEWhGeE="

3,AccountController(获取访问令牌)

using System;
using System.Threading.Tasks;
using System.Web.Http;
using Abp.Authorization;
using Abp.Authorization.Users;
using Abp.UI;
using Abp.Web.Models;
using Abp.WebApi.Controllers;
using MyPassword.Api.Models;
using MyPassword.Authorization;
using MyPassword.Authorization.Users;
using MyPassword.MultiTenancy;
using MyPassword.Users;
using Microsoft.Owin.Infrastructure;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth; namespace MyPassword.Api.Controllers
{
public class AccountController : AbpApiController
{
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; } private readonly LogInManager _logInManager; static AccountController()
{
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
} public AccountController(LogInManager logInManager)
{
_logInManager = logInManager;
LocalizationSourceName = MyPasswordConsts.LocalizationSourceName;
} //获取访问令牌
[HttpPost]
public async Task<AjaxResponse> Authenticate(LoginModel loginModel)
{
CheckModelState(); var loginResult = await GetLoginResultAsync(
loginModel.UsernameOrEmailAddress,
loginModel.Password,
loginModel.TenancyName
); var ticket = new AuthenticationTicket(loginResult.Identity, new AuthenticationProperties()); var currentUtc = new SystemClock().UtcNow;
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes()); return new AjaxResponse(OAuthBearerOptions.AccessTokenFormat.Protect(ticket));
} private async Task<AbpLoginResult<Tenant, User>> GetLoginResultAsync(string usernameOrEmailAddress, string password, string tenancyName)
{
var loginResult = await _logInManager.LoginAsync(usernameOrEmailAddress, password, tenancyName); switch (loginResult.Result)
{
case AbpLoginResultType.Success:
return loginResult;
default:
throw CreateExceptionForFailedLoginAttempt(loginResult.Result, usernameOrEmailAddress, tenancyName);
}
} private Exception CreateExceptionForFailedLoginAttempt(AbpLoginResultType result, string usernameOrEmailAddress, string tenancyName)
{
switch (result)
{
case AbpLoginResultType.Success:
return new ApplicationException("Don't call this method with a success result!");
case AbpLoginResultType.InvalidUserNameOrEmailAddress:
case AbpLoginResultType.InvalidPassword:
return new UserFriendlyException(L("LoginFailed"), L("InvalidUserNameOrPassword"));
case AbpLoginResultType.InvalidTenancyName:
return new UserFriendlyException(L("LoginFailed"), L("ThereIsNoTenantDefinedWithName{0}", tenancyName));
case AbpLoginResultType.TenantIsNotActive:
return new UserFriendlyException(L("LoginFailed"), L("TenantIsNotActive", tenancyName));
case AbpLoginResultType.UserIsNotActive:
return new UserFriendlyException(L("LoginFailed"), L("UserIsNotActiveAndCanNotLogin", usernameOrEmailAddress));
case AbpLoginResultType.UserEmailIsNotConfirmed:
return new UserFriendlyException(L("LoginFailed"), "Your email address is not confirmed. You can not login"); //TODO: localize message
default: //Can not fall to default actually. But other result types can be added in the future and we may forget to handle it
Logger.Warn("Unhandled login fail reason: " + result);
return new UserFriendlyException(L("LoginFailed"));
}
} protected virtual void CheckModelState()
{
if (!ModelState.IsValid)
{
throw new UserFriendlyException("Invalid request!");
}
}
}
}

AccountController

4,Web工程下的Startup.cs

app.UseOAuthBearerAuthentication(AccountController.OAuthBearerOptions);

将Bearer Token处理添加到OWIN应用程序管道。
这个中间件理解适当的格式化和安全的令牌出现在请求头。
如果Options.AuthenticationMode处于Active状态,则不记名令牌(bearer token)中的声明将被添加到当前请求的IPrincipal用户。
如果Options.AuthenticationMode是Passive的,那么当前请求不被修改,但IAuthenticationManager AuthenticateAsync可以随时用来从请求的不记名令牌(bearer token)中获得请求。
另见http://tools.ietf.org/html/rfc6749

5,案例

请求授权接口

返回令牌

请求受保护的资源

六、PM.Web(MPA)

1,NuGet安装:Abp.Zero、Abp.EntityFramework、Abp.Zero.EntityFramework、Abp.Web.Api、Abp.Web.Mvc、Abp.AutoMapper、Abp.Castle.Log4Net、Abp.Owin

可选则安装:Abp.Web.SignalR

注意,先安装Abp.EntityFramework再安装Abp.Zero.EntityFramework

针对通用的依赖类型的解析与创建,微软默认定义了4种类别的生命周期,分别如下:

类型 描述
Instance 任何时间都只能使用特定的实例对象,开发人员需要负责该对象的初始化工作。
Transient 每次都重新创建一个实例。
Singleton 创建一个单例,以后每次调用的时候都返回该单例对象。
Scoped 在当前作用域内,不管调用多少次,都是一个实例,换了作用域就会再次创建实例,类似于特定作用内的单例。

前端笔记

1,按钮

<button type="button" class="btn btn-primary btn-circle waves-effect waves-circle waves-float"  data-toggle="modal"><i class="material-icons">add</i></button>

btn-circle:圆形按钮

pull-right:右浮动

waves-effect:点击按钮波浪效果

waves-block:块状效果

waves-circle:圆状效果

waves-float:效果浮动

2, 模态框

<a href="#" class="waves-effect waves-block edit-role" data-role-id="@role.Id" data-toggle="modal" data-target="#RoleEditModal"><i class="material-icons">edit</i>@L("Edit")</a>
<div class="modal fade" id="RoleEditModal" tabindex="-1" role="dialog" aria-labelledby="RoleEditModalLabel" data-backdrop="static">
<div class="modal-dialog" role="document">
<div class="modal-content"> </div>
</div>
</div>
<div class="modal fade" id="RoleCreateModal" tabindex="-1" role="dialog" aria-labelledby="RoleCreateModalLabel" data-backdrop="static">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">
<span>@L("CreateNewRole")</span>
</h4>
</div>
<div class="modal-body">
<form name="roleCreateForm" role="form" novalidate class="form-validation">
<div class="row clearfix">
<div class="col-sm-12">
<div class="form-group form-float">
<div class="form-line">
<input id="rolename" type="text" name="Name" required maxlength="32" minlength="2" class="validate form-control">
<label for="rolename" class="form-label">@L("RoleName")</label>
</div>
</div>
</div>
</div> <div class="row clearfix">
<div class="col-sm-12">
<div class="form-group form-float">
<div class="form-line">
<input id="displayname" type="text" name="DisplayName" required maxlength="32" minlength="2" class="validate form-control">
<label for="displayname" class="form-label">@L("DisplayName")</label>
</div>
</div>
</div>
</div> <div class="row">
<div class="col-sm-12">
<div class="form-group form-float">
<div class="form-line">
<textarea id="role-description" name="Description" class="validate form-control"></textarea>
<label for="role-description" class="form-label">Role Description</label>
</div>
</div>
</div>
</div> <div class="row clearfix">
<div class="col-sm-12">
<h4>Permissions</h4>
@foreach (var permission in Model.Permissions)
{
<div class="col-sm-6">
<input type="checkbox" name="permission" value="@permission.Name" class="filled-in" id="@string.Format("permission{0}",permission.Name)" checked="checked" />
<label for="@string.Format("permission{0}",permission.Name)">@permission.DisplayName</label>
</div>
}
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default waves-effect" data-dismiss="modal">@L("Cancel")</button>
<button type="submit" class="btn btn-primary waves-effect">@L("Save")</button>
</div>
</form>
</div>
</div>
</div>
</div>

3,for属性

在用户注册的时候,常常用户点击文字就需要将光标聚焦到对应的表单上面,这个是怎么实现的呢?就是下面我要介绍的<label>标签的for属性

定义:for 属性规定 label 与哪个表单元素绑定

                  <div class="form-line">
<input id="rolename" type="text" name="Name" required maxlength="32" minlength="2" class="validate form-control">
<label for="rolename" class="form-label">@L("RoleName")</label>
</div>

七、PM.Web(SPA)

八、单元测试

1,NuGet安装:

Abp.TestBase、Abp.EntityFramework、Effort.EF6、xunit、Shouldly、xunit.runner.visualstudio、Abp.Zero.EntityFramework、NSubstitute

2,基本结构

using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp;
using Abp.Configuration.Startup;
using Abp.Domain.Uow;
using Abp.Runtime.Session;
using Abp.TestBase;
using Castle.MicroKernel.Registration;
using Effort;
using EntityFramework.DynamicFilters;
using PM.Core.MultiTenant;
using PM.Core.Users;
using PM.EntityFramework.EntityFramework;
using PM.EntityFramework.Migrations.SeedData; namespace PM.Test
{
public class PMTestBase: AbpIntegratedTestBase<PMTestModule>
{
private DbConnection _hostDb;
private Dictionary<int, DbConnection> _tenantDbs; //only used for db per tenant architecture protected PMTestBase()
{
//Seed initial data for host
AbpSession.TenantId = null;
UsingDbContext(context =>
{
new InitialHostDbBuilder(context).Create();
new DefaultTenantCreator(context).Create();
}); //Seed initial data for default tenant
AbpSession.TenantId = ;
UsingDbContext(context =>
{
new TenantRoleAndUserBuilder(context, ).Create();
}); LoginAsDefaultTenantAdmin();
} protected override void PreInitialize()
{
base.PreInitialize(); /* You can switch database architecture here: */
UseSingleDatabase();
//UseDatabasePerTenant();
} /* Uses single database for host and all tenants.
*/
private void UseSingleDatabase()
{
_hostDb = DbConnectionFactory.CreateTransient(); LocalIocManager.IocContainer.Register(
Component.For<DbConnection>()
.UsingFactoryMethod(() => _hostDb)
.LifestyleSingleton()
);
} /* Uses single database for host and Default tenant,
* but dedicated databases for all other tenants.
*/
private void UseDatabasePerTenant()
{
_hostDb = DbConnectionFactory.CreateTransient();
_tenantDbs = new Dictionary<int, DbConnection>(); LocalIocManager.IocContainer.Register(
Component.For<DbConnection>()
.UsingFactoryMethod((kernel) =>
{
lock (_tenantDbs)
{
var currentUow = kernel.Resolve<ICurrentUnitOfWorkProvider>().Current;
var abpSession = kernel.Resolve<IAbpSession>(); var tenantId = currentUow != null ? currentUow.GetTenantId() : abpSession.TenantId; if (tenantId == null || tenantId == ) //host and default tenant are stored in host db
{
return _hostDb;
} if (!_tenantDbs.ContainsKey(tenantId.Value))
{
_tenantDbs[tenantId.Value] = DbConnectionFactory.CreateTransient();
} return _tenantDbs[tenantId.Value];
}
}, true)
.LifestyleTransient()
);
} #region UsingDbContext protected IDisposable UsingTenantId(int? tenantId)
{
var previousTenantId = AbpSession.TenantId;
AbpSession.TenantId = tenantId;
return new DisposeAction(() => AbpSession.TenantId = previousTenantId);
} protected void UsingDbContext(Action<PMDBContext> action)
{
UsingDbContext(AbpSession.TenantId, action);
} protected Task UsingDbContextAsync(Func<PMDBContext, Task> action)
{
return UsingDbContextAsync(AbpSession.TenantId, action);
} protected T UsingDbContext<T>(Func<PMDBContext, T> func)
{
return UsingDbContext(AbpSession.TenantId, func);
} protected Task<T> UsingDbContextAsync<T>(Func<PMDBContext, Task<T>> func)
{
return UsingDbContextAsync(AbpSession.TenantId, func);
} protected void UsingDbContext(int? tenantId, Action<PMDBContext> action)
{
using (UsingTenantId(tenantId))
{
using (var context = LocalIocManager.Resolve<PMDBContext>())
{
context.DisableAllFilters();
action(context);
context.SaveChanges();
}
}
} protected async Task UsingDbContextAsync(int? tenantId, Func<PMDBContext, Task> action)
{
using (UsingTenantId(tenantId))
{
using (var context = LocalIocManager.Resolve<PMDBContext>())
{
context.DisableAllFilters();
await action(context);
await context.SaveChangesAsync();
}
}
} protected T UsingDbContext<T>(int? tenantId, Func<PMDBContext, T> func)
{
T result; using (UsingTenantId(tenantId))
{
using (var context = LocalIocManager.Resolve<PMDBContext>())
{
context.DisableAllFilters();
result = func(context);
context.SaveChanges();
}
} return result;
} protected async Task<T> UsingDbContextAsync<T>(int? tenantId, Func<PMDBContext, Task<T>> func)
{
T result; using (UsingTenantId(tenantId))
{
using (var context = LocalIocManager.Resolve<PMDBContext>())
{
context.DisableAllFilters();
result = await func(context);
await context.SaveChangesAsync();
}
} return result;
} #endregion #region Login protected void LoginAsHostAdmin()
{
LoginAsHost(User.AdminUserName);
} protected void LoginAsDefaultTenantAdmin()
{
LoginAsTenant(Tenant.DefaultTenantName, User.AdminUserName);
} protected void LogoutAsDefaultTenant()
{
LogoutAsTenant(Tenant.DefaultTenantName);
} protected void LoginAsHost(string userName)
{
AbpSession.TenantId = null; var user =
UsingDbContext(
context =>
context.Users.FirstOrDefault(u => u.TenantId == AbpSession.TenantId && u.UserName == userName));
if (user == null)
{
throw new Exception("There is no user: " + userName + " for host.");
} AbpSession.UserId = user.Id;
} protected void LogoutAsHost()
{
Resolve<IMultiTenancyConfig>().IsEnabled = true; AbpSession.TenantId = null;
AbpSession.UserId = null;
} protected void LoginAsTenant(string tenancyName, string userName)
{
var tenant = UsingDbContext(context => context.Tenants.FirstOrDefault(t => t.TenancyName == tenancyName));
if (tenant == null)
{
throw new Exception("There is no tenant: " + tenancyName);
} AbpSession.TenantId = tenant.Id; var user =
UsingDbContext(
context =>
context.Users.FirstOrDefault(u => u.TenantId == AbpSession.TenantId && u.UserName == userName));
if (user == null)
{
throw new Exception("There is no user: " + userName + " for tenant: " + tenancyName);
} AbpSession.UserId = user.Id;
} protected void LogoutAsTenant(string tenancyName)
{
var tenant = UsingDbContext(context => context.Tenants.FirstOrDefault(t => t.TenancyName == tenancyName));
if (tenant == null)
{
throw new Exception("There is no tenant: " + tenancyName);
} AbpSession.TenantId = tenant.Id;
AbpSession.UserId = null;
} #endregion /// <summary>
/// Gets current user if <see cref="IAbpSession.UserId"/> is not null.
/// Throws exception if it's null.
/// </summary>
protected async Task<Core.Users.User> GetCurrentUserAsync()
{
var userId = AbpSession.GetUserId();
return await UsingDbContext(context => context.Users.SingleAsync(u => u.Id == userId));
} /// <summary>
/// Gets current tenant if <see cref="IAbpSession.TenantId"/> is not null.
/// Throws exception if there is no current tenant.
/// </summary>
protected async Task<Tenant> GetCurrentTenantAsync()
{
var tenantId = AbpSession.GetTenantId();
return await UsingDbContext(context => context.Tenants.SingleAsync(t => t.Id == tenantId));
}
}
}

PMTestBase

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Modules;
using Abp.MultiTenancy;
using Abp.TestBase;
using Abp.Zero.Configuration;
using Castle.MicroKernel.Registration;
using NSubstitute;
using PM.Application;
using PM.EntityFramework; namespace PM.Test
{
[DependsOn(
typeof(PMDataModule),
typeof(PMApplicationModule),
typeof(AbpTestBaseModule)
)]
public class PMTestModule:AbpModule
{
public override void PreInitialize()
{
//使用数据库进行语言管理
Configuration.Modules.Zero().LanguageManagement.EnableDbLocalization(); //注册伪服务 IocManager.IocContainer.Register(
Component.For<IAbpZeroDbMigrator>()
.UsingFactoryMethod(() => Substitute.For<IAbpZeroDbMigrator>())
.LifestyleSingleton()
);
}
}
}

PMTestModule

using Xunit;

namespace AbpCompanyName.AbpProjectName.Tests
{
public sealed class MultiTenantFactAttribute : FactAttribute
{
public MultiTenantFactAttribute()
{
if (!AbpProjectNameConsts.MultiTenancyEnabled)
{
Skip = "MultiTenancy is disabled.";
}
}
}
}

MultiTenantFactAttribute

手动搭建ABP2.1.3 Zero——基础框架的更多相关文章

  1. 手动搭建ABP2.1.3——基础框架

    一.基础层搭建 1,创建一个空解决方案 2,层结构 Demo.Core[v:4.6.1]:类库 Demo.EntityFramework[v:4.6.1]:类库(引用Demo.Core) Demo.A ...

  2. 手动搭建I/O网络通信框架1:Socket和ServerSocket入门实战,实现单聊

    资料:慕课网 第二章:手动搭建I/O网络通信框架2:Socket和ServerSocket入门实战,实现单聊 这个基础项目会作为BIO.NIO.AIO的一个前提,后面会有数篇博客会基于这个小项目利用B ...

  3. vue新手入门之使用vue框架搭建用户登录注册案例,手动搭建webpack+Vue项目(附源码,图文详解,亲测有效)

    前言 本篇随笔主要写了手动搭建一个webpack+Vue项目,掌握相关loader的安装与使用,包括css-loader.style-loader.vue-loader.url-loader.sass ...

  4. 手动从0搭建ABP框架-ABP官方完整解决方案和手动搭建简化解决方案实践

      本文主要讲解了如何把ABP官方的在线生成解决方案运行起来,并说明了解决方案中项目间的依赖关系.然后手动实践了如何从0搭建了一个简化的解决方案.ABP官方的在线生成解决方案源码下载参考[3],手动搭 ...

  5. iOS基础框架的搭建/国际化操作

    1.基础框架的搭建 1.1 pod引入常用的第三方类库 1.2 创建基础文件夹结构/目录结构 Resource———存放声音/图片/xib/storyboard 等资源文件 Define——宏定义, ...

  6. 准备.Net转前端开发-WPF界面框架那些事,搭建基础框架

    题外话 最近都没怎么写博客,主要是最近在看WPF方面的书<wpf-4-unleashed.pdf>,挑了比较重要的几个章节学习了下WPF基础技术.另外,也把这本书推荐给目前正在从事WPF开 ...

  7. iOS基础框架的搭建 / 及国际化操作

    1.基础框架的搭建 1.1 pod引入常用的第三方类库 1.2 创建基础文件夹结构/目录结构 Resource———存放声音/图片/xib/storyboard 等资源文件 Define——宏定义, ...

  8. LayIM.AspNetCore Middleware 开发日记(三)基础框架搭建

    前言 在上一篇中简单讲了一些基础知识,例如Asp.Net Core Middleware 的使用,DI的简单使用以及嵌入式资源的使用方法等.本篇就是结合基础知识来构建一个基础框架出来. 那么框架有什么 ...

  9. SSM框架手动搭建

    SSM框架手动搭建 创建web项目 IDEA创建Maven项目 [File]-->[new]-->[project..] 将项目变为web项目 [File]-->[Project S ...

随机推荐

  1. Codeforces 932 E. Team Work(组合数学)

    http://codeforces.com/contest/932/problem/E 题意:   可以看做 有n种小球,每种小球有无限个,先从中选出x种,再在这x种小球中任选k个小球的方案数 选出的 ...

  2. ASP.NET MVC学习笔记-----ControllerFactory

    上面这张图是asp.net mvc的工作流程图,我们可以看到当一个http请求来临时,首先需要经过路由系统,路由系统从中获取一些路由信息,然后ControllerFactory根据所得到的路由信息生成 ...

  3. MySQL索引背后的数据结构及算法原理 (转)

    摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BT ...

  4. 第6月第17天 CGAffineTransformMake(a,b,c,d,tx,ty) 矩阵运算的原理

    1. 为了把二维图形的变化统一在一个坐标系里,引入了齐次坐标的概念,即把一个图形用一个三维矩阵表示,其中第三列总是(0,0,1),用来作为坐标系的标准.所以所有的变化都由前两列完成. 以上参数在矩阵中 ...

  5. mysql.user细节三问

    一.如何拒绝用户从某个精确ip访问数据库假如在mysql.user表中存在用户'mydba'@'192.168.85.%',现在想拒绝此用户从某个精确ip访问数据库 # 创建精确ip用户,分配不同的密 ...

  6. Python内置模块与标准库

    Python内置模块就是标准库(模块)吗?或者说Python的自带string模块是内置模块吗? 答案是:string不是内置模块,它是标准库.也就是说Python内置模块和标准库并不是同一种东西. ...

  7. 转:VMWare服务器虚拟化--转自CSDN

    http://blog.csdn.net/kkfloat/article/category/1249845/3

  8. slf4j MDC使用

    slf4j MDC使用 最近也是在项目代码里发现一个地方有个MDC.put(),忍不住好奇点了进去,于是知道了MDC这个东西,细研究一下,发现还真是个好东西. MDC解决了什么问题 MDC全名Mapp ...

  9. Linux内核启动流程分析(一)【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-3380535.html 很久以前分析的,一直在电脑的一个角落,今天发现贴出来和大家分享下.由于是word直接 ...

  10. linux服务器如何添加sudo用户

    1. 编辑 vi /etc/ssh/sshd_config 文件,修改默认端口:默认Port为22,并且已经注释掉了,修改是把注释去掉,并修改成其它的端口. 原来用默认端口:22修改为:8975 (这 ...