Asp.NET Core+ABP框架+IdentityServer4+MySQL+Ext JS之添加实体
来源:互联网 发布:淘宝店铺活动方图案例 编辑:程序博客网 时间:2024/05/17 22:45
- 尝试新的开发组合:Asp.NET Core+ABP框架+IdentityServer4+MySQL+Ext JS
- Asp.NET Core+ABP框架+IdentityServer4+MySQL+Ext JS之配置IdentityServer
- Asp.NET Core+ABP框架+IdentityServer4+MySQL+Ext JS之数据迁移
- Asp.NET Core+ABP框架+IdentityServer4+MySQL+Ext JS之添加实体
在ABP框架中,实体类是在Core项目中定义的。根据模版提供的Core项目,可以看到,实体类都是根据功能划分到不同的文件夹的。在这里,我们可以将SimpleCMS都放到CMS文件夹内,也可以单独方在独立的文件夹内。在本练习将使用独立文件夹的方式。
要定义实体,可以从Entity
、Entity<T&>
、IEntity
和IEntity<T>
等类或接口中派生。这4个类或接口中,Entity
派生于Entity<int>
、 IEntity
和IEntity<T>
,使用整型作为实体的主键;Entity<T>
是接口IEntity<T>
的实现,也就是已经为你实现了接口的功能,不再需要自己去实现接口功能。从这4个类或接口的定义来看,一般情况下,我们从Entity
类或Entity<T>
类派生实体类就行,如果有特殊需求,就从接口中派生。
在定义实体类时,还可以为实体类添加以下常用接口用来实现一些常用功能:
IHasCreationTime
:为实体添加CreationTime
属性,用来记录实体的创建时间IHasDeletionTime
:为实体添加DeletionTime
属性,用来记录实体的删除时间,这个只有在使用软删除的时候才有效。如果不是使用软删除,记录都删除了,这个字段没有任何意义。IHasModificationTime
:为实体添加LastModificationTime
属性,用来记录实体的最后修改时间ICreationAudited
:在IHasCreationTime
的基础上添加CreatorUserId
属性,用来记录创建实体的用户的IdIDeletionAudited
:在IHasDeletionTime
的基础上添加DeleterUserId
属性,用来记录删除实体的用户的IdIModificationAudited
:在IHasModificationTime
的基础上添加LastModifierUserId
属性,用来记录最后修改实体的用户的IdIAudited
:ICreationAudited
和IModificationAudited
的合体,主要用于非软删除的情景IFullAudited
:IAudited
和IDeletionAudited
的合体,主要用于软删除的情景ISoftDelete
:为实体添加IsDeleted
属性,用于判断实体是否已经被删除,主要用于软删除的情景IPassivable
:为实体添加IsActive
属性,用于判断实体是否处于活跃状态IMayHaveTenant
:为实体添加TenantId
属性,用于指定实体所属的租户。该属性允许值为null,也就是可以指定租户,也可以不指定IMustHaveTenant
:该接口与IMayHaveTenant
接口的主要区别是,必须指定租户IExtendableObject
:为实体添加ExtensionData
属性,用于存储JSON格式的数据。在实体中可通过SetData
方法来设置存储的数据,通过GetData
来获取存储的数据
了解了实体类的定义方式后,我们来编写类别实体类,在Core项目下新建一个Categories文件夹,并添加一个名为Category的类,具体定义如下:
[Table("AppCategories")] public class Category :Entity<long>, IFullAudited, IMustHaveTenant { public const int MaxStringLength = 255; public const int MaxContentLength = 4000; public long? ParentId { get; set; } [ForeignKey("ParentId")] public virtual Category Parent { get; set; } [Required] [MaxLength(MaxStringLength)] public string Title { get; set; } [MaxLength(MaxStringLength)] public string Image { get; set; } [MaxLength(MaxContentLength)] public string Content { get; set; } [DefaultValue(0)] public int SortOrder { get; set; } public virtual ICollection<Category> SubCategories { get; set; } public virtual ICollection<Content> Contents { get; set; } public DateTime CreationTime { get; set; } public DateTime? LastModificationTime { get; set; } public DateTime? DeletionTime { get; set; } public long? CreatorUserId { get; set; } public long? LastModifierUserId { get; set; } public long? DeleterUserId { get; set; } public bool IsDeleted { get; set; } public int TenantId { get; set; } public Category() { CreationTime = Clock.Now; SortOrder = 0; } }
在代码中,使用了Table
特性将实体对应的表的名称定义为了AppCategories。在类中,还加入了IFullAudited
和IMustHaveTenant
接口,说明类别实体将采用完整的审计功能,使用软删除来实现删除,而且必须为它设置租户。
在实体的构造函数中,将创建时间设置为了当前时间。在这里没有使用DataTime
的Now
属性是因为用户可能在不同的时区使用系统,为了能很好的处理这个问题,ABP定义了自己的时间操作功能。如果不考虑时区问题,这里可以换回DataTime
对象。
估计很多人都会觉得奇怪,为什么在定义字符串的最大长度时,都要在实体类内定义一个常量呢?这是因为在使用AutoMap来实现DTO类的时候,还需要定义一次最大长度,如果直接使用数字,那么,当需要修改字符串长度的时候,就需要修改2次了,而使用常量的方式,只需要修改一次就行了。
由于在MySQL中触发器与SQL Server的表现有点不同,因而没有定义HierarchyLevel和FullPath这两个字段。
由于Entity Framework Core不支持使用Index
特性来声明索引,只能使用Fluent API来创建索引。切换到EntityFrameworkCore项目,打开SimpleCmsWithAbpDbContext.cs文件,在类内先添加实体集,代码如下:
public DbSet<Category> Categories { get; set; }
然后在OnModelCreating
方法的最底部,添加以下代码创建索引:
modelBuilder.Entity<Category>().HasIndex(p => p.SortOrder);
至此,类别实体就已经定义完了,相当的简单。下面来定义文章实体,具体代码如下:
[Table("AppContents")] public class Content : Entity<long>, IFullAudited, IMustHaveTenant { public const int MaxStringLength = 255; public const int MaxSummaryLength = 500; [Required] [MaxLength(MaxStringLength)] public string Title { get; set; } [Required] public long CategoryId { get; set; } [ForeignKey("CategoryId")] public virtual Category Category { get; set; } [MaxLength(MaxStringLength)] public string Image { get; set; } [MaxLength(MaxSummaryLength)] public string Summary { get; set; } [Required] [Column(TypeName = "text")] public string Body { get; set; } [Required] [DefaultValue(0)] public int Hits { get; set; } [Required] [DefaultValue(0)] public int SortOrder { get; set; } public virtual ICollection<ContentTag> ContentTags { get; set; } public DateTime CreationTime { get; set; } public DateTime? LastModificationTime { get; set; } public DateTime? DeletionTime { get; set; } public long? CreatorUserId { get; set; } public long? LastModifierUserId { get; set; } public long? DeleterUserId { get; set; } public bool IsDeleted { get; set; } public int TenantId { get; set; } public Content() { CreationTime = Clock.Now; Hits = 0; SortOrder = 0; } }
在这里需要注意的是数据库的区别,由于MySQL的存储超长字符的数据类型有text、mediumtext和longtext等,大家需要根据需要进行选择。在这里我觉得使用text就足够了,它可以存储65535个字符。如果认为不足够,可以修改为longtext,当然,一劳永逸的方法就是无论什么情况,都设置为longtext。
文章实体创建后,别忘记在Context中添加实体集和索引。
由于Entity Framework Core不再支持自动创建多对多关系的关联表,需要显式定义关联表,因而,我们需要在Contents文件夹下再创建一个名为ContentTag的实体,作为文章和标签的关联实体。对于ContentTag实体,很有意思,如果从Entity<T>
派生,那就会为它添加一个主键,而不能使用文章的Id和标签的Id来创建主键。为这个问题,我特意搜索了一下,找到了《Excluding the default Id primary key from an Entity….》这个帖子。在帖子中,ABP官方的答复是使用文章的Id和标签的Id创建一个唯一索引,而不去管那个主键,因为这个主键是人畜无害的,而且在删除的时候可以使用这个主键去删除实体,也挺方便的。不过,官方的答复人员对于这样的结构也有点不爽,进一步的方法是使用NotMapped
特性屏蔽Id字段,不写到数据库,但带来的问题是,要使用Repository
来处理实体的CURD操作,没有Id主键会出现问题。除非重写Repository
类,不然解决不了这个问题,但在ABP官方文档《Repositories 》的最佳实践(Repository Best Practices)一节中,建议不要去自定义存储,而且重写存储也确实是比较大的工程,因而,笔者的看法是,虽然这样使用是丑陋了点,但有时候做开发只能这样折衷一下。
定义好的ContentTag实体代码如下:
[Table("AppContentTags")] public class ContentTag:Entity<long> { [Required] public long ContentId { get; set; } [ForeignKey("ContentId")] public virtual Content Content { get; set; } [Required] public long TagId { get; set; } [ForeignKey("TagId")] public virtual Tag Tag { get; set; } }
定义好ContentTag实体 后,在OnModelCreating
方法中为实体添加索引,代码如下:
modelBuilder.Entity<ContentTag>().HasIndex(p => new {p.ContentId, p.TagId}).IsUnique();
下面来完成标签实体,代码如下:
[Table("AppTags")] public class Tag: Entity<long>, IMustHaveTenant { public const int MaxNameLength = 50; [Required] [MaxLength(MaxNameLength)] public string Name { get; set; } public int TenantId { get; set; } public virtual ICollection<ContentTag> ContentTags { get; set; } }
在标签实体中,没有使用审计功能。
还要为标签的Name字段添加唯一索引,代码如下:
modelBuilder.Entity<Tag>().HasIndex(p => p.Name).IsUnique();
接下来是媒体实体,代码如下:
public class Media : Entity<long>, ICreationAudited, IDeletionAudited, IMustHaveTenant { public const int MaxFileNameLength = 32; public const int MaxDescriptionLength = 255; public const int MaxPathLength = 10; [Required] [MaxLength(MaxFileNameLength)] public string Filename { get; set; } [Required] [MaxLength(MaxDescriptionLength)] public string Description { get; set; } [Required] [MaxLength(MaxPathLength)] public string Path { get; set; } [Required] [Range(0, 2)] [DefaultValue(0)] public MediaType Type { get; set; } [Required] [DefaultValue(0)] public int Size { get; set; } public DateTime CreationTime { get; set; } public DateTime? DeletionTime { get; set; } public long? CreatorUserId { get; set; } public long? DeleterUserId { get; set; } public bool IsDeleted { get; set; } public int TenantId { get; set; } public Media() { CreationTime = Clock.Now; } }
由于媒体没有更新功能,因而不需要更新审计,不采用IFullAudited接口,直接使用ICreationAudited和IDeletionAudited接口。
在定义媒体类型的时候,使用了枚举类型的数据,定义如下:
public enum MediaType: byte { Image = 0, Audio = 1, Video = 2 }
最后是用户配置实体,代码如下:
[Table("AppUserProfiles")] public class UserProfile :Entity<long> { public const int MaxKeywordLength = 200; public const int MaxValueLength = 1000; [DefaultValue(1)] public UserProfileType UserProfileType { get; set; } public long UserId { get; set; } [ForeignKey("UserId")] public virtual User User { get; set; } [Required] [MaxLength(MaxKeywordLength)] public string Keyword { get; set; } [Required] [MaxLength(MaxValueLength)] public string Value { get; set; } }
这里使用了一个UserProfileType的枚举,代码如下:
public enum UserProfileType : byte { State = 1 }
在Context中添加全部实体集后,就可调用以下语句添加迁移文件了:
Add-Migration AddCmsTables -Context SimpleCmsWithAbpDbContext
生成迁移文件后,不要使用Update-Database来更新数据库,使用Migrator项目来进行迁移,以便为类别表加入未分类类别。
在Seed文件夹下新建一个名为Cms的文件夹,然后参考TenantRoleAndUserBuilder.cs文件创建一个名为DefaultCategoryBuilder的类,代码如下:
public class DefaultCategoryBuilder { private readonly SimpleCmsWithAbpDbContext _context; private readonly int _tenantId; public DefaultCategoryBuilder(SimpleCmsWithAbpDbContext context, int tenantId) { _context = context; _tenantId = tenantId; } public void Create() { CreateDefaultTenant(); } private void CreateDefaultTenant() { // Default tenant if(_context.Categories.Any(m=>m.Title.Equals("未分类", StringComparison.CurrentCulture))) return; var category = new Category() {Title = "未分类", Content = "", SortOrder = 0,TenantId = _tenantId }; _context.Categories.Add(category); _context.SaveChanges(); } }
接下来在SeedHelper类中的SeedHostDb
方法的底部添加以下代码创建DefaultCategoryBuilder的实例来添加未分类类别:
new DefaultCategoryBuilder(context,1).Create();
好了,现在将Migrator项目设置为启动项目,执行一次,就可在数据库中看到本文创建的实体了,打开appcategories表会看到一条记录。
- Asp.NET Core+ABP框架+IdentityServer4+MySQL+Ext JS之添加实体
- Asp.NET Core+ABP框架+IdentityServer4+MySQL+Ext JS之配置IdentityServer
- Asp.NET Core+ABP框架+IdentityServer4+MySQL+Ext JS之数据迁移
- Asp.NET Core+ABP框架+IdentityServer4+MySQL+Ext JS之显示登录视图
- 尝试新的开发组合:Asp.NET Core+ABP框架+IdentityServer4+MySQL+Ext JS
- ASP.NET Core的身份认证框架IdentityServer4(3)-术语的解释
- ASP.NET Core的身份认证框架IdentityServer4(7)- 使用客户端证书控制API访问
- ASP.NET Core的身份认证框架IdentityServer4(7)- 使用客户端证书控制API访问
- IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架学习保护API
- ABP官方文档(四十一)【ASP.NET Core】
- 基于DDD的现代ASP.NET开发框架--ABP系列之2、ABP入门教程
- 开始使用ABP.CORE模板 (ASP.NET Core with Angular)
- .net FrameWork WebAPI 如何添加保护接口,授权服务器是.net Core 的IdentityServer4 ,并且对signalr进行保护
- Asp.Net Core-添加用户
- Asp.Net Core 连接MySQL
- ASP.NET Core开发-使用Nancy框架
- Asp.net+Vue2构建简单记账WebApp之二(使用ABP迅速搭建.Net后台)
- 详解ASP.NET 中的ADO.NET实体框架
- kafka源码分析二
- Java内存区域与内存溢出异常
- H分量旋转法
- 自动化运维工具puppet一:puppet资源及单机模型
- qdu 15信安 vjudge
- Asp.NET Core+ABP框架+IdentityServer4+MySQL+Ext JS之添加实体
- leetcode 684. Redundant Connection 邻接表的环的判断 + 深度优先遍历DFS
- android 中遥控器键值的添加和修改
- 数据结构实验之排序五:归并求逆序数
- POJ 1088 滑雪
- jquery_ajax请求@RequestBody的controller时出现400或者415的错误_SpringBoot
- python chr()、unichr()和ord()
- 数字古籍图书馆
- 【raspberrypi】大神自制树莓派墨水屏