@ -0,0 +1,14 @@ |
|||
node_modules |
|||
npm-debug.log |
|||
Dockerfile* |
|||
docker-compose* |
|||
.dockerignore |
|||
.git |
|||
.gitignore |
|||
.env |
|||
*/bin |
|||
*/obj |
|||
README.md |
|||
LICENSE |
|||
.vscode |
|||
.vs |
|||
@ -0,0 +1,826 @@ |
|||
// <auto-generated />
|
|||
using System; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Microsoft.EntityFrameworkCore.Infrastructure; |
|||
using Microsoft.EntityFrameworkCore.Metadata; |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion; |
|||
using Volo.AbpWebSite.EntityFrameworkCore; |
|||
|
|||
namespace Volo.AbpWebSite.EntityFrameworkCore.Migrations |
|||
{ |
|||
[DbContext(typeof(AbpWebSiteDbContext))] |
|||
[Migration("20181227114311_Added_ConcurrencyStamp_IsDeleted_ExtraProperties")] |
|||
partial class Added_ConcurrencyStamp_IsDeleted_ExtraProperties |
|||
{ |
|||
protected override void BuildTargetModel(ModelBuilder modelBuilder) |
|||
{ |
|||
#pragma warning disable 612, 618
|
|||
modelBuilder |
|||
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024") |
|||
.HasAnnotation("Relational:MaxIdentifierLength", 128) |
|||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd(); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.IsRequired() |
|||
.HasColumnName("ConcurrencyStamp") |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<string>("ExtraProperties") |
|||
.HasColumnName("ExtraProperties"); |
|||
|
|||
b.Property<bool>("IsStatic"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<string>("Regex") |
|||
.HasMaxLength(512); |
|||
|
|||
b.Property<string>("RegexDescription") |
|||
.HasMaxLength(128); |
|||
|
|||
b.Property<bool>("Required"); |
|||
|
|||
b.Property<int>("ValueType"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("AbpClaimTypes"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd(); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.IsRequired() |
|||
.HasColumnName("ConcurrencyStamp") |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<string>("ExtraProperties") |
|||
.HasColumnName("ExtraProperties"); |
|||
|
|||
b.Property<bool>("IsDefault") |
|||
.HasColumnName("IsDefault"); |
|||
|
|||
b.Property<bool>("IsPublic") |
|||
.HasColumnName("IsPublic"); |
|||
|
|||
b.Property<bool>("IsStatic") |
|||
.HasColumnName("IsStatic"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<string>("NormalizedName") |
|||
.IsRequired() |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<Guid?>("TenantId"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("NormalizedName"); |
|||
|
|||
b.ToTable("AbpRoles"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd(); |
|||
|
|||
b.Property<string>("ClaimType") |
|||
.IsRequired() |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<string>("ClaimValue") |
|||
.HasMaxLength(1024); |
|||
|
|||
b.Property<Guid>("RoleId"); |
|||
|
|||
b.Property<Guid?>("TenantId"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("RoleId"); |
|||
|
|||
b.ToTable("AbpRoleClaims"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd(); |
|||
|
|||
b.Property<int>("AccessFailedCount") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnName("AccessFailedCount") |
|||
.HasDefaultValue(0); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnName("ConcurrencyStamp"); |
|||
|
|||
b.Property<DateTime>("CreationTime") |
|||
.HasColumnName("CreationTime"); |
|||
|
|||
b.Property<Guid?>("CreatorId") |
|||
.HasColumnName("CreatorId"); |
|||
|
|||
b.Property<Guid?>("DeleterId") |
|||
.HasColumnName("DeleterId"); |
|||
|
|||
b.Property<DateTime?>("DeletionTime") |
|||
.HasColumnName("DeletionTime"); |
|||
|
|||
b.Property<string>("Email") |
|||
.HasColumnName("Email") |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<bool>("EmailConfirmed") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnName("EmailConfirmed") |
|||
.HasDefaultValue(false); |
|||
|
|||
b.Property<string>("ExtraProperties") |
|||
.HasColumnName("ExtraProperties"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnName("IsDeleted") |
|||
.HasDefaultValue(false); |
|||
|
|||
b.Property<DateTime?>("LastModificationTime") |
|||
.HasColumnName("LastModificationTime"); |
|||
|
|||
b.Property<Guid?>("LastModifierId") |
|||
.HasColumnName("LastModifierId"); |
|||
|
|||
b.Property<bool>("LockoutEnabled") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnName("LockoutEnabled") |
|||
.HasDefaultValue(false); |
|||
|
|||
b.Property<DateTimeOffset?>("LockoutEnd"); |
|||
|
|||
b.Property<string>("Name") |
|||
.HasColumnName("Name") |
|||
.HasMaxLength(64); |
|||
|
|||
b.Property<string>("NormalizedEmail") |
|||
.HasColumnName("NormalizedEmail") |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<string>("NormalizedUserName") |
|||
.IsRequired() |
|||
.HasColumnName("NormalizedUserName") |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<string>("PasswordHash") |
|||
.HasColumnName("PasswordHash") |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<string>("PhoneNumber") |
|||
.HasColumnName("PhoneNumber") |
|||
.HasMaxLength(16); |
|||
|
|||
b.Property<bool>("PhoneNumberConfirmed") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnName("PhoneNumberConfirmed") |
|||
.HasDefaultValue(false); |
|||
|
|||
b.Property<string>("SecurityStamp") |
|||
.IsRequired() |
|||
.HasColumnName("SecurityStamp") |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<string>("Surname") |
|||
.HasColumnName("Surname") |
|||
.HasMaxLength(64); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnName("TenantId"); |
|||
|
|||
b.Property<bool>("TwoFactorEnabled") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnName("TwoFactorEnabled") |
|||
.HasDefaultValue(false); |
|||
|
|||
b.Property<string>("UserName") |
|||
.IsRequired() |
|||
.HasColumnName("UserName") |
|||
.HasMaxLength(256); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Email"); |
|||
|
|||
b.HasIndex("NormalizedEmail"); |
|||
|
|||
b.HasIndex("NormalizedUserName"); |
|||
|
|||
b.HasIndex("UserName"); |
|||
|
|||
b.ToTable("AbpUsers"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd(); |
|||
|
|||
b.Property<string>("ClaimType") |
|||
.IsRequired() |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<string>("ClaimValue") |
|||
.HasMaxLength(1024); |
|||
|
|||
b.Property<Guid?>("TenantId"); |
|||
|
|||
b.Property<Guid>("UserId"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("UserId"); |
|||
|
|||
b.ToTable("AbpUserClaims"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => |
|||
{ |
|||
b.Property<Guid>("UserId"); |
|||
|
|||
b.Property<string>("LoginProvider") |
|||
.HasMaxLength(64); |
|||
|
|||
b.Property<string>("ProviderDisplayName") |
|||
.HasMaxLength(128); |
|||
|
|||
b.Property<string>("ProviderKey") |
|||
.IsRequired() |
|||
.HasMaxLength(196); |
|||
|
|||
b.Property<Guid?>("TenantId"); |
|||
|
|||
b.HasKey("UserId", "LoginProvider"); |
|||
|
|||
b.HasIndex("LoginProvider", "ProviderKey"); |
|||
|
|||
b.ToTable("AbpUserLogins"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => |
|||
{ |
|||
b.Property<Guid>("UserId"); |
|||
|
|||
b.Property<Guid>("RoleId"); |
|||
|
|||
b.Property<Guid?>("TenantId"); |
|||
|
|||
b.HasKey("UserId", "RoleId"); |
|||
|
|||
b.HasIndex("RoleId", "UserId"); |
|||
|
|||
b.ToTable("AbpUserRoles"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => |
|||
{ |
|||
b.Property<Guid>("UserId"); |
|||
|
|||
b.Property<string>("LoginProvider") |
|||
.HasMaxLength(64); |
|||
|
|||
b.Property<string>("Name") |
|||
.HasMaxLength(128); |
|||
|
|||
b.Property<Guid?>("TenantId"); |
|||
|
|||
b.Property<string>("Value"); |
|||
|
|||
b.HasKey("UserId", "LoginProvider", "Name"); |
|||
|
|||
b.ToTable("AbpUserTokens"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd(); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(128); |
|||
|
|||
b.Property<string>("ProviderKey") |
|||
.IsRequired() |
|||
.HasMaxLength(64); |
|||
|
|||
b.Property<string>("ProviderName") |
|||
.IsRequired() |
|||
.HasMaxLength(64); |
|||
|
|||
b.Property<Guid?>("TenantId"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Name", "ProviderName", "ProviderKey"); |
|||
|
|||
b.ToTable("AbpPermissionGrants"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd(); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(128); |
|||
|
|||
b.Property<string>("ProviderKey") |
|||
.HasMaxLength(64); |
|||
|
|||
b.Property<string>("ProviderName") |
|||
.HasMaxLength(64); |
|||
|
|||
b.Property<string>("Value") |
|||
.IsRequired() |
|||
.HasMaxLength(2048); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("Name", "ProviderName", "ProviderKey"); |
|||
|
|||
b.ToTable("AbpSettings"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Blogging.Blogs.Blog", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd(); |
|||
|
|||
b.Property<string>("ConcurrencyStamp"); |
|||
|
|||
b.Property<DateTime>("CreationTime") |
|||
.HasColumnName("CreationTime"); |
|||
|
|||
b.Property<Guid?>("CreatorId") |
|||
.HasColumnName("CreatorId"); |
|||
|
|||
b.Property<Guid?>("DeleterId") |
|||
.HasColumnName("DeleterId"); |
|||
|
|||
b.Property<DateTime?>("DeletionTime") |
|||
.HasColumnName("DeletionTime"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasColumnName("Description") |
|||
.HasMaxLength(1024); |
|||
|
|||
b.Property<string>("ExtraProperties") |
|||
.HasColumnName("ExtraProperties"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnName("IsDeleted") |
|||
.HasDefaultValue(false); |
|||
|
|||
b.Property<DateTime?>("LastModificationTime") |
|||
.HasColumnName("LastModificationTime"); |
|||
|
|||
b.Property<Guid?>("LastModifierId") |
|||
.HasColumnName("LastModifierId"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasColumnName("Name") |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<string>("ShortName") |
|||
.IsRequired() |
|||
.HasColumnName("ShortName") |
|||
.HasMaxLength(32); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("BlgBlogs"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Blogging.Comments.Comment", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd(); |
|||
|
|||
b.Property<string>("ConcurrencyStamp"); |
|||
|
|||
b.Property<DateTime>("CreationTime") |
|||
.HasColumnName("CreationTime"); |
|||
|
|||
b.Property<Guid?>("CreatorId") |
|||
.HasColumnName("CreatorId"); |
|||
|
|||
b.Property<Guid?>("DeleterId") |
|||
.HasColumnName("DeleterId"); |
|||
|
|||
b.Property<DateTime?>("DeletionTime") |
|||
.HasColumnName("DeletionTime"); |
|||
|
|||
b.Property<string>("ExtraProperties") |
|||
.HasColumnName("ExtraProperties"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnName("IsDeleted") |
|||
.HasDefaultValue(false); |
|||
|
|||
b.Property<DateTime?>("LastModificationTime") |
|||
.HasColumnName("LastModificationTime"); |
|||
|
|||
b.Property<Guid?>("LastModifierId") |
|||
.HasColumnName("LastModifierId"); |
|||
|
|||
b.Property<Guid>("PostId") |
|||
.HasColumnName("PostId"); |
|||
|
|||
b.Property<Guid?>("RepliedCommentId") |
|||
.HasColumnName("RepliedCommentId"); |
|||
|
|||
b.Property<string>("Text") |
|||
.IsRequired() |
|||
.HasColumnName("Text") |
|||
.HasMaxLength(1024); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("PostId"); |
|||
|
|||
b.HasIndex("RepliedCommentId"); |
|||
|
|||
b.ToTable("BlgComments"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Blogging.Posts.Post", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd(); |
|||
|
|||
b.Property<Guid>("BlogId") |
|||
.HasColumnName("BlogId"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp"); |
|||
|
|||
b.Property<string>("Content") |
|||
.HasColumnName("Content") |
|||
.HasMaxLength(1048576); |
|||
|
|||
b.Property<string>("CoverImage") |
|||
.IsRequired() |
|||
.HasColumnName("CoverImage"); |
|||
|
|||
b.Property<DateTime>("CreationTime") |
|||
.HasColumnName("CreationTime"); |
|||
|
|||
b.Property<Guid?>("CreatorId") |
|||
.HasColumnName("CreatorId"); |
|||
|
|||
b.Property<Guid?>("DeleterId") |
|||
.HasColumnName("DeleterId"); |
|||
|
|||
b.Property<DateTime?>("DeletionTime") |
|||
.HasColumnName("DeletionTime"); |
|||
|
|||
b.Property<string>("ExtraProperties") |
|||
.HasColumnName("ExtraProperties"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnName("IsDeleted") |
|||
.HasDefaultValue(false); |
|||
|
|||
b.Property<DateTime?>("LastModificationTime") |
|||
.HasColumnName("LastModificationTime"); |
|||
|
|||
b.Property<Guid?>("LastModifierId") |
|||
.HasColumnName("LastModifierId"); |
|||
|
|||
b.Property<int>("ReadCount"); |
|||
|
|||
b.Property<string>("Title") |
|||
.IsRequired() |
|||
.HasColumnName("Title") |
|||
.HasMaxLength(512); |
|||
|
|||
b.Property<string>("Url") |
|||
.IsRequired() |
|||
.HasColumnName("Url") |
|||
.HasMaxLength(64); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.HasIndex("BlogId"); |
|||
|
|||
b.ToTable("BlgPosts"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Blogging.Posts.PostTag", b => |
|||
{ |
|||
b.Property<Guid>("PostId") |
|||
.HasColumnName("PostId"); |
|||
|
|||
b.Property<Guid>("TagId") |
|||
.HasColumnName("TagId"); |
|||
|
|||
b.Property<DateTime>("CreationTime"); |
|||
|
|||
b.Property<Guid?>("CreatorId"); |
|||
|
|||
b.HasKey("PostId", "TagId"); |
|||
|
|||
b.HasIndex("TagId"); |
|||
|
|||
b.ToTable("BlgPostTags"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Blogging.Tagging.Tag", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd(); |
|||
|
|||
b.Property<Guid>("BlogId"); |
|||
|
|||
b.Property<string>("ConcurrencyStamp"); |
|||
|
|||
b.Property<DateTime>("CreationTime") |
|||
.HasColumnName("CreationTime"); |
|||
|
|||
b.Property<Guid?>("CreatorId") |
|||
.HasColumnName("CreatorId"); |
|||
|
|||
b.Property<Guid?>("DeleterId") |
|||
.HasColumnName("DeleterId"); |
|||
|
|||
b.Property<DateTime?>("DeletionTime") |
|||
.HasColumnName("DeletionTime"); |
|||
|
|||
b.Property<string>("Description") |
|||
.HasColumnName("Description") |
|||
.HasMaxLength(512); |
|||
|
|||
b.Property<string>("ExtraProperties") |
|||
.HasColumnName("ExtraProperties"); |
|||
|
|||
b.Property<bool>("IsDeleted") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnName("IsDeleted") |
|||
.HasDefaultValue(false); |
|||
|
|||
b.Property<DateTime?>("LastModificationTime") |
|||
.HasColumnName("LastModificationTime"); |
|||
|
|||
b.Property<Guid?>("LastModifierId") |
|||
.HasColumnName("LastModifierId"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasColumnName("Name") |
|||
.HasMaxLength(64); |
|||
|
|||
b.Property<int>("UsageCount") |
|||
.HasColumnName("UsageCount"); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("BlgTags"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Blogging.Users.BlogUser", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd(); |
|||
|
|||
b.Property<string>("ConcurrencyStamp"); |
|||
|
|||
b.Property<string>("Email") |
|||
.HasColumnName("Email") |
|||
.HasMaxLength(256); |
|||
|
|||
b.Property<bool>("EmailConfirmed") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnName("EmailConfirmed") |
|||
.HasDefaultValue(false); |
|||
|
|||
b.Property<string>("ExtraProperties") |
|||
.HasColumnName("ExtraProperties"); |
|||
|
|||
b.Property<string>("Name") |
|||
.HasColumnName("Name") |
|||
.HasMaxLength(64); |
|||
|
|||
b.Property<string>("PhoneNumber") |
|||
.HasColumnName("PhoneNumber") |
|||
.HasMaxLength(16); |
|||
|
|||
b.Property<bool>("PhoneNumberConfirmed") |
|||
.ValueGeneratedOnAdd() |
|||
.HasColumnName("PhoneNumberConfirmed") |
|||
.HasDefaultValue(false); |
|||
|
|||
b.Property<string>("Surname") |
|||
.HasColumnName("Surname") |
|||
.HasMaxLength(64); |
|||
|
|||
b.Property<Guid?>("TenantId") |
|||
.HasColumnName("TenantId"); |
|||
|
|||
b.Property<string>("UserName") |
|||
.IsRequired() |
|||
.HasColumnName("UserName") |
|||
.HasMaxLength(256); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("BlgUsers"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Docs.Projects.Project", b => |
|||
{ |
|||
b.Property<Guid>("Id") |
|||
.ValueGeneratedOnAdd(); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnName("ConcurrencyStamp"); |
|||
|
|||
b.Property<string>("DefaultDocumentName") |
|||
.IsRequired() |
|||
.HasMaxLength(128); |
|||
|
|||
b.Property<string>("DocumentStoreType"); |
|||
|
|||
b.Property<string>("ExtraProperties") |
|||
.HasColumnName("ExtraProperties"); |
|||
|
|||
b.Property<string>("Format"); |
|||
|
|||
b.Property<string>("LatestVersionBranchName") |
|||
.HasMaxLength(128); |
|||
|
|||
b.Property<string>("MainWebsiteUrl"); |
|||
|
|||
b.Property<string>("MinimumVersion"); |
|||
|
|||
b.Property<string>("Name") |
|||
.IsRequired() |
|||
.HasMaxLength(128); |
|||
|
|||
b.Property<string>("NavigationDocumentName") |
|||
.IsRequired() |
|||
.HasMaxLength(128); |
|||
|
|||
b.Property<string>("ShortName") |
|||
.IsRequired() |
|||
.HasMaxLength(32); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("DocsProjects"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Utils.SolutionTemplating.DownloadInfo", b => |
|||
{ |
|||
b.Property<int>("Id") |
|||
.ValueGeneratedOnAdd() |
|||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); |
|||
|
|||
b.Property<string>("ConcurrencyStamp") |
|||
.IsConcurrencyToken() |
|||
.HasColumnName("ConcurrencyStamp"); |
|||
|
|||
b.Property<int>("CreationDuration"); |
|||
|
|||
b.Property<DateTime>("CreationTime") |
|||
.HasColumnName("CreationTime"); |
|||
|
|||
b.Property<Guid?>("CreatorId") |
|||
.HasColumnName("CreatorId"); |
|||
|
|||
b.Property<byte>("DatabaseProvider"); |
|||
|
|||
b.Property<string>("ExtraProperties") |
|||
.HasColumnName("ExtraProperties"); |
|||
|
|||
b.Property<string>("ProjectName") |
|||
.IsRequired() |
|||
.HasMaxLength(128); |
|||
|
|||
b.Property<string>("TemplateName") |
|||
.IsRequired() |
|||
.HasMaxLength(42); |
|||
|
|||
b.Property<string>("Version") |
|||
.IsRequired() |
|||
.HasMaxLength(20); |
|||
|
|||
b.HasKey("Id"); |
|||
|
|||
b.ToTable("Downloads"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => |
|||
{ |
|||
b.HasOne("Volo.Abp.Identity.IdentityRole") |
|||
.WithMany("Claims") |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => |
|||
{ |
|||
b.HasOne("Volo.Abp.Identity.IdentityUser") |
|||
.WithMany("Claims") |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Cascade); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => |
|||
{ |
|||
b.HasOne("Volo.Abp.Identity.IdentityUser") |
|||
.WithMany("Logins") |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Cascade); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => |
|||
{ |
|||
b.HasOne("Volo.Abp.Identity.IdentityRole") |
|||
.WithMany() |
|||
.HasForeignKey("RoleId") |
|||
.OnDelete(DeleteBehavior.Cascade); |
|||
|
|||
b.HasOne("Volo.Abp.Identity.IdentityUser") |
|||
.WithMany("Roles") |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Cascade); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => |
|||
{ |
|||
b.HasOne("Volo.Abp.Identity.IdentityUser") |
|||
.WithMany("Tokens") |
|||
.HasForeignKey("UserId") |
|||
.OnDelete(DeleteBehavior.Cascade); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Blogging.Comments.Comment", b => |
|||
{ |
|||
b.HasOne("Volo.Blogging.Posts.Post") |
|||
.WithMany() |
|||
.HasForeignKey("PostId") |
|||
.OnDelete(DeleteBehavior.Cascade); |
|||
|
|||
b.HasOne("Volo.Blogging.Comments.Comment") |
|||
.WithMany() |
|||
.HasForeignKey("RepliedCommentId"); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Blogging.Posts.Post", b => |
|||
{ |
|||
b.HasOne("Volo.Blogging.Blogs.Blog") |
|||
.WithMany() |
|||
.HasForeignKey("BlogId") |
|||
.OnDelete(DeleteBehavior.Cascade); |
|||
}); |
|||
|
|||
modelBuilder.Entity("Volo.Blogging.Posts.PostTag", b => |
|||
{ |
|||
b.HasOne("Volo.Blogging.Posts.Post") |
|||
.WithMany("Tags") |
|||
.HasForeignKey("PostId") |
|||
.OnDelete(DeleteBehavior.Cascade); |
|||
|
|||
b.HasOne("Volo.Blogging.Tagging.Tag") |
|||
.WithMany() |
|||
.HasForeignKey("TagId") |
|||
.OnDelete(DeleteBehavior.Cascade); |
|||
}); |
|||
#pragma warning restore 612, 618
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
using Microsoft.EntityFrameworkCore.Migrations; |
|||
|
|||
namespace Volo.AbpWebSite.EntityFrameworkCore.Migrations |
|||
{ |
|||
public partial class Added_ConcurrencyStamp_IsDeleted_ExtraProperties : Migration |
|||
{ |
|||
protected override void Up(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.AlterColumn<bool>( |
|||
name: "IsDeleted", |
|||
table: "AbpUsers", |
|||
nullable: false, |
|||
defaultValue: false, |
|||
oldClrType: typeof(bool)); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "ConcurrencyStamp", |
|||
table: "AbpUsers", |
|||
nullable: true, |
|||
oldClrType: typeof(string), |
|||
oldMaxLength: 256); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "ConcurrencyStamp", |
|||
table: "AbpRoles", |
|||
maxLength: 256, |
|||
nullable: false, |
|||
oldClrType: typeof(string), |
|||
oldNullable: true); |
|||
|
|||
migrationBuilder.AddColumn<string>( |
|||
name: "ConcurrencyStamp", |
|||
table: "AbpClaimTypes", |
|||
maxLength: 256, |
|||
nullable: false, |
|||
defaultValue: ""); |
|||
|
|||
migrationBuilder.AddColumn<string>( |
|||
name: "ExtraProperties", |
|||
table: "AbpClaimTypes", |
|||
nullable: true); |
|||
} |
|||
|
|||
protected override void Down(MigrationBuilder migrationBuilder) |
|||
{ |
|||
migrationBuilder.DropColumn( |
|||
name: "ConcurrencyStamp", |
|||
table: "AbpClaimTypes"); |
|||
|
|||
migrationBuilder.DropColumn( |
|||
name: "ExtraProperties", |
|||
table: "AbpClaimTypes"); |
|||
|
|||
migrationBuilder.AlterColumn<bool>( |
|||
name: "IsDeleted", |
|||
table: "AbpUsers", |
|||
nullable: false, |
|||
oldClrType: typeof(bool), |
|||
oldDefaultValue: false); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "ConcurrencyStamp", |
|||
table: "AbpUsers", |
|||
maxLength: 256, |
|||
nullable: false, |
|||
oldClrType: typeof(string), |
|||
oldNullable: true); |
|||
|
|||
migrationBuilder.AlterColumn<string>( |
|||
name: "ConcurrencyStamp", |
|||
table: "AbpRoles", |
|||
nullable: true, |
|||
oldClrType: typeof(string), |
|||
oldMaxLength: 256); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
using Serilog.Core; |
|||
using Serilog.Events; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Tracing; |
|||
|
|||
namespace Volo.AbpWebSite |
|||
{ |
|||
//This is for trial for now
|
|||
public class CorrelationIdLogEventEnricher : ILogEventEnricher, ITransientDependency |
|||
{ |
|||
private readonly ICorrelationIdProvider _correlationIdProvider; |
|||
|
|||
public CorrelationIdLogEventEnricher(ICorrelationIdProvider correlationIdProvider) |
|||
{ |
|||
_correlationIdProvider = correlationIdProvider; |
|||
} |
|||
|
|||
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) |
|||
{ |
|||
logEvent.AddOrUpdateProperty( |
|||
new LogEventProperty( |
|||
"CorrelationId", |
|||
new ScalarValue("CorrId:" + _correlationIdProvider.Get()) |
|||
) |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -1,50 +0,0 @@ |
|||
# COMMON PATHS |
|||
|
|||
$buildFolder = (Get-Item -Path "./" -Verbose).FullName |
|||
$slnFolder = Join-Path $buildFolder "../" |
|||
$outputFolder = Join-Path $buildFolder "outputs" |
|||
$abpDeskFolder = Join-Path $slnFolder "src/AbpDesk" |
|||
$abpDeskWebFolder = Join-Path $abpDeskFolder "AbpDesk.Web.Mvc" |
|||
|
|||
## CLEAR ###################################################################### |
|||
|
|||
Remove-Item $outputFolder -Force -Recurse |
|||
New-Item -Path $outputFolder -ItemType Directory |
|||
|
|||
## RESTORE NUGET PACKAGES ##################################################### |
|||
|
|||
Set-Location $slnFolder |
|||
dotnet restore |
|||
|
|||
## PUBLISH ASPDESK WEB ######################################################## |
|||
|
|||
Set-Location $abpDeskWebFolder |
|||
dotnet publish --output (Join-Path $outputFolder "AbpDesk/Web") |
|||
|
|||
New-Item -Path (Join-Path $outputFolder "AbpDesk/Web/PlugIns") -ItemType Directory |
|||
Copy-Item (Join-Path $abpDeskFolder "Web_PlugIns/*") (Join-Path $outputFolder "AbpDesk/Web/PlugIns/") |
|||
|
|||
## PUBLISH IDENTITY HTTP API HOST ############################################# |
|||
|
|||
Set-Location (Join-Path $slnFolder "src/Volo.Abp.Identity.HttpApi.Host") |
|||
dotnet publish --output (Join-Path $outputFolder "AbpIdentity/HttpApiHost") |
|||
|
|||
## CREATE DOCKER IMAGES ####################################################### |
|||
|
|||
Set-Location (Join-Path $outputFolder "AbpDesk/Web") |
|||
|
|||
docker rmi abpdesk/web -f |
|||
docker build -t abpdesk/web . |
|||
|
|||
Set-Location (Join-Path $outputFolder "AbpIdentity/HttpApiHost") |
|||
|
|||
docker rmi abpidentity/httpapihost -f |
|||
docker build -t abpidentity/httpapihost . |
|||
|
|||
## DOCKER COMPOSE FILES ####################################################### |
|||
|
|||
Copy-Item (Join-Path $slnFolder "docker/*.*") $outputFolder |
|||
|
|||
## FINALIZE ################################################################### |
|||
|
|||
Set-Location $outputFolder |
|||
@ -1,28 +0,0 @@ |
|||
version: '2' |
|||
|
|||
services: |
|||
|
|||
mongodb: |
|||
image: tutum/mongodb |
|||
environment: |
|||
- AUTH=no |
|||
ports: |
|||
- "27017:27017" |
|||
- "28017:28017" |
|||
|
|||
abpidentity_httpapihost: |
|||
image: abpidentity/httpapihost |
|||
environment: |
|||
- ASPNETCORE_ENVIRONMENT=Staging |
|||
|
|||
abpdesk_web: |
|||
image: abpdesk/web |
|||
environment: |
|||
- ASPNETCORE_ENVIRONMENT=Staging |
|||
|
|||
load_balancer: |
|||
image: haproxy:1.7.1 |
|||
volumes: |
|||
- "./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg" |
|||
ports: |
|||
- "9005:8080" |
|||
@ -1 +0,0 @@ |
|||
docker-compose down -v --rmi local |
|||
@ -1,18 +0,0 @@ |
|||
global |
|||
maxconn 4096 |
|||
|
|||
defaults |
|||
mode http |
|||
timeout connect 5s |
|||
timeout client 50s |
|||
timeout server 50s |
|||
|
|||
listen http-in |
|||
bind *:8080 |
|||
|
|||
server web-1 outputs_abpdesk_web_1:80 |
|||
server web-2 outputs_abpdesk_web_2:80 |
|||
|
|||
stats enable |
|||
stats uri /haproxy |
|||
stats refresh 1s |
|||
@ -1,8 +0,0 @@ |
|||
docker rm $(docker ps -aq) |
|||
docker-compose up -d mongodb |
|||
docker-compose up -d abpidentity_httpapihost |
|||
docker-compose up -d abpdesk_web |
|||
sleep 2 |
|||
docker-compose scale abpdesk_web=2 |
|||
sleep 2 |
|||
docker-compose up -d load_balancer |
|||
@ -0,0 +1,138 @@ |
|||
# Auto API Controllers |
|||
|
|||
Once you create an [application service](Application-Services.md), you generally want to create an API controller to expose this service as an HTTP (REST) API endpoint. A typical API controller does nothing but redirects method calls to the application service and configures the REST API using attributes like [HttpGet], [HttpPost], [Route]... etc. |
|||
|
|||
ABP can **automagically** configures your application services as MVC API Controllers by convention. Most of time you don't care about its detailed configuration, but it's possible fully customize it. |
|||
|
|||
## Configuration |
|||
|
|||
Basic configuration is simple. Just configure `AbpAspNetCoreMvcOptions` and use `ConventionalControllers.Create` method as shown below: |
|||
|
|||
````csharp |
|||
[DependsOn(BookStoreApplicationModule)] |
|||
public class BookStoreWebModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options.ConventionalControllers.Create(typeof(BookStoreApplicationModule).Assembly); |
|||
}); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
This example code configures all the application services in the assembly containing the class `BookStoreApplicationModule`. The figure below shows the resulting API on the [Swagger UI](https://swagger.io/tools/swagger-ui/). |
|||
|
|||
 |
|||
|
|||
### Examples |
|||
|
|||
Some example method names and the corresponding routes calculated by convention: |
|||
|
|||
| Service Method Name | HTTP Method | Route | |
|||
| ----------------------------------------------------- | ----------- | -------------------------- | |
|||
| GetAsync(Guid id) | GET | /api/app/book/{id} | |
|||
| GetListAsync() | GET | /api/app/book | |
|||
| CreateAsync(CreateBookDto input) | POST | /api/app/book | |
|||
| UpdateAsync(Guid id, UpdateBookDto input) | PUT | /api/app/book/{id} | |
|||
| DeleteAsync(Guid id) | DELETE | /api/app/book/{id} | |
|||
| GetEditorsAsync(Guid id) | GET | /api/app/book/{id}/editors | |
|||
| CreateEditorAsync(Guid id, BookEditorCreateDto input) | POST | /api/app/book/{id}/editor | |
|||
|
|||
### HTTP Method |
|||
|
|||
ABP uses a naming convention while determining the HTTP method for a service method (action): |
|||
|
|||
- **Get**: Used if the method name starts with 'GetList', 'GetAll' or 'Get'. |
|||
- **Put**: Used if the method name starts with 'Put' or 'Update'. |
|||
- **Delete**: Used if the method name starts with 'Delete' or 'Remove'. |
|||
- **Post**: Used if the method name starts with 'Create', 'Add', 'Insert' or 'Post'. |
|||
- **Patch**: Used if the method name starts with 'Patch'. |
|||
- Otherwise, **Post** is used **by default**. |
|||
|
|||
If you need to customize HTTP method for a particular method, then you can use one of the standard ASP.NET Core attributes ([HttpPost], [HttpGet], [HttpPut]... etc.). This requires to add [Microsoft.AspNetCore.Mvc.Core](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.Core) nuget package to your project that contains the service. |
|||
|
|||
### Route |
|||
|
|||
Route is calculated based on some conventions: |
|||
|
|||
* It always starts with '**/api**'. |
|||
* Continues with a **route path**. Default value is '**/app**' and can be configured as like below: |
|||
|
|||
````csharp |
|||
Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options.ConventionalControllers |
|||
.Create(typeof(BookStoreApplicationModule).Assembly, opts => |
|||
{ |
|||
opts.RootPath = "volosoft/book-store"; |
|||
}); |
|||
}); |
|||
```` |
|||
|
|||
Then the route for getting a book will be '**/api/volosoft/book-store/book/{id}**'. This sample uses two-level root path, but you generally use a single level depth. |
|||
|
|||
* Continues with the **normalized controller/service name**. Normalization removes 'AppService', 'ApplicationService' and 'Service' postfixes and converts it to **camelCase**. If your application service class name is 'BookAppService' then it becomes only '/book'. |
|||
* If you want to customize naming, then set the `UrlControllerNameNormalizer` option. It's a func delegate which allows you to determine the name per controller/service. |
|||
* If the method has an '**id**' parameter then it adds '**/{id}**' ro the route. |
|||
* Then it adds the action name if necessary. Action name is obtained from the method name on the service and normalized by; |
|||
* Removing '**Async**' postfix. If the method name is 'GetPhonesAsync' then it becomes 'GetPhones'. |
|||
* Removing **HTTP method prefix**. 'GetList', 'GetAll', 'Get', 'Put', 'Update', 'Delete', 'Remove', 'Create', 'Add', 'Insert', 'Post' and 'Patch' prefixes are removed based on the selected HTTP method. So, 'GetPhones' becomes 'Phones' since 'Get' prefix is a duplicate for a GET request. |
|||
* Converting the result to **camelCase**. |
|||
* If the resulting action name is **empty** then it's not added to the route. If it's not empty, it's added to the route (like '/phones'). For 'GetAllAsync' method name it will be empty, for 'GetPhonesAsync' method name is will be 'phones'. |
|||
* Normalization can be customized by setting the `UrlActionNameNormalizer` option. It's an action delegate that is called for every method. |
|||
* If there is another parameter with 'Id' postfix, then it's also added to the route as the final route segment (like '/phoneId'). |
|||
|
|||
## Service Selection |
|||
|
|||
Creating conventional HTTP API controllers are not unique to application services actually. |
|||
|
|||
### IRemoteService Interface |
|||
|
|||
If a class implements the `IRemoteService` interface then it's automatically selected to be a conventional API controller. Since application services inherently implement it, they are considered as natural API controllers. |
|||
|
|||
### RemoteService Attribute |
|||
|
|||
`RemoteService` attribute can be used to mark a class as a remote service or disable for a particular class that inherently implements the `IRemoteService` interface. Example: |
|||
|
|||
````csharp |
|||
[RemoteService(IsEnabled = false)] //or simply [RemoteService(false)] |
|||
public class PersonAppService : ApplicationService |
|||
{ |
|||
|
|||
} |
|||
```` |
|||
|
|||
### TypePredicate Option |
|||
|
|||
You can further filter classes to become an API controller by providing the `TypePedicate` option: |
|||
|
|||
````csharp |
|||
services.Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options.ConventionalControllers |
|||
.Create(typeof(BookStoreApplicationModule).Assembly, opts => |
|||
{ |
|||
opts.TypePredicate = type => { return true; }; |
|||
}); |
|||
}); |
|||
```` |
|||
|
|||
Instead of returning `true` for every type, you can check it and return `false` if you don't want to expose this type as an API controller. |
|||
|
|||
## API Explorer |
|||
|
|||
API Exploring a service that makes possible to investigate API structure by the clients. Swagger uses it to create a documentation and test UI for an endpoint. |
|||
|
|||
API Explorer is automatically enabled for conventional HTTP API controllers by default. Use `RemoteService` attribute to control it per class or method level. Example: |
|||
|
|||
````csharp |
|||
[RemoteService(IsMetadataEnabled = false)] |
|||
public class PersonAppService : ApplicationService |
|||
{ |
|||
|
|||
} |
|||
```` |
|||
|
|||
Disabled `IsMetadataEnabled` which hides this service from API explorer and it will not be discoverable. However, it still can be usable for the clients know the exact API path/route. |
|||
@ -0,0 +1,165 @@ |
|||
# Dynamic C# API Clients |
|||
|
|||
ABP can dynamically create C# API client proxies to call remote HTTP services (REST APIs). In this way, you don't need to deal with `HttpClient` and other low level HTTP features to call remote services and get results. |
|||
|
|||
## Service Interface |
|||
|
|||
Your service/controller should implement an interface that is shared between the server and the client. So, first define a service interface in a shared library project. Example: |
|||
|
|||
````csharp |
|||
public interface IBookAppService : IApplicationService |
|||
{ |
|||
Task<List<BookDto>> GetListAsync(); |
|||
} |
|||
```` |
|||
|
|||
Your interface should implement the `IRemoteService` interface to be automatically discovered. Since the `IApplicationService` inherits the `IRemoteService` interface, the `IBookAppService` above satisfies this condition. |
|||
|
|||
Implement this class in your service application. You can use [auto API controller system](Auto-API-Controllers.md) to expose the service as a REST API endpoint. |
|||
|
|||
## Client Proxy Generation |
|||
|
|||
First, add [Volo.Abp.Http.Client](https://www.nuget.org/packages/Volo.Abp.Http.Client) nuget package to your client project: |
|||
|
|||
```` |
|||
Install-Package Volo.Abp.Http.Client |
|||
```` |
|||
|
|||
Then add `AbpHttpClientModule` dependency to your module: |
|||
|
|||
````csharp |
|||
[DependsOn(typeof(AbpHttpClientModule))] //add the dependency |
|||
public class MyClientAppModule : AbpModule |
|||
{ |
|||
} |
|||
```` |
|||
|
|||
Now, it's ready to create the client proxies. Example: |
|||
|
|||
````csharp |
|||
[DependsOn( |
|||
typeof(AbpHttpClientModule), //used to create client proxies |
|||
typeof(BookStoreApplicationModule) //contains the application service interfaces |
|||
)] |
|||
public class MyClientAppModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
//Create dynamic client proxies |
|||
context.Services.AddHttpClientProxies( |
|||
typeof(BookStoreApplicationModule).Assembly |
|||
); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`AddHttpClientProxies` method gets an assembly, finds all service interfaces in the given assembly, creates and registers proxy classes. |
|||
|
|||
### Endpoint Configuration |
|||
|
|||
`RemoteServices` section in the `appsettings.json` file is used to get remote service address by default. Simplest configuration is shown below: |
|||
|
|||
```` |
|||
{ |
|||
"RemoteServices": { |
|||
"Default": { |
|||
"BaseUrl": "http://localhost:53929/" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
See the "RemoteServiceOptions" section below for more detailed configuration. |
|||
|
|||
## Usage |
|||
|
|||
It's straightforward to use. Just inject the service interface in the client application code: |
|||
|
|||
````csharp |
|||
public class MyService : ITransientDependency |
|||
{ |
|||
private readonly IBookAppService _bookService; |
|||
|
|||
public MyService(IBookAppService bookService) |
|||
{ |
|||
_bookService = bookService; |
|||
} |
|||
|
|||
public async Task DoIt() |
|||
{ |
|||
var books = await _bookService.GetListAsync(); |
|||
foreach (var book in books) |
|||
{ |
|||
Console.WriteLine($"[BOOK {book.Id}] Name={book.Name}"); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
This sample injects the `IBookAppService` service interface defined above. The dynamic client proxy implementation makes an HTTP call whenever a service method is called by the client. |
|||
|
|||
### IHttpClientProxy Interface |
|||
|
|||
While you can inject `IBookAppService` like above to use the client proxy, you could inject `IHttpClientProxy<IBookAppService>` for a more explicit usage. In this case you will use the `Service` property of the `IHttpClientProxy<T>` interface. |
|||
|
|||
## Configuration |
|||
|
|||
### RemoteServiceOptions |
|||
|
|||
`RemoteServiceOptions` is automatically set from the `appsettings.json` by default. Alternatively, you can use `Configure` method to set or override it. Example: |
|||
|
|||
````csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.Configure<RemoteServiceOptions>(options => |
|||
{ |
|||
options.RemoteServices.Default = |
|||
new RemoteServiceConfiguration("http://localhost:53929/"); |
|||
}); |
|||
|
|||
//... |
|||
} |
|||
```` |
|||
|
|||
### Multiple Remote Service Endpoints |
|||
|
|||
The examples above have configured the "Default" remote service endpoint. You may have different endpoints for different services (as like in a microservice approach where each microservice has different endpoints). In this case, you can add other endpoints to your configuration file: |
|||
|
|||
````json |
|||
{ |
|||
"RemoteServices": { |
|||
"Default": { |
|||
"BaseUrl": "http://localhost:53929/" |
|||
}, |
|||
"BookStore": { |
|||
"BaseUrl": "http://localhost:48392/" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`AddHttpClientProxies` method can get an additional parameter for the remote service name. Example: |
|||
|
|||
````csharp |
|||
context.Services.AddHttpClientProxies( |
|||
typeof(BookStoreApplicationModule).Assembly, |
|||
remoteServiceName: "BookStore" |
|||
); |
|||
```` |
|||
|
|||
`remoteServiceName` parameter matches the service endpoint configured via `RemoteServiceOptions`. If the `BookStore` endpoint is not defined then it fallbacks to the `Default` endpoint. |
|||
|
|||
### As Default Services |
|||
|
|||
When you create a service proxy for `IBookAppService`, you can directly inject the `IBookAppService` to use the proxy client (as shown in the usage section). You can pass `asDefaultServices: false` to the `AddHttpClientProxies` method to disable this feature. |
|||
|
|||
````csharp |
|||
context.Services.AddHttpClientProxies( |
|||
typeof(BookStoreApplicationModule).Assembly, |
|||
asDefaultServices: false |
|||
); |
|||
```` |
|||
|
|||
Using `asDefaultServices: false` may only be needed if your application has already an implementation of the service and you do not want to override/replace the other implementation by your client proxy. |
|||
|
|||
> If you disable `asDefaultServices`, you can only use `IHttpClientProxy<T>` interface to use the client proxies (see the related section above). |
|||
@ -0,0 +1,3 @@ |
|||
# Audit Logging |
|||
|
|||
TODO |
|||
@ -0,0 +1,54 @@ |
|||
# Microservice Demo, Projects Status and Road Map |
|||
|
|||
After [the first announcement](https://abp.io/blog/abp/Abp-vNext-Announcement) on the ABP vNext, we have a lot of improvements on the codebase (1100+ commits on the [GitHub repository](https://github.com/abpframework/abp)). We've created features, samples, documentation and much more. In this post, I want to inform you about some news and the status of the project. |
|||
|
|||
## Microservice Demo Solution |
|||
|
|||
One of the major goals of the ABP framework is to provide a [convenient infrastructure to create microservice solutions](https://abp.io/documents/abp/latest/Microservice-Architecture). |
|||
|
|||
We've been working to develop a microservice solution demo. Initial version was completed and [documented](https://abp.io/documents/abp/latest/Samples/Microservice-Demo). This sample solution aims to demonstrate a simple yet complete microservice solution; |
|||
|
|||
- Has multiple, independent, self-deployable **microservices**. |
|||
- Multiple **web applications**, each uses a different API gateway. |
|||
- Has multiple **gateways** / BFFs (Backend for Frontends) developed using the [Ocelot](https://github.com/ThreeMammals/Ocelot) library. |
|||
- Has an **authentication service** developed using the [IdentityServer](https://identityserver.io/) framework. It's also a SSO (Single Sign On) application with necessary UIs. |
|||
- Has **multiple databases**. Some microservices has their own database while some services/applications shares a database (to demonstrate different use cases). |
|||
- Has different types of databases: **SQL Server** (with **Entity Framework Core** ORM) and **MongoDB**. |
|||
- Has a **console application** to show the simplest way of using a service by authenticating. |
|||
- Uses [Redis](https://redis.io/) for **distributed caching**. |
|||
- Uses [RabbitMQ](https://www.rabbitmq.com/) for service-to-service **messaging**. |
|||
- Uses [Docker](https://www.docker.com/) & [Kubernates](https://kubernetes.io/) to **deploy** & run all services and applications. |
|||
- Uses [Elasticsearch](https://www.elastic.co/products/elasticsearch) & [Kibana](https://www.elastic.co/products/kibana) to store and visualize the logs (written using [Serilog](https://serilog.net/)). |
|||
|
|||
See [its documentation](https://abp.io/documents/abp/latest/Samples/Microservice-Demo) for a detailed explanation of the solution. |
|||
|
|||
## Improvements/Features |
|||
|
|||
We've worked on so many features including **distributed event bus** (with RabbitMQ integration), **IdentityServer4 integration** and enhancements for almost all features. We are continuously refactoring and adding tests to make the framework more stable and production ready. It is [rapidly growing](https://github.com/abpframework/abp/graphs/contributors). |
|||
|
|||
## Road Map |
|||
|
|||
There are still too much work to be done before the first stable release (v1.0). You can see [prioritized backlog items](https://github.com/abpframework/abp/issues?q=is%3Aopen+is%3Aissue+milestone%3ABacklog) on the GitHub repo. |
|||
|
|||
According to our estimation, we have planned to release v1.0 in Q2 of 2019 (probably in May or June). So, not too much time to wait. We are also very excited for the first stable release. |
|||
|
|||
We will also work on [the documentation](https://abp.io/documents/abp/latest) since it is far from complete now. |
|||
|
|||
First release may not include a SPA template. However, we want to prepare a simple one if it can be possible. Haven't decided yet about the SPA framework. Alternatives: **Angular, React and Blazor**. Please write your thought as a comment to this post. |
|||
|
|||
## Chinese Web Site |
|||
|
|||
There is a big ABP community in China. They have created a Chinese version of the abp.io web site: https://cn.abp.io/ They are keeping it up to date. Thanks to the Chinese developers and especially to [Liming Ma](https://github.com/maliming). |
|||
|
|||
## NDC {London} 2019 |
|||
|
|||
It was a pleasure to be in [NDC {London}](https://ndc-london.com/) 2019 as a partner. We've talked to many developers about the current ASP.NET Boilerplate and the ABP vNext and we got good feedbacks. |
|||
|
|||
We also had a chance to talk with [Scott Hanselman](https://twitter.com/shanselman) and [Jon Galloway](https://twitter.com/jongalloway). They visited our booth and we talked about the ideas for ABP vNext. They liked features, approaches and the goal of new ABP framework. See some photos and comments on twitter: |
|||
|
|||
 |
|||
|
|||
## Follow It |
|||
|
|||
* You can star and follow the **GitHub** repository: https://github.com/abpframework/abp |
|||
* You can follow the official **Twitter** account for news: https://twitter.com/abpframework |
|||
|
After Width: | Height: | Size: 477 KiB |
@ -0,0 +1,3 @@ |
|||
# Event Bus |
|||
|
|||
TODO |
|||
@ -0,0 +1,30 @@ |
|||
# Microservice Architecture |
|||
|
|||
*"Microservices are a software development technique—a variant of the **service-oriented architecture** (SOA) architectural style that structures an application as a collection of **loosely coupled services**. In a microservices architecture, services are **fine-grained** and the protocols are **lightweight**. The benefit of decomposing an application into different smaller services is that it improves **modularity**. This makes the application easier to understand, develop, test, and become more resilient to architecture erosion. It **parallelizes development** by enabling small autonomous teams to **develop, deploy and scale** their respective services independently. It also allows the architecture of an individual service to emerge through **continuous refactoring**. Microservices-based architectures enable **continuous delivery and deployment**."* |
|||
|
|||
— [Wikipedia](https://en.wikipedia.org/wiki/Microservices) |
|||
|
|||
## Introduction |
|||
|
|||
One of the major goals of the ABP framework is to provide a convenient infrastructure to create microservice solutions. To make this possible, |
|||
|
|||
* Provides a [module system](Module-Development-Basics.md) that allows you to split your application into modules where each module may have its own database, entities, services, APIs, UI components/pages... etc. |
|||
* Offers an [architectural model](Best-Practices/Module-Architecture.md) to develop your modules to be compatible to microservice development and deployment. |
|||
* Provides [best practices guide](Best-Practices/Index.md) to develop your module standards-compliance. |
|||
* Provides base infrastructure to implement [Domain Driven Design](Domain-Driven-Design.md) in your microservices. |
|||
* Provide services to [automatically create REST-style APIs](AspNetCore/Auto-API-Controllers.md) from your application services. |
|||
* Provide services to [automatically create C# API clients](AspNetCore/Dynamic-CSharp-API-Clients.md) that makes easy to consume your services from another service/application. |
|||
* Provides a [distributed event bus](Event-Bus.md) to communicate your services. |
|||
* Provides many other services to make your daily development easier. |
|||
|
|||
## Microservice for New Applications |
|||
|
|||
One common advise to start a new solution is **always to start with a monolith**, keep it modular and split into microservices once the monolith becomes a problem. This makes your progress fast in the beginning especially if your team is small and you don't want to deal with challanges of the microservice architecture. |
|||
|
|||
However, developing such a well-modular application can be a problem since it is **hard to keep modules isolated** from each other as you would do it for microservices (see [Stefan Tilkov's article](https://martinfowler.com/articles/dont-start-monolith.html) about that). Microservice architecture naturally forces you to develop well isolated services, but in a modular monolithic application it's easy to tight couple modules to each other and design **weak module boundaries** and API contracts. |
|||
|
|||
ABP can help you in that point by oferring a **microservice-compatible, strict module architecture** where your module is splitted into multiple layers/projects and developed in its own VS solution completely isolated and independent from other modules. Such a developed module is a natural microservice yet it can be easily plugged-in a monolithic application. See the [module development best practice guide](Best-Practices/Index.md) that offers a **microservice-first module design**. All [standard ABP modules](https://github.com/abpframework/abp/tree/master/modules) are developed based on this guide. So, you can use these modules by embedding into your monolithic solution or deploy them separately and use via remote APIs. They can share a single database or can have their own database based on your simple configuration. |
|||
|
|||
## Microservice Demo Solution |
|||
|
|||
The [sample microservice solution](Samples/Microservice-Demo.md) demonstrates a complete microservice solution based on the ABP framework. |
|||
@ -1,9 +1,475 @@ |
|||
# Docs Module |
|||
|
|||
Docs module is used to create technical documentation pages. ABP's [own documentation](https://abp.io/documents/) already using this module. |
|||
## What is Docs Module? |
|||
|
|||
Docs module is an application module for ABP framework. It simplifies software documentation. This module is free and open-source. |
|||
|
|||
### Integration |
|||
|
|||
Currently docs module provides you to store your docs both on GitHub and file system. |
|||
|
|||
### Hosting |
|||
|
|||
Docs module is an application module and does not offer any hosting solution. You can host your docs on-premise or on cloud. |
|||
|
|||
### Versioning |
|||
|
|||
When you use GitHub to store your docs, Docs Module supports versioning. If you have multiple versions for your docs, there will be a combo-box on the UI to switch between versions. If you choose file system to store your docs, it does not support multiple versions. |
|||
|
|||
[The documents](https://abp.io/documents/) for ABP framework is also using this module. |
|||
|
|||
> Docs module follows the [module architecture best practices](../Best-Practices/Module-Architecture.md) guide. |
|||
|
|||
|
|||
|
|||
## Installation |
|||
|
|||
TODO... |
|||
### 1- Download |
|||
|
|||
If you do not have an existing ABP project, this step shows you how to create a new project from [abp.io](https://abp.io) to add the Docs Module. If you already have an ABP project, you can skip this step. |
|||
|
|||
Navigate to https://abp.io/Templates. Enter your project name as `Acme.MyProject`, select `ASP.NET Core Mvc Application` and select `Entity Framework Core` for the database provider. |
|||
|
|||
Note that this document covers `Entity Framework Core` provider but you can also select `MongoDB` as your database provider. |
|||
|
|||
 |
|||
|
|||
### 2- Running The Empty Application |
|||
|
|||
After you download the project, extract the ZIP file and open `Acme.MyProject.sln`. You will see that the solution consists of `Application`, `Domain `, `EntityFrameworkCore` and `Web` projects. Right click on `Acme.MyProject.Web` project and **Set as StartUp Project**. |
|||
|
|||
 |
|||
|
|||
The database connection string is located in `appsettings.json` of your `Acme.MyProject.Web` project. If you have a different database configuration, change the connection string. |
|||
|
|||
```json |
|||
{ |
|||
"ConnectionStrings": { |
|||
"Default": "Server=localhost;Database=MyProject;Trusted_Connection=True;MultipleActiveResultSets=true" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
|
|||
|
|||
Open `Package Manager Console` in the Visual Studio and choose `src\Acme.MyProject.EntityFrameworkCore` as the default project. Run `Update-Database` command to create your new database. The database `MyProject` will be created in your database server. |
|||
|
|||
Now an empty ABP project has been created! You can now run your project and see the empty website. |
|||
|
|||
To login your website enter `admin` as the username and `1q2w3E*` as the password. |
|||
|
|||
### 2- Referencing Docs Module Packages |
|||
|
|||
Docs module packages are hosted on NuGet. There are 4 packages that needs be to installed to your application. Each package has to be installed to the relevant project. |
|||
|
|||
* [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) needs to be referenced to `Acme.MyProject.Domain` project. |
|||
|
|||
* Edit `Acme.MyProject.Domain.csproj`file and add the below line to as a reference. Note that you need to change version (v0.9.0) to the latest. |
|||
|
|||
```csharp |
|||
<PackageReference Include="Volo.Docs.Domain" Version="0.9.0" /> |
|||
``` |
|||
* [Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) needs to be referenced to `Acme.MyProject.EntityFrameworkCore` project. |
|||
|
|||
- Edit `Acme.MyProject.EntityFrameworkCore.csproj`file and add the below line to as a reference. Note that you need to change version (v0.9.0) to the latest. |
|||
|
|||
```csharp |
|||
<PackageReference Include="Volo.Docs.EntityFrameworkCore" Version="0.9.0" /> |
|||
``` |
|||
* [Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) needs to be referenced to `Acme.MyProject.Application` project. |
|||
|
|||
* Edit `Acme.MyProject.Application.csproj`file and add the below line to as a reference. Note that you need to change version (v0.9.0) to the latest. |
|||
|
|||
```csharp |
|||
<PackageReference Include="Volo.Docs.Application" Version="0.9.0" /> |
|||
``` |
|||
* [Volo.Docs.Web ](https://www.nuget.org/packages/Volo.Docs.Web/)needs to be referenced to `Acme.MyProject.Web` project. |
|||
|
|||
- Edit `Acme.MyProject.Web.csproj`file and add the below line to as a reference. Note that you need to change version (v0.9.0) to the latest. |
|||
|
|||
```csharp |
|||
<PackageReference Include="Volo.Docs.Web" Version="0.9.0" /> |
|||
``` |
|||
|
|||
|
|||
|
|||
### 3- Adding Module Dependencies |
|||
|
|||
An ABP module must declare `[DependsOn]` attribute if it has a dependency upon another module. Each module has to be added in`[DependsOn]` attribute to the relevant project. |
|||
|
|||
* Open `MyProjectDomainModule.cs`and add `typeof(DocsDomainModule)` as shown below; |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(DocsDomainModule), |
|||
typeof(AbpIdentityDomainModule), |
|||
typeof(AbpAuditingModule), |
|||
typeof(BackgroundJobsDomainModule), |
|||
typeof(AbpAuditLoggingDomainModule) |
|||
)] |
|||
public class MyProjectDomainModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
* Open `MyProjectEntityFrameworkCoreModule.cs`and add `typeof(DocsEntityFrameworkCoreModule)` as shown below; |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(DocsEntityFrameworkCoreModule), |
|||
typeof(MyProjectDomainModule), |
|||
typeof(AbpIdentityEntityFrameworkCoreModule), |
|||
typeof(AbpPermissionManagementEntityFrameworkCoreModule), |
|||
typeof(AbpSettingManagementEntityFrameworkCoreModule), |
|||
typeof(AbpEntityFrameworkCoreSqlServerModule), |
|||
typeof(BackgroundJobsEntityFrameworkCoreModule), |
|||
typeof(AbpAuditLoggingEntityFrameworkCoreModule) |
|||
)] |
|||
public class MyProjectEntityFrameworkCoreModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
|
|||
* Open `MyProjectApplicationModule.cs`and add `typeof(DocsApplicationModule)` as shown below; |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(DocsApplicationModule), |
|||
typeof(MyProjectDomainModule), |
|||
typeof(AbpIdentityApplicationModule))] |
|||
public class MyProjectApplicationModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<PermissionOptions>(options => |
|||
{ |
|||
options.DefinitionProviders.Add<MyProjectPermissionDefinitionProvider>(); |
|||
}); |
|||
|
|||
Configure<AbpAutoMapperOptions>(options => |
|||
{ |
|||
options.AddProfile<MyProjectApplicationAutoMapperProfile>(); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
|
|||
* Open `MyProjectWebModule.cs`and add `typeof(DocsWebModule)` as shown below; |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(DocsWebModule), |
|||
typeof(MyProjectApplicationModule), |
|||
typeof(MyProjectEntityFrameworkCoreModule), |
|||
typeof(AbpAutofacModule), |
|||
typeof(AbpIdentityWebModule), |
|||
typeof(AbpAccountWebModule), |
|||
typeof(AbpAspNetCoreMvcUiBasicThemeModule) |
|||
)] |
|||
public class MyProjectWebModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
|
|||
|
|||
### 4- Database Integration |
|||
|
|||
#### 4.1- Entity Framework Integration |
|||
|
|||
If you choose Entity Framework as your database provider, you need to configure the Docs Module in your DbContext. To do this; |
|||
|
|||
- Open `MyProjectDbContext.cs` and add `modelBuilder.ConfigureDocs()` to the `OnModelCreating()` |
|||
|
|||
```csharp |
|||
[ConnectionStringName("Default")] |
|||
public class MyProjectDbContext : AbpDbContext<MyProjectDbContext> |
|||
{ |
|||
public MyProjectDbContext(DbContextOptions<MyProjectDbContext> options) |
|||
: base(options) |
|||
{ |
|||
|
|||
} |
|||
|
|||
protected override void OnModelCreating(ModelBuilder modelBuilder) |
|||
{ |
|||
//... |
|||
modelBuilder.ConfigureDocs(); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
* Open `Package Manager Console` in `Visual Studio` and choose `Acme.MyProject.EntityFrameworkCore` as default project. Then write the below command to add the migration for Docs Module. |
|||
|
|||
```csharp |
|||
add-migration Added_Docs_Module |
|||
``` |
|||
|
|||
When the command successfully executes , you will see a new migration file named as `20181221111621_Added_Docs_Module` in the folder `Acme.MyProject.EntityFrameworkCore\Migrations`. |
|||
|
|||
Now, update the database for Docs module database changes. To do this run the below code on `Package Manager Console` in `Visual Studio`. Be sure `Acme.MyProject.EntityFrameworkCore` is still default project. |
|||
|
|||
```csharp |
|||
update-database |
|||
``` |
|||
|
|||
Finally, you can check your database to see the newly created tables. For example you can see `DocsProjects` table must be added to your database. |
|||
|
|||
|
|||
### 5- Linking Docs Module |
|||
|
|||
The default route for Docs module is; |
|||
|
|||
``` |
|||
/Documents |
|||
``` |
|||
|
|||
To add Docs module link to your application menu; |
|||
|
|||
* Open `MyProjectMenuContributor.cs` and add the below line to the method `ConfigureMainMenuAsync()`. |
|||
|
|||
```csharp |
|||
context.Menu.Items.Add(new ApplicationMenuItem("MyProject.Docs", l["Menu:Docs"], "/Documents")); |
|||
``` |
|||
|
|||
Final look of **MyProjectMenuContributor.cs** |
|||
|
|||
```csharp |
|||
private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) |
|||
{ |
|||
var l = context.ServiceProvider.GetRequiredService<IStringLocalizer<MyProjectResource>>(); |
|||
|
|||
context.Menu.Items.Insert(0, new ApplicationMenuItem("MyProject.Home", l["Menu:Home"], "/")); |
|||
|
|||
context.Menu.Items.Add(new ApplicationMenuItem("MyProject.Docs", l["Menu:Docs"], "/Documents")); |
|||
} |
|||
``` |
|||
|
|||
The `Menu:Docs` keyword is a localization key. To localize the menu text, open `Localization\MyProject\en.json` in the project `Acme.MyProject.Domain`. And add the below line |
|||
|
|||
```json |
|||
"Menu:Docs": "Documents" |
|||
``` |
|||
|
|||
Final look of **en.json** |
|||
|
|||
```json |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"Menu:Home": "Home", |
|||
"Welcome": "Welcome", |
|||
"LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io.", |
|||
"Menu:Docs": "Documents" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
The new menu item for Docs Module is added to the menu. Run your web application and browse to `http://localhost:YOUR_PORT_NUMBER/documents` URL. |
|||
|
|||
You will see a warning says; |
|||
|
|||
``` |
|||
There are no projects yet! |
|||
``` |
|||
|
|||
As we have not added any projects yet, this warning is normal. |
|||
|
|||
### 6- Adding New Docs Project |
|||
|
|||
Open `DocsProjects` in your database, and insert a new record with the following field information; |
|||
|
|||
* **Name**: The display name of the document name which will be shown on the web page. |
|||
* **ShortName**: A short and URL friendly name that will be used in your docs URL. |
|||
* **Format**: The format of the document (for Markdown: `md`, for HTML: `html`) |
|||
* **DefaultDocumentName**: The document for the initial page. |
|||
* **NavigationDocumentName**: The document to be used for the navigation menu (index). |
|||
* **MinimumVersion**: The minimum version to show the docs. Below version will not be listed. |
|||
* **DocumentStoreType**: The source of the documents (for GitHub:`GitHub`, for file system`FileSystem`) |
|||
* **ExtraProperties**: A serialized `JSON` that stores special configuration for the selected `DocumentStoreType`. |
|||
* **MainWebsiteUrl**: The URL when user clicks to the logo of the Docs module page. You can simply set as `/` to link to your website root address. |
|||
* **LatestVersionBranchName**: This is a config for GitHub. It's the branch name which to retrieve the docs. You can set it as `master`. |
|||
|
|||
#### Sample Project Record for "GitHub" |
|||
|
|||
You can use [ABP Framework](https://github.com/abpframework/abp/) GitHub documents to configure your GitHub document store. |
|||
|
|||
- Name: `ABP framework (GitHub)` |
|||
|
|||
- ShortName: `abp` |
|||
|
|||
- Format: `md` |
|||
|
|||
- DefaultDocumentName: `Index` |
|||
|
|||
- NavigationDocumentName: `docs-nav.json` |
|||
|
|||
- MinimumVersion: `<NULL>` (no minimum version) |
|||
|
|||
- DocumentStoreType: `GitHub` |
|||
|
|||
- ExtraProperties: |
|||
|
|||
```json |
|||
{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs/en/","GitHubAccessToken":"***"} |
|||
``` |
|||
|
|||
Note that `GitHubAccessToken` is masked with `***`. It's a private token that you must get it from GitHub. See https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/ |
|||
|
|||
- MainWebsiteUrl: `/` |
|||
|
|||
- LatestVersionBranchName: `master` |
|||
|
|||
For `SQL` databases, you can use the below `T-SQL` command to insert the specified sample into your `DocsProjects` table: |
|||
|
|||
```mssql |
|||
INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939658', N'ABP framework (GitHub)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'GitHub', N'{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs/en/","GitHubAccessToken":"***"}', N'/', N'master') |
|||
``` |
|||
|
|||
Be aware that `GitHubAccessToken` is masked. It's a private token and you must get your own token and replace the `***` string. |
|||
|
|||
#### Sample Project Record for "FileSystem" |
|||
|
|||
You can use [ABP Framework](https://github.com/abpframework/abp/) GitHub documents to configure your GitHub document store. |
|||
|
|||
- Name: `ABP framework (FileSystem)` |
|||
|
|||
- ShortName: `abp` |
|||
|
|||
- Format: `md` |
|||
|
|||
- DefaultDocumentName: `Index` |
|||
|
|||
- NavigationDocumentName: `docs-nav.json` |
|||
|
|||
- MinimumVersion: `<NULL>` (no minimum version) |
|||
|
|||
- DocumentStoreType: `FileSystem` |
|||
|
|||
- ExtraProperties: |
|||
|
|||
```json |
|||
{"Path":"C:\\Github\\abp\\docs\\en"} |
|||
``` |
|||
|
|||
Note that `Path` must be replaced with your local docs directory. You can fetch the ABP Framework's documents from https://github.com/abpframework/abp/tree/master/docs/en and copy to the directory `C:\\Github\\abp\\docs\\en` to get it work. |
|||
|
|||
- MainWebsiteUrl: `/` |
|||
|
|||
- LatestVersionBranchName: `<NULL>` |
|||
|
|||
For `SQL` databases, you can use the below `T-SQL` command to insert the specified sample into your `DocsProjects` table: |
|||
|
|||
```mssql |
|||
INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939659', N'ABP framework (FileSystem)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'FileSystem', N'{"Path":"C:\\Github\\abp\\docs\\en"}', N'/', NULL) |
|||
``` |
|||
|
|||
|
|||
|
|||
Add one of the sample projects above and run the application. In the menu you will see `Documents` link, click the menu link to open the documents page. |
|||
|
|||
So far, we have created a new application from abp.io website and made it up and ready for Docs module. |
|||
|
|||
### 7- Creating a New Document |
|||
|
|||
In the sample Project records, you see that `Format` is specified as `md` which refers to [Mark Down](https://en.wikipedia.org/wiki/Markdown). You can see the mark down cheat sheet following the below link; |
|||
|
|||
https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet |
|||
|
|||
ABP Docs Module can render mark down to HTML. |
|||
|
|||
Now let's have a look a sample document in markdown format. |
|||
|
|||
~~~markdown |
|||
# This is a header |
|||
|
|||
Welcome to Docs Module. |
|||
|
|||
## This is a sub header |
|||
|
|||
[This is a link](https://abp.io) |
|||
|
|||
 |
|||
|
|||
## This is a code block |
|||
|
|||
```csharp |
|||
public class Person |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public string Address { get; set; } |
|||
} |
|||
``` |
|||
~~~ |
|||
|
|||
|
|||
|
|||
As an example you can see ABP Framework documentation: |
|||
|
|||
[https://github.com/abpframework/abp/blob/master/docs/en/](https://github.com/abpframework/abp/blob/master/docs/en/) |
|||
|
|||
### 8- Creating the Navigation Document |
|||
|
|||
Navigation document is the main menu of the documents page. It is located on the left side of the page. It is a `JSON` file. Take a look at the below sample navigation document to understand the structure. |
|||
|
|||
```json |
|||
{ |
|||
"items":[ |
|||
{ |
|||
"text":"Sample Menu Item - 1", |
|||
"items":[ |
|||
{ |
|||
"text":"Sample Menu Item - 1.1", |
|||
"items":[ |
|||
{ |
|||
"text":"Sample Menu Item - 1.1.1", |
|||
"path":"SampleMenuItem_1_1_1.md" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text":"Sample Menu Item - 1.2", |
|||
"items":[ |
|||
{ |
|||
"text":"Sample Menu Item - 1.2.1", |
|||
"path":"SampleMenuItem_1_2_1.md" |
|||
}, |
|||
{ |
|||
"text":"Sample Menu Item - 1.2.2", |
|||
"path":"SampleMenuItem_1_2_2.md" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text":"Sample Menu Item - 2", |
|||
"items":[ |
|||
{ |
|||
"text":"Sample Menu Item - 2.1", |
|||
"items":[ |
|||
{ |
|||
"text":"Sample Menu Item - 2.1.1", |
|||
"path":"SampleMenuItem_2_1_1.md" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
The upper sample `JSON` file renders the below navigation menu as `HTML`. |
|||
|
|||
 |
|||
|
|||
|
|||
|
|||
Finally a new Docs Module is added to your project which is feeded with GitHub. |
|||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 154 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 10 KiB |
@ -0,0 +1,138 @@ |
|||
# 自动API控制器 |
|||
|
|||
创建[应用程序服务](Application-Services.md)后, 通常需要创建API控制器以将此服务公开为HTTP(REST)API端点. 典型的API控制器除了将方法调用重定向到应用程序服务并使用[HttpGet],[HttpPost],[Route]等属性配置REST API之外什么都不做. |
|||
|
|||
ABP可以按照惯例 **自动** 将你的应用程序服务配置为MVC API控制器. 大多数时候你不关心它的详细配置,但它可以完全被自定义. |
|||
|
|||
## 配置 |
|||
|
|||
基本配置很简单. 只需配置`AbpAspNetCoreMvcOptions`并使用`ConventionalControllers.Create`方法,如下所示: |
|||
|
|||
````csharp |
|||
[DependsOn(BookStoreApplicationModule)] |
|||
public class BookStoreWebModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options.ConventionalControllers.Create(typeof(BookStoreApplicationModule).Assembly); |
|||
}); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
此示例代码配置包含类`BookStoreApplicationModule`的程序集中的所有应用程序服务.下图显示了[Swagger UI](https://swagger.io/tools/swagger-ui/)上的API内容. |
|||
|
|||
 |
|||
|
|||
### 例子 |
|||
|
|||
一些示例方法名称和按约定生成的相应路由: |
|||
|
|||
| 服务方法名称 | HTTP Method | 路由 | |
|||
| ----------------------------------------------------- | ----------- | -------------------------- | |
|||
| GetAsync(Guid id) | GET | /api/app/book/{id} | |
|||
| GetListAsync() | GET | /api/app/book | |
|||
| CreateAsync(CreateBookDto input) | POST | /api/app/book | |
|||
| UpdateAsync(Guid id, UpdateBookDto input) | PUT | /api/app/book/{id} | |
|||
| DeleteAsync(Guid id) | DELETE | /api/app/book/{id} | |
|||
| GetEditorsAsync(Guid id) | GET | /api/app/book/{id}/editors | |
|||
| CreateEditorAsync(Guid id, BookEditorCreateDto input) | POST | /api/app/book/{id}/editor | |
|||
|
|||
### HTTP Method |
|||
|
|||
ABP在确定服务方法的HTTP Method时使用命名约定: |
|||
|
|||
- **Get**: 如果方法名称以`GetList`,`GetAll`或`Get`开头. |
|||
- **Put**: 如果方法名称以`Put`或`Update`开头. |
|||
- **Delete**: 如果方法名称以`Delete`或`Remove`开头. |
|||
- **Post**: 如果方法名称以`Create`,`Add`,`Insert`或`Post`开头. |
|||
- **Patch**: 如果方法名称以`Patch`开头. |
|||
- 其他情况, **Post** 为 **默认方式**. |
|||
|
|||
如果需要为特定方法自定义HTTP Method, 则可以使用标准ASP.NET Core的属性([HttpPost], [HttpGet], [HttpPut]... 等等.). 这需要添加[Microsoft.AspNetCore.Mvc.Core](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.Core)的Nuget包. |
|||
|
|||
### 路由 |
|||
|
|||
路由根据一些惯例生成: |
|||
|
|||
* 它始终以 **/api**开头. |
|||
* 接着是**路由路径**. 默认值为"**/app**", 可以进行如下配置: |
|||
|
|||
````csharp |
|||
Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options.ConventionalControllers |
|||
.Create(typeof(BookStoreApplicationModule).Assembly, opts => |
|||
{ |
|||
opts.RootPath = "volosoft/book-store"; |
|||
}); |
|||
}); |
|||
```` |
|||
|
|||
然后获得一本书的路由将是'**/api/volosoft/book-store/book/{id}**'. 此示例使用两级根路径,但通常使用单个级别的深度. |
|||
|
|||
* 接着 **标准化控制器/服务名称**. 会删除`AppService`,`ApplicationService`和`Service`的后缀并将其转换为 **camelCase**. 如果你的应用程序服务类名称为`BookAppService`.那么它将变为`/book`. |
|||
* 如果要自定义命名, 则设置`UrlControllerNameNormalizer`选项. 它是一个委托允许你自定义每个控制器/服务的名称. |
|||
* 如果该方法具有 '**id**'参数, 则会在路由中添加'**/{id}**'. |
|||
* 如有必要,它会添加操作名称. 操作名称从服务上的方法名称获取并标准化; |
|||
* 删除'**Async**'后缀. 如果方法名称为'GetPhonesAsync',则变为`GetPhones`. |
|||
* 删除**HTTP method前缀**. 基于的HTTP method删除`GetList`,`GetAll`,`Get`,`Put`,`Update`,`Delete`,`Remove`,`Create`,`Add`,`Insert`,`Post`和`Patch`前缀, 因此`GetPhones`变为`Phones`, 因为`Get`前缀和GET请求重复. |
|||
* 将结果转换为**camelCase**. |
|||
* 如果生成的操作名称为**空**,则它不会添加到路径中.否则它会被添加到路由中(例如'/phones').对于`GetAllAsync`方法名称,它将为空,因为`GetPhonesAsync`方法名称将为`phone`. |
|||
* 可以通过设置`UrlActionNameNormalizer`选项来自定义.It's an action delegate that is called for every method. |
|||
* 如果有另一个带有'Id'后缀的参数,那么它也会作为最终路线段添加到路线中(例如'/phoneId'). |
|||
|
|||
## 服务选择 |
|||
|
|||
创建的HTTP API控制器并不是应用服务所独有的功能. |
|||
|
|||
### IRemoteService 接口 |
|||
|
|||
如果一个类实现了`IRemoteService`接口, 那么它会被自动选择为API控制器. 由于应用程序服务本身实现了`IRemoteService`接口, 因此它自然就成为API控制器. |
|||
|
|||
### RemoteService Attribute |
|||
|
|||
`RemoteService`可用于将实现`IRemoteService`接口的类标记为远程服务或禁用它. 例如: |
|||
|
|||
````csharp |
|||
[RemoteService(IsEnabled = false)] //or simply [RemoteService(false)] |
|||
public class PersonAppService : ApplicationService |
|||
{ |
|||
|
|||
} |
|||
```` |
|||
|
|||
### TypePredicate 选项 |
|||
|
|||
你可以通过提供`TypePedicate`选项进一步过滤类以成为API控制器: |
|||
|
|||
````csharp |
|||
services.Configure<AbpAspNetCoreMvcOptions>(options => |
|||
{ |
|||
options.ConventionalControllers |
|||
.Create(typeof(BookStoreApplicationModule).Assembly, opts => |
|||
{ |
|||
opts.TypePredicate = type => { return true; }; |
|||
}); |
|||
}); |
|||
```` |
|||
|
|||
如果你不想将此类型公开为API控制器, 则可以在类型检查时返回`false`. |
|||
|
|||
## API Explorer |
|||
|
|||
API Explorer是可以由客户端获取API结构的服务. Swagger使用它为endpoint创建文档和test UI. |
|||
|
|||
默认情况下, HTTP API控制器会自动启用API Explorer, 可以使用`RemoteService`按类或方法的级别控制它. 例如: |
|||
|
|||
````csharp |
|||
[RemoteService(IsMetadataEnabled = false)] |
|||
public class PersonAppService : ApplicationService |
|||
{ |
|||
|
|||
} |
|||
```` |
|||
|
|||
禁用`IsMetadataEnabled`从而从API Explorer中隐藏此服务, 并且无法被发现. 但是它仍然可以被知道确切API路径/路由的客户端使用. |
|||
@ -0,0 +1,165 @@ |
|||
# 动态 C# API 客户端 |
|||
|
|||
ABP可以自动创建C# API 客户端代理来调用远程HTTP服务(REST APIS).通过这种方式,你不需要通过 `HttpClient` 或者其他低级的HTTP功能调用远程服务并获取数据. |
|||
|
|||
## 服务接口 |
|||
|
|||
你的service或controller需要实现一个在服务端和客户端共享的接口.因此,首先需要在一个共享的类库项目中定义一个服务接口.例如: |
|||
|
|||
````csharp |
|||
public interface IBookAppService : IApplicationService |
|||
{ |
|||
Task<List<BookDto>> GetListAsync(); |
|||
} |
|||
```` |
|||
|
|||
为了能自动被发现,你的接口需要实现`IRemoteService`接口.由于`IApplicationService`继承自`IRemoteService`接口.所以`IBookAppService`完全满足这个条件. |
|||
|
|||
在你的服务中实现这个类,你可以使用[auto API controller system](Auto-API-Controllers.md)将你的服务暴漏为一个REST API 端点. |
|||
|
|||
## 客户端代理生成 |
|||
|
|||
首先,将[Volo.Abp.Http.Client](https://www.nuget.org/packages/Volo.Abp.Http.Client) nuget包添加到你的客户端项目中: |
|||
|
|||
```` |
|||
Install-Package Volo.Abp.Http.Client |
|||
```` |
|||
|
|||
然后给你的模块添加`AbpHttpClientModule`依赖: |
|||
|
|||
````csharp |
|||
[DependsOn(typeof(AbpHttpClientModule))] //添加依赖 |
|||
public class MyClientAppModule : AbpModule |
|||
{ |
|||
} |
|||
```` |
|||
|
|||
现在,已经可以创建客户端代理了.例如: |
|||
|
|||
````csharp |
|||
[DependsOn( |
|||
typeof(AbpHttpClientModule), //用来创建客户端代理 |
|||
typeof(BookStoreApplicationModule) //包含应用服务接口 |
|||
)] |
|||
public class MyClientAppModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
//创建动态客户端代理 |
|||
context.Services.AddHttpClientProxies( |
|||
typeof(BookStoreApplicationModule).Assembly |
|||
); |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`AddHttpClientproxies`方法获得一个程序集,找到这个程序集中所有的服务接口,创建并注册代理类. |
|||
|
|||
### Endpoint配置 |
|||
|
|||
`appsettings.json`文件中的`RemoteServices`节点被用来设置默认的服务地址.下面是最简单的配置: |
|||
|
|||
```` |
|||
{ |
|||
"RemoteServices": { |
|||
"Default": { |
|||
"BaseUrl": "http://localhost:53929/" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
查看下面的"RemoteServiceOptions"章节获取更多详细配置. |
|||
|
|||
## 使用 |
|||
|
|||
可以很直接地使用.只需要在你的客户端程序中注入服务接口: |
|||
|
|||
````csharp |
|||
public class MyService : ITransientDependency |
|||
{ |
|||
private readonly IBookAppService _bookService; |
|||
|
|||
public MyService(IBookAppService bookService) |
|||
{ |
|||
_bookService = bookService; |
|||
} |
|||
|
|||
public async Task DoIt() |
|||
{ |
|||
var books = await _bookService.GetListAsync(); |
|||
foreach (var book in books) |
|||
{ |
|||
Console.WriteLine($"[BOOK {book.Id}] Name={book.Name}"); |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
本例注入了上面定义的`IBookAppService`服务接口.当客户端调用服务方法的时候动态客户端代理就会创建一个HTTP调用. |
|||
|
|||
### IHttpClientProxy接口 |
|||
|
|||
你可以像上面那样注入`IBookAppService`来使用客户端代理,也可以注入`IHttpClientProxy<IBookAppService>`获取更多明确的用法.这种情况下你可以使用`IHttpClientProxy<T>`接口的`Service`属性. |
|||
|
|||
## 配置 |
|||
|
|||
### RemoteServiceOptions |
|||
|
|||
默认情况下`RemoteServiceOptions`从`appsettings.json`获取.或者,你可以使用`Configure`方法来设置或重写它.如: |
|||
|
|||
````csharp |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.Configure<RemoteServiceOptions>(options => |
|||
{ |
|||
options.RemoteServices.Default = |
|||
new RemoteServiceConfiguration("http://localhost:53929/"); |
|||
}); |
|||
|
|||
//... |
|||
} |
|||
```` |
|||
|
|||
### 多个远程服务端点 |
|||
|
|||
上面的例子已经配置了"Default"远程服务端点.你可能需要为不同的服务创建不同的端点.(就像在微服务方法中一样,每个微服务具有不同的端点).在这种情况下,你可以在你的配置文件中添加其他的端点: |
|||
|
|||
````json |
|||
{ |
|||
"RemoteServices": { |
|||
"Default": { |
|||
"BaseUrl": "http://localhost:53929/" |
|||
}, |
|||
"BookStore": { |
|||
"BaseUrl": "http://localhost:48392/" |
|||
} |
|||
} |
|||
} |
|||
```` |
|||
|
|||
`AddHttpClientProxies`方法有一个可选的参数来定义远程服务的名字: |
|||
|
|||
````csharp |
|||
context.Services.AddHttpClientProxies( |
|||
typeof(BookStoreApplicationModule).Assembly, |
|||
remoteServiceName: "BookStore" |
|||
); |
|||
```` |
|||
|
|||
`remoteServiceName`参数会匹配通过`RemoteServiceOptions`配置的服务端点.如果`BookStore`端点没有定义就会使用默认的`Default`端点. |
|||
|
|||
### 作为默认服务 |
|||
|
|||
当你为`IBookAppService`创建了一个服务代理,你可以直接注入`IBookAppService`来使用代理客户端(像上面章节中将的那样).你可以传递`asDefaultService:false`到`AddHttpClientProxies`方法来禁用此功能. |
|||
|
|||
````csharp |
|||
context.Services.AddHttpClientProxies( |
|||
typeof(BookStoreApplicationModule).Assembly, |
|||
asDefaultServices: false |
|||
); |
|||
```` |
|||
|
|||
如果你的程序中已经有一个服务的实现并且你不想用你的客户端代理重写或替换其他的实现,就需要使用`asDefaultServices:false` |
|||
|
|||
> 如果你禁用了`asDefaultService`,你只能使用`IHttpClientProxy<T>`接口去使用客户端代理.(参见上面的相关章节). |
|||
@ -0,0 +1,54 @@ |
|||
# 微服务演示,项目状态和路线图 |
|||
|
|||
在ABP vNext上的[第一个公告](https://cn.abp.io/blog/abp/Abp-vNext-Announcement)之后,我们对代码库进行了很多改进([GitHub存储库](https://github.com/abpframework/abp)上的1100多次提交).我们已经创建了功能,示例,文档等等.在这篇文章中,我想告诉你一些新闻和项目的状态. |
|||
|
|||
## 微服务演示解决方案 |
|||
|
|||
ABP框架的主要目标之一是提供[创建微服务解决方案的便利基础设施](https://cn.abp.io/documents/abp/latest/Microservice-Architecture). |
|||
|
|||
我们一直在努力开发微服务解决方案演示.初始版本已完成并[文档化](https://cn.abp.io/documents/abp/latest/Samples/Microservice-Demo).该示例解决方案旨在演示一个简单而完整的微服务解决方案; |
|||
|
|||
- 具有多个独立的,可自我部署的**微服务**. |
|||
- 多个**Web应用程序**,每个都使用不同的API网关. |
|||
- 使用[Ocelot](https://github.com/ThreeMammals/Ocelot)库开发了多个**网关** / BFF(后端为前端(Backend for Frontends)). |
|||
- 使用[IdentityServer](https://identityserver.io/)框架开发**身份验证服务**.它也是一个带有必要UI的SSO(单点登录)应用程序. |
|||
- 有**多个数据库**.一些微服务有自己的数据库,而一些服务/应用程序共享一个数据库(以演示不同的用例). |
|||
- 具有不同类型的数据库:**SQL Server**(使用**Entity Framework Core** ORM)和**MongoDB**. |
|||
- 有一个**控制台应用程序**来显示通过身份验证使用服务的最简单方法. |
|||
- 使用[Redis](https://redis.io/)进行**分布式缓存**. |
|||
- 使用[RabbitMQ](https://www.rabbitmq.com/)进行服务到服务(service-to-service)的**消息传递**. |
|||
- 使用[Docker](https://www.docker.com/)和[Kubernates](https://kubernetes.io/)**部署**并运行所有服务和应用程序. |
|||
- 使用[Elasticsearch](https://www.elastic.co/products/elasticsearch)和[Kibana](https://www.elastic.co/products/kibana)存储和可视化日志(使用[Serilog](https://serilog.net/)编写). |
|||
|
|||
有关解决方案的详细说明,请参阅[其文档](https://cn.abp.io/documents/abp/latest/Samples/Microservice-Demo). |
|||
|
|||
## 改进/功能 |
|||
|
|||
我们已经开发了许多功能,包括**分布式事件总线**(与RabbitMQ集成),**IdentityServer4集成**以及几乎所有功能的增强.我们不断重构和添加测试,以使框架更稳定和生产就绪.它正在[快速增长](https://github.com/abpframework/abp/graphs/contributors). |
|||
|
|||
## 路线图 |
|||
|
|||
在第一个稳定版本(v1.0)之前还有很多工作要做.您可以在GitHub仓库上看到[优先的积压项目](https://github.com/abpframework/abp/issues?q=is%3Aopen+is%3Aissue+milestone%3ABacklog). |
|||
|
|||
根据我们的估计,我们计划在2019年第二季度(可能在五月或六月)发布v1.0.所以,不用等待太长时间了.我们也对第一个稳定版本感到非常兴奋. |
|||
|
|||
我们还将完善[文档](https://cn.abp.io/documents/abp/latest),因为它现在还远未完成. |
|||
|
|||
第一个版本可能不包含SPA模板.但是,如果可能的话,我们想要准备一个简单些的.SPA框架还没有确定下来.备选有:**Angular,React和Blazor**.请将您的想法写为对此帖的评论. |
|||
|
|||
## 中文网 |
|||
|
|||
中国有一个大型的ABP社区.他们创建了一个中文版的abp.io网站:https://cn.abp.io/. 他们一直在保持更新.感谢中国的开发人员,特别是[Liming Ma](https://github.com/maliming). |
|||
|
|||
## NDC {London} 2019 |
|||
|
|||
很高兴作为合作伙伴参加[NDC {London}](https://ndc-london.com/)2019 .我们已经与许多开发人员讨论过当前的ASP.NET Boilerplate和ABP vNext,我们得到了很好的反馈. |
|||
|
|||
我们还有机会与[Scott Hanselman](https://twitter.com/shanselman)和[Jon Galloway](https://twitter.com/jongalloway)交谈.他们参观了我们的展位,我们谈到了ABP vNext的想法.他们喜欢新的ABP框架的功能,方法和目标.在twitter上查看一些照片和评论: |
|||
|
|||
 |
|||
|
|||
## 跟上步伐 |
|||
|
|||
* 您可以标星并关注**GitHub**存储库:https://github.com/abpframework/abp |
|||
* 您可以关注官方**Twitter**帐户获取新闻:https://twitter.com/abpframework |
|||
|
After Width: | Height: | Size: 477 KiB |
@ -0,0 +1,3 @@ |
|||
# ABP Documentation |
|||
|
|||
待添加 |
|||
@ -0,0 +1,3 @@ |
|||
## Dynamic Proxying / Interceptors |
|||
|
|||
待添加 |
|||
@ -0,0 +1,3 @@ |
|||
# Event Bus |
|||
|
|||
TODO |
|||
@ -0,0 +1,3 @@ |
|||
## Guid 生成 |
|||
|
|||
待添加 |
|||
@ -0,0 +1,30 @@ |
|||
# 微服务架构 |
|||
|
|||
*"作为**面向服务架构**(SOA)的一个变体,微服务是一种将应用程序分解成**松散耦合服务**的新型架构风格. 通过**细粒度**的服务和**轻量级**的协议,微服务提供了更多的**模块化**,使应用程序更容易理解,开发,测试,并且更容易抵抗架构侵蚀. 它使小型团队能够**开发,部署和扩展**各自的服务,实现开发的**并行化**.它还允许通过**连续重构**形成单个服务的架构. 基于微服务架构可以实现**持续交付和部署**."* |
|||
|
|||
— [维基百科](https://zh.wikipedia.org/wiki/Microservices) |
|||
|
|||
## 介绍 |
|||
|
|||
ABP框架的主要目标之一就是提供**便捷的基础设施来创建微服务解决方案**. 我们做了以下工作: |
|||
|
|||
* 提供[模块系统](Module-Development-Basics.md),允许将应用程序拆分为模块,其中每个模块可以拥有自己的数据库,实体,服务,API,UI组件/页面....等. |
|||
* 提供[架构模型](Best-Practices/Module-Architecture.md)来开发模块,与微服务开发和部署兼容. |
|||
* 提供[最佳实践指南](Best-Practices/Index.md)制定模块开发标准. |
|||
* 提供基础设施来实现微服务中的[领域驱动设计](Domain-Driven-Design.md). |
|||
* 提供从应用程序服务[自动生成REST风格的API](AspNetCore/Auto-API-Controllers.md)的服务. |
|||
* 提供[自动创建C#API客户端](AspNetCore/Dynamic-CSharp-API-Clients.md)服务,以便从其他服务/应用程序使用你服务. |
|||
* 提供[分布式事件总线](Event-Bus.md)用于服务通信. |
|||
* 提供更多其他服务,使日常开发更加简便. |
|||
|
|||
## 在新应用程序中使用微服务 |
|||
|
|||
开始一个新解决方案建议**始终从单体开始**, 保持模块化,在单体成为问题时将其拆分为微服务.这使初期进度会很快,特别是如果你的团队人数不多,并且不想处理微服务架构带来的各种挑战. |
|||
|
|||
然而开发一个良好的模块化应用程序不是那么简单,因为很难像微服务那样**保持模块之间的隔离** (参阅 [Stefan Tilkov的文章](https://martinfowler.com/articles/dont-start-monolith.html)). 微服务架构会自然的让你开发隔离的服务,但是在模块化的单体应用程序中,模块很容易彼此紧密耦合并设计出**弱模块边界**和API约定. |
|||
|
|||
ABP可以帮助你,它提供了与**与微服务兼容的严格模块架构** 在这个架构中你的模块被分割成多个层/项目,在自己的VS解决方案中进行开发,该解决方案完成独立于其它模块. 这种方式开发的模块是一种天然的微服务,但是它可以很容易的插入到单体应用程序中. 请参阅**微服务优先的模块设计**的[模块开发最佳实践指南](Best-Practices/Index.md). 所有[标准的ABP模块](https://github.com/abpframework/abp/tree/master/modules)都是基于本指南开发的. 因此你可以将这些模块嵌入到单体解决方案中使用它们,也可以单独部署通过远程API调用. 它们可以共享一个数据库,也可以通过简单配置使用自己的数据库. |
|||
|
|||
## 微服务解决方案示例 |
|||
|
|||
[微服务解决方案示例](Samples/Microservice-Demo.md)演示了基于ABP框架的完整的微服务的解决方案. |
|||
@ -0,0 +1,467 @@ |
|||
# 文档模块 |
|||
|
|||
## 什么是文档模块? |
|||
|
|||
文档模块是ABP框架的一个应用程序模块. 它简化了软件文档的制作. 这个模块是开源免费的. |
|||
|
|||
### 集成 |
|||
|
|||
目前文档模块提供提供了两种支持的存储,Github与文件系统. |
|||
|
|||
### 托管 |
|||
|
|||
文档模块是一个应用程序模块,不提供任何托管的解决方案,你可以在本地或云上托管文档. |
|||
|
|||
### 版本 |
|||
|
|||
当你使用GitHub存储文档时,文档模块支持多版本. 如果你的文档具有多个版本, UI上有一个组合框,用于切换版本. 如果你选择使用文件系统存储文档, 那么它不支持多版本. |
|||
|
|||
ABP框架的[文档](https://abp.io/documents/)也是使用的此模块. |
|||
|
|||
> 文档模块遵循 [模块化架构最佳实践](../Best-Practices/Module-Architecture.md) 指南. |
|||
|
|||
|
|||
|
|||
## 安装 |
|||
|
|||
### 1- 下载 |
|||
|
|||
如果你没有现有的ABP项目, 这个步骤向你展示如何在[abp.io](https://cn.abp.io)创建一个新项目并添加文档模块. 如果你本地已经有了一个ABP项目, 那么你可以跳过这一步. |
|||
|
|||
打开 https://cn.abp.io/Templates. 输入项目名称为 `Acme.MyProject`, 选择 `ASP.NET Core Mvc Application` 和选择 `Entity Framework Core` 做为数据库提供者. |
|||
|
|||
请注意,本文档包含了 `Entity Framework Core` 提供者 不过你也可以选择 `MongoDB` 做为数据库提供者. |
|||
|
|||
 |
|||
|
|||
### 2- 运行这个空项目 |
|||
|
|||
下载项目后, 解压压缩文档并且打开 `Acme.MyProject.sln`. 你可以看到这个解决方案包含了 `Application`, `Domain `, `EntityFrameworkCore` 和 `Web` 项目. 右键选择 `Acme.MyProject.Web` 项目**设置为启动项目**. |
|||
|
|||
 |
|||
|
|||
数据库连接字符串位于`Acme.MyProject.Web`项目的`appsettings.json`中. 如果你有不同的数据库配置, 可以修改这个连接字符串. |
|||
|
|||
```json |
|||
{ |
|||
"ConnectionStrings": { |
|||
"Default": "Server=localhost;Database=MyProject;Trusted_Connection=True;MultipleActiveResultSets=true" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
打开Visual Studio包管理控制台选择`src\Acme.MyProject.EntityFrameworkCore` 做为默认项目. 运行 `Update-Database` 命令创建数据库. 数据库`MyProject`将在数据库服务器中创建. |
|||
|
|||
现在一个空的ABP项目已经创建完成! 现在你可以运行项目并且查看网站. |
|||
|
|||
输入用户名 `admin` 密码 `1q2w3E*` 登录到网站. |
|||
|
|||
### 2- 引用文档模块包 |
|||
|
|||
文档模块包托管在Nuget上面. 需要有四个包安装到你的应用程序中. 每个包必须安装到相关的项目. |
|||
|
|||
* [Volo.Docs.Domain](https://www.nuget.org/packages/Volo.Docs.Domain/) 需要安装到 `Acme.MyProject.Domain` 项目. |
|||
|
|||
* 修改 `Acme.MyProject.Domain.csproj` 文件并且添加以下行. 需要注意它要设置(v0.9.0)为Latest版本. |
|||
|
|||
```csharp |
|||
<PackageReference Include="Volo.Docs.Domain" Version="0.9.0" /> |
|||
``` |
|||
* [Volo.Docs.EntityFrameworkCore](https://www.nuget.org/packages/Volo.Docs.EntityFrameworkCore/) 需要安装到 `Acme.MyProject.EntityFrameworkCore` 项目. |
|||
|
|||
- 修改 `Acme.MyProject.EntityFrameworkCore.csproj`文件并且添加以下行. 需要注意它要设置(v0.9.0)为Latest版本. |
|||
|
|||
```csharp |
|||
<PackageReference Include="Volo.Docs.EntityFrameworkCore" Version="0.9.0" /> |
|||
``` |
|||
* [Volo.Docs.Application](https://www.nuget.org/packages/Volo.Docs.Application/) 需要安装到 `Acme.MyProject.Application` 项目. |
|||
|
|||
* 修改 `Acme.MyProject.Application.csproj`文件并且添加以下行. 需要注意它要设置(v0.9.0)为Latest版本. |
|||
|
|||
```csharp |
|||
<PackageReference Include="Volo.Docs.Application" Version="0.9.0" /> |
|||
``` |
|||
* [Volo.Docs.Web ](https://www.nuget.org/packages/Volo.Docs.Web/) 需要安装到 `Acme.MyProject.Web` 项目. |
|||
|
|||
- 修改 `Acme.MyProject.Web.csproj`文件并且添加以下行. 需要注意它要设置(v0.9.0)为Latest版本. |
|||
|
|||
```csharp |
|||
<PackageReference Include="Volo.Docs.Web" Version="0.9.0" /> |
|||
``` |
|||
|
|||
|
|||
|
|||
### 3- 添加模块添加 |
|||
|
|||
一个ABP模块必须声明 `[DependsOn]` attribute 如果它依赖于另一个模块. 每个模块都必须在相关的项目的`[DependsOn]`Attribute 中添加. |
|||
|
|||
* 打开 `MyProjectDomainModule.cs`并且添加 `typeof(DocsDomainModule)` 如下所示; |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(DocsDomainModule), |
|||
typeof(AbpIdentityDomainModule), |
|||
typeof(AbpAuditingModule), |
|||
typeof(BackgroundJobsDomainModule), |
|||
typeof(AbpAuditLoggingDomainModule) |
|||
)] |
|||
public class MyProjectDomainModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
* 打开 `MyProjectEntityFrameworkCoreModule.cs`并且添加 `typeof(DocsEntityFrameworkCoreModule)` 如下所示; |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(DocsEntityFrameworkCoreModule), |
|||
typeof(MyProjectDomainModule), |
|||
typeof(AbpIdentityEntityFrameworkCoreModule), |
|||
typeof(AbpPermissionManagementEntityFrameworkCoreModule), |
|||
typeof(AbpSettingManagementEntityFrameworkCoreModule), |
|||
typeof(AbpEntityFrameworkCoreSqlServerModule), |
|||
typeof(BackgroundJobsEntityFrameworkCoreModule), |
|||
typeof(AbpAuditLoggingEntityFrameworkCoreModule) |
|||
)] |
|||
public class MyProjectEntityFrameworkCoreModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
|
|||
* 打开 `MyProjectApplicationModule.cs`并且添加 `typeof(DocsApplicationModule)` 如下所示; |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(DocsApplicationModule), |
|||
typeof(MyProjectDomainModule), |
|||
typeof(AbpIdentityApplicationModule))] |
|||
public class MyProjectApplicationModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<PermissionOptions>(options => |
|||
{ |
|||
options.DefinitionProviders.Add<MyProjectPermissionDefinitionProvider>(); |
|||
}); |
|||
|
|||
Configure<AbpAutoMapperOptions>(options => |
|||
{ |
|||
options.AddProfile<MyProjectApplicationAutoMapperProfile>(); |
|||
}); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
|
|||
* 打开 `MyProjectWebModule.cs`并且添加 `typeof(DocsWebModule)` 如下所示; |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(DocsWebModule), |
|||
typeof(MyProjectApplicationModule), |
|||
typeof(MyProjectEntityFrameworkCoreModule), |
|||
typeof(AbpAutofacModule), |
|||
typeof(AbpIdentityWebModule), |
|||
typeof(AbpAccountWebModule), |
|||
typeof(AbpAspNetCoreMvcUiBasicThemeModule) |
|||
)] |
|||
public class MyProjectWebModule : AbpModule |
|||
{ |
|||
//... |
|||
} |
|||
``` |
|||
|
|||
|
|||
|
|||
### 4- 数据库集成 |
|||
|
|||
#### 4.1- Entity Framework 集成 |
|||
|
|||
如果你选择了Entity Framework 做为数据库供应者,你需要在DbContext中配置文档模块. 做以下操作; |
|||
|
|||
- 打开 `MyProjectDbContext.cs` 并且添加 `modelBuilder.ConfigureDocs()` 到 `OnModelCreating()` 方法中 |
|||
|
|||
```csharp |
|||
[ConnectionStringName("Default")] |
|||
public class MyProjectDbContext : AbpDbContext<MyProjectDbContext> |
|||
{ |
|||
public MyProjectDbContext(DbContextOptions<MyProjectDbContext> options) |
|||
: base(options) |
|||
{ |
|||
|
|||
} |
|||
|
|||
protected override void OnModelCreating(ModelBuilder modelBuilder) |
|||
{ |
|||
//... |
|||
modelBuilder.ConfigureDocs(); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
* 打开 `Visual Studio` 的 `包管理控制台` 选择 `Acme.MyProject.EntityFrameworkCore` 做为默认项目. 然后编写以下命令为文档模块添加迁移. |
|||
|
|||
```csharp |
|||
add-migration Added_Docs_Module |
|||
``` |
|||
|
|||
当命令执行成功后 , 你会看到`Acme.MyProject.EntityFrameworkCore\Migrations` 目录下有名为 `20181221111621_Added_Docs_Module` 的迁移文件. |
|||
|
|||
现在更新数据库. 在 `Visual Studio` 的 `包管理控制台` 中执行以下代码. 要确认已 `Acme.MyProject.EntityFrameworkCore` 项目设置为默认项目. |
|||
|
|||
```csharp |
|||
update-database |
|||
``` |
|||
|
|||
最后你可以查看数据库中创建的新表,例如你可以看到 `DocsProjects` 表已经添加到数据库中. |
|||
|
|||
|
|||
### 5- 链接文档模块 |
|||
|
|||
文档模块的默认路由是; |
|||
|
|||
``` |
|||
/Documents |
|||
``` |
|||
|
|||
添加文档模块的链接到你的应用程序菜单中; |
|||
|
|||
* 打开 `MyProjectMenuContributor.cs` 并且在 `ConfigureMainMenuAsync()` 方法方法中添加以下代码. |
|||
|
|||
```csharp |
|||
context.Menu.Items.Add(new ApplicationMenuItem("MyProject.Docs", l["Menu:Docs"], "/Documents")); |
|||
``` |
|||
|
|||
最后 **MyProjectMenuContributor.cs** 有以下内容 |
|||
|
|||
```csharp |
|||
private async Task ConfigureMainMenuAsync(MenuConfigurationContext context) |
|||
{ |
|||
var l = context.ServiceProvider.GetRequiredService<IStringLocalizer<MyProjectResource>>(); |
|||
|
|||
context.Menu.Items.Insert(0, new ApplicationMenuItem("MyProject.Home", l["Menu:Home"], "/")); |
|||
|
|||
context.Menu.Items.Add(new ApplicationMenuItem("MyProject.Docs", l["Menu:Docs"], "/Documents")); |
|||
} |
|||
``` |
|||
|
|||
`Menu:Docs` 关键词是本地化的Key. 要本地化菜单文本, 打开`Acme.MyProject.Domain` 中的 `Localization\MyProject\zh-Hans.json`. 添加以下行. |
|||
|
|||
```json |
|||
"Menu:Docs": "文档" |
|||
``` |
|||
|
|||
最后 **zh-Hans.json** 有以下内容 |
|||
|
|||
```json |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Menu:Home": "首页", |
|||
"Welcome": "欢迎", |
|||
"LongWelcomeMessage": "欢迎来到该应用程序. 这是一个基于ABP框架的启动项目. 有关更多信息, 请访问 cn.abp.io.", |
|||
"Menu:Docs": "文档" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
现在菜单中已经添加了文档模块项. 运行Web应用程序并且在浏览器中打开 `http://localhost:YOUR_PORT_NUMBER/documents` URL. |
|||
|
|||
你会看到一个警告; |
|||
|
|||
``` |
|||
There are no projects yet! |
|||
``` |
|||
|
|||
这个警告是正常的,因为我们还没有添加任何项目. |
|||
|
|||
### 6- 添加文档项目 |
|||
|
|||
在数据库中打开 `DocsProjects`, 并且插入包含以下字段的新记录; |
|||
|
|||
* **Name**: 在Web页面上文档的显示名称. |
|||
* **ShortName**: 在文档URL中使用的友好的简短URL名称. |
|||
* **Format**: 文档的格式 ( Markdown: `md`, HTML: `html`) |
|||
* **DefaultDocumentName**: 文档的初始页面. |
|||
* **NavigationDocumentName**: 导航菜单(索引)的文档. |
|||
* **MinimumVersion**: 显示文档的最低版本. 低于此的版本不会列出. |
|||
* **DocumentStoreType**: 文档的来源 ( GitHub:`GitHub`,文件系统`FileSystem`). |
|||
* **ExtraProperties**: 序列化的`JSON`, 它存储所选 `DocumentStoreType` 的特殊配置. |
|||
* **MainWebsiteUrl**: 用户单击文档模块页面Logo时跳转的URL.你只需设置为`/`即可链接到网站根地址. |
|||
* **LatestVersionBranchName**: 这是GitHub的配置.它是检索文档的分支名称.你可以将其设置为`master`. |
|||
|
|||
#### "GitHub" 项目的示例记录 |
|||
|
|||
你可以使用 [ABP Framework](https://github.com/abpframework/abp/) GitHub文档来配置Github文档存储. |
|||
|
|||
- Name: `ABP framework (GitHub)` |
|||
|
|||
- ShortName: `abp` |
|||
|
|||
- Format: `md` |
|||
|
|||
- DefaultDocumentName: `Index` |
|||
|
|||
- NavigationDocumentName: `docs-nav.json` |
|||
|
|||
- MinimumVersion: `<NULL>` (no minimum version) |
|||
|
|||
- DocumentStoreType: `GitHub` |
|||
|
|||
- ExtraProperties: |
|||
|
|||
```json |
|||
{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs/zh-Hans/","GitHubAccessToken":"***"} |
|||
``` |
|||
|
|||
注意 `GitHubAccessToken` 用 `***` 掩盖. 这是一个私人令牌,你必须从GitHub获取它. 请参阅 https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/ |
|||
|
|||
- MainWebsiteUrl: `/` |
|||
|
|||
- LatestVersionBranchName: `master` |
|||
|
|||
对于 `SQL` 数据库,你可以使用下面的 `T-SQL` 命令将指定的示例插入到 `DocsProjects` 表中: |
|||
|
|||
```mssql |
|||
INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939658', N'ABP framework (GitHub)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'GitHub', N'{"GitHubRootUrl":"https://github.com/abpframework/abp/tree/{version}/docs/zh-Hans/","GitHubAccessToken":"***"}', N'/', N'master') |
|||
``` |
|||
|
|||
请注意,`GitHubAccessToken` 被屏蔽了.它是一个私人令牌,你必须获得自己的令牌并替换 `***` 字符串. |
|||
|
|||
#### "FileSystem" 项目的示例记录 |
|||
|
|||
你可以使用 [ABP Framework](https://github.com/abpframework/abp/) GitHub文档来配置你的文件系统存储. |
|||
|
|||
- Name: `ABP framework (FileSystem)` |
|||
|
|||
- ShortName: `abp` |
|||
|
|||
- Format: `md` |
|||
|
|||
- DefaultDocumentName: `Index` |
|||
|
|||
- NavigationDocumentName: `docs-nav.json` |
|||
|
|||
- MinimumVersion: `<NULL>` (no minimum version) |
|||
|
|||
- DocumentStoreType: `FileSystem` |
|||
|
|||
- ExtraProperties: |
|||
|
|||
```json |
|||
{"Path":"C:\\Github\\abp\\docs\\zh-Hans"} |
|||
``` |
|||
|
|||
请注意 `Path` 必须使用本地docs目录替换. 你可以从https://github.com/abpframework/abp/tree/master/docs/zh-hans获取ABP Framework的文档并且复制到该目录 `C:\\Github\\abp\\docs\\zh-Hans` 使其正常工作. |
|||
|
|||
- MainWebsiteUrl: `/` |
|||
|
|||
- LatestVersionBranchName: `<NULL>` |
|||
|
|||
对于 `SQL` 数据库,你可以使用下面的 `T-SQL` 命令将指定的示例插入到 `DocsProjects` 表中: |
|||
|
|||
```mssql |
|||
INSERT [dbo].[DocsProjects] ([Id], [Name], [ShortName], [Format], [DefaultDocumentName], [NavigationDocumentName], [MinimumVersion], [DocumentStoreType], [ExtraProperties], [MainWebsiteUrl], [LatestVersionBranchName]) VALUES (N'12f21123-e08e-4f15-bedb-ae0b2d939659', N'ABP framework (FileSystem)', N'abp', N'md', N'Index', N'docs-nav.json', NULL, N'FileSystem', N'{"Path":"C:\\Github\\abp\\docs\\zh-Hans"}', N'/', NULL) |
|||
``` |
|||
|
|||
添加上面的一个示例项目后运行该应用程序. 在菜单中你会看到`文档` 链接,点击菜单链接打开文档页面. |
|||
|
|||
到目前为止, 我们已经从abp.io网站创建了一个新的应用程序,并为Docs模块做好准备. |
|||
|
|||
### 7- 添加一个新文档 |
|||
|
|||
在示例项目记录中, 你可以看到 `Format` 被指定为 `md` 指的是 [Mark Down](https://en.wikipedia.org/wiki/Markdown). 你可以打开下面的链接查看语法; |
|||
|
|||
https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet |
|||
|
|||
ABP文档模块可以把MarkDown渲染为HTML. |
|||
|
|||
现在让我们看一下Markdown格式的示例文档. |
|||
|
|||
~~~markdown |
|||
# This is a header |
|||
|
|||
Welcome to Docs Module. |
|||
|
|||
## This is a sub header |
|||
|
|||
[This is a link](https://abp.io) |
|||
|
|||
 |
|||
|
|||
## This is a code block |
|||
|
|||
```csharp |
|||
public class Person |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public string Address { get; set; } |
|||
} |
|||
``` |
|||
~~~ |
|||
|
|||
你可以使用 ABP Framework 的文档做为示例: |
|||
|
|||
[https://github.com/abpframework/abp/blob/master/docs/zh-Hans/](https://github.com/abpframework/abp/blob/master/docs/zh-Hans/) |
|||
|
|||
### 8- 创建文档导航 |
|||
|
|||
导航文档是文档页面的主菜单. 它位于页面的左侧,是一个`JSON` 文件. 请查看以下示例导航文档以了解结构. |
|||
|
|||
```json |
|||
{ |
|||
"items":[ |
|||
{ |
|||
"text":"Sample Menu Item - 1", |
|||
"items":[ |
|||
{ |
|||
"text":"Sample Menu Item - 1.1", |
|||
"items":[ |
|||
{ |
|||
"text":"Sample Menu Item - 1.1.1", |
|||
"path":"SampleMenuItem_1_1_1.md" |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text":"Sample Menu Item - 1.2", |
|||
"items":[ |
|||
{ |
|||
"text":"Sample Menu Item - 1.2.1", |
|||
"path":"SampleMenuItem_1_2_1.md" |
|||
}, |
|||
{ |
|||
"text":"Sample Menu Item - 1.2.2", |
|||
"path":"SampleMenuItem_1_2_2.md" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"text":"Sample Menu Item - 2", |
|||
"items":[ |
|||
{ |
|||
"text":"Sample Menu Item - 2.1", |
|||
"items":[ |
|||
{ |
|||
"text":"Sample Menu Item - 2.1.1", |
|||
"path":"SampleMenuItem_2_1_1.md" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
上面的示例 `JSON` 文件将下面的导航菜单呈现为 `HTML` . |
|||
|
|||
 |
|||
|
|||
最后,为您的项目添加了一个新的Docs模块, 该模块由GitHub提供. |
|||
@ -0,0 +1,26 @@ |
|||
# 应用程序模块 |
|||
|
|||
ABP是一个 **模块化的应用程序框架** 由十多个 **nuget packages** 组成. 它提供了一个完整的基础设施来构建你自己的应用程序模块,这些模块包含实体,服务,数据库集成,API,UI组件等. |
|||
|
|||
**有两种类型的模块.** 它们没有任何结构上的差异,只是按照功能和目地分类: |
|||
|
|||
* [**框架模块**](https://github.com/abpframework/abp/tree/master/framework/src): 这些是 **框架的核心模块**,像缓存,邮件,主题,安全性,序列化,验证,Ef Core集成,MongoDB集成...等等. 它们没有应用程序/业务功能,但通过提供通用基础架构,集成和抽象会使你的日常开发更加容易. |
|||
* [**应用程序模块**](https://github.com/abpframework/abp/tree/master/modules): 这些模块是实现特定的应用程序/业务功能,像 博客, 文档管理, 身份管理, 租户管理... 等等. 它是通常有自己的实体,服务,API和UI组件. |
|||
|
|||
## 开源的应用程序模块 |
|||
|
|||
有一些由ABP社区开发和维护的 **开源免费** 的应用程序模块: |
|||
|
|||
* **Account**: 用于用户登录/注册应用程序. |
|||
* **Audit Logging**: 用于将审计日志持久化到数据库. |
|||
* **Background Jobs**: 用于在使用默认后台作业管理器时保存后台作业. |
|||
* **Blogging**: 用于创建精美的博客. ABP的[博客](https://abp.io/blog/abp/) 就使用了此模块. |
|||
* [**Docs**](Docs.md): 用于创建技术文档页面. ABP的[文档](https://abp.io/documents/) 就使用了此模块. |
|||
* **Identity**: 用于管理角色,用户和他们的权限. |
|||
* **Identity Server**: 集成了IdentityServer4. |
|||
* **Permission Management**: 用于保存权限. |
|||
* **Setting Management**: 用于保存设置. |
|||
* **Tenant Management**: 用于管理[多租户](../Multi-Tenancy.md)应用程序的租户. |
|||
* **Users**: 用于抽象用户, 因此其他模块可以依赖此模块而不是Identity模块. |
|||
|
|||
模块化文档正在编写中. 请参阅[这个仓库](https://github.com/abpframework/abp/tree/master/modules)获取所有模块的源代码. |
|||
@ -0,0 +1,3 @@ |
|||
## Unit of Work |
|||
|
|||
待添加 |
|||
@ -0,0 +1,3 @@ |
|||
## Validation |
|||
|
|||
待添加 |
|||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 10 KiB |