From 6097476289b31f572df0dca9deb1e090cdbc6f79 Mon Sep 17 00:00:00 2001 From: Rob Lankey Date: Wed, 11 Jul 2018 13:59:16 -0400 Subject: [PATCH 01/17] Add AutoMapper DI library Add new MappingResourceService that can be used to separate and map between Entity Framework objects and JSON-API data transfer objects Added new example project, ResourceEntitySeparationExample, showing how to configure an entity/resource split and corresponding tests. --- JsonApiDotnetCore.sln | 30 +++ .../Data/AppDbContext.cs | 2 + .../Migrations/20180327120810_initial.cs | 18 ++ .../Migrations/AppDbContextModelSnapshot.cs | 69 +++++- .../Models/StudentDto.cs | 15 ++ .../Models/StudentEntity.cs | 22 ++ .../.gitignore | 234 ++++++++++++++++++ .../Controllers/StudentsController.cs | 17 ++ .../Profiles/StudentProfile.cs | 33 +++ .../Program.cs | 18 ++ .../Properties/launchSettings.json | 23 ++ .../ResourceEntitySeparationExample.csproj | 12 + .../Startup.cs | 78 ++++++ .../appsettings.Development.json | 9 + .../appsettings.json | 11 + src/JsonApiDotNetCore/AssemblyInfo.cs | 3 +- .../JsonApiDotNetCore.csproj | 3 +- .../Services/MappingResourceService.cs | 198 +++++++++++++++ .../.gitignore | 234 ++++++++++++++++++ .../ResourceEntitySeparationExampleTests.cs | 128 ++++++++++ ...esourceEntitySeparationExampleTests.csproj | 22 ++ .../TestStartup.cs | 22 ++ .../appsettings.json | 15 ++ 23 files changed, 1207 insertions(+), 9 deletions(-) create mode 100644 src/Examples/JsonApiDotNetCoreExample/Models/StudentDto.cs create mode 100644 src/Examples/JsonApiDotNetCoreExample/Models/StudentEntity.cs create mode 100644 src/Examples/ResourceEntitySeparationExample/.gitignore create mode 100644 src/Examples/ResourceEntitySeparationExample/Controllers/StudentsController.cs create mode 100644 src/Examples/ResourceEntitySeparationExample/Profiles/StudentProfile.cs create mode 100644 src/Examples/ResourceEntitySeparationExample/Program.cs create mode 100644 src/Examples/ResourceEntitySeparationExample/Properties/launchSettings.json create mode 100644 src/Examples/ResourceEntitySeparationExample/ResourceEntitySeparationExample.csproj create mode 100644 src/Examples/ResourceEntitySeparationExample/Startup.cs create mode 100644 src/Examples/ResourceEntitySeparationExample/appsettings.Development.json create mode 100644 src/Examples/ResourceEntitySeparationExample/appsettings.json create mode 100644 src/JsonApiDotNetCore/Services/MappingResourceService.cs create mode 100644 test/ResourceEntitySeparationExampleTests/.gitignore create mode 100644 test/ResourceEntitySeparationExampleTests/Acceptance/Extensibility/ResourceEntitySeparationExampleTests.cs create mode 100644 test/ResourceEntitySeparationExampleTests/ResourceEntitySeparationExampleTests.csproj create mode 100644 test/ResourceEntitySeparationExampleTests/TestStartup.cs create mode 100644 test/ResourceEntitySeparationExampleTests/appsettings.json diff --git a/JsonApiDotnetCore.sln b/JsonApiDotnetCore.sln index 4b7cf5ea47..ce0219b1c8 100644 --- a/JsonApiDotnetCore.sln +++ b/JsonApiDotnetCore.sln @@ -41,6 +41,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OperationsExample", "src\Ex EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OperationsExampleTests", "test\OperationsExampleTests\OperationsExampleTests.csproj", "{9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceEntitySeparationExample", "src\Examples\ResourceEntitySeparationExample\ResourceEntitySeparationExample.csproj", "{F4097194-9415-418A-AB4E-315C5D5466AF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceEntitySeparationExampleTests", "test\ResourceEntitySeparationExampleTests\ResourceEntitySeparationExampleTests.csproj", "{6DFA30D7-1679-4333-9779-6FB678E48EF5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -159,6 +163,30 @@ Global {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|x64.Build.0 = Release|Any CPU {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|x86.ActiveCfg = Release|Any CPU {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1}.Release|x86.Build.0 = Release|Any CPU + {F4097194-9415-418A-AB4E-315C5D5466AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F4097194-9415-418A-AB4E-315C5D5466AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F4097194-9415-418A-AB4E-315C5D5466AF}.Debug|x64.ActiveCfg = Debug|Any CPU + {F4097194-9415-418A-AB4E-315C5D5466AF}.Debug|x64.Build.0 = Debug|Any CPU + {F4097194-9415-418A-AB4E-315C5D5466AF}.Debug|x86.ActiveCfg = Debug|Any CPU + {F4097194-9415-418A-AB4E-315C5D5466AF}.Debug|x86.Build.0 = Debug|Any CPU + {F4097194-9415-418A-AB4E-315C5D5466AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F4097194-9415-418A-AB4E-315C5D5466AF}.Release|Any CPU.Build.0 = Release|Any CPU + {F4097194-9415-418A-AB4E-315C5D5466AF}.Release|x64.ActiveCfg = Release|Any CPU + {F4097194-9415-418A-AB4E-315C5D5466AF}.Release|x64.Build.0 = Release|Any CPU + {F4097194-9415-418A-AB4E-315C5D5466AF}.Release|x86.ActiveCfg = Release|Any CPU + {F4097194-9415-418A-AB4E-315C5D5466AF}.Release|x86.Build.0 = Release|Any CPU + {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Debug|x64.ActiveCfg = Debug|Any CPU + {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Debug|x64.Build.0 = Debug|Any CPU + {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Debug|x86.ActiveCfg = Debug|Any CPU + {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Debug|x86.Build.0 = Debug|Any CPU + {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|Any CPU.Build.0 = Release|Any CPU + {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x64.ActiveCfg = Release|Any CPU + {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x64.Build.0 = Release|Any CPU + {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x86.ActiveCfg = Release|Any CPU + {6DFA30D7-1679-4333-9779-6FB678E48EF5}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -175,6 +203,8 @@ Global {1F604666-BB0F-413E-922D-9D37C6073285} = {076E1AE4-FD25-4684-B826-CAAE37FEA0AA} {CF2C1EB6-8449-4B35-B8C7-F43D6D90632D} = {026FBC6C-AF76-4568-9B87-EC73457899FD} {9CD2C116-D133-4FE4-97DA-A9FEAFF045F1} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F} + {F4097194-9415-418A-AB4E-315C5D5466AF} = {026FBC6C-AF76-4568-9B87-EC73457899FD} + {6DFA30D7-1679-4333-9779-6FB678E48EF5} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A2421882-8F0A-4905-928F-B550B192F9A4} diff --git a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs index 6f50f9aa5b..4c250690f1 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -39,5 +39,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) public DbSet Authors { get; set; } public DbSet NonJsonApiResources { get; set; } public DbSet Users { get; set; } + + public DbSet Students { get; set; } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.cs index ffbf105255..f561c3f210 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.cs @@ -149,6 +149,21 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_TodoItems_OwnerId", table: "TodoItems", column: "OwnerId"); + + migrationBuilder.CreateTable( + name: "student", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), + firstname = table.Column(name: "first-name", nullable: false), + lastname = table.Column(name: "last-name", maxLength: 255, nullable: false), + address = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_student", x => x.Id); + }); } protected override void Down(MigrationBuilder migrationBuilder) @@ -170,6 +185,9 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.DropTable( name: "People"); + + migrationBuilder.DropTable( + name: "student"); } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs index 08c284393e..d674cb4e98 100755 --- a/src/Examples/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs @@ -1,12 +1,9 @@ -// +// +using System; using JsonApiDotNetCoreExample.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.EntityFrameworkCore.Storage.Internal; -using System; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace JsonApiDotNetCoreExample.Migrations @@ -19,7 +16,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) - .HasAnnotation("ProductVersion", "2.0.1-rtm-125"); + .HasAnnotation("ProductVersion", "2.1.0-rtm-30799") + .HasAnnotation("Relational:MaxIdentifierLength", 63); modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Article", b => { @@ -61,6 +59,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("CamelCasedModels"); }); + modelBuilder.Entity("JsonApiDotNetCoreExample.Models.NonJsonApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.HasKey("Id"); + + b.ToTable("NonJsonApiResources"); + }); + modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Person", b => { b.Property("Id") @@ -70,11 +78,37 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("LastName"); + b.Property("UnIncludeableItemId"); + b.HasKey("Id"); + b.HasIndex("UnIncludeableItemId"); + b.ToTable("People"); }); + modelBuilder.Entity("JsonApiDotNetCoreExample.Models.StudentEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Address") + .HasColumnName("address"); + + b.Property("FirstName") + .IsRequired() + .HasColumnName("first-name"); + + b.Property("LastName") + .IsRequired() + .HasColumnName("last-name") + .HasMaxLength(255); + + b.HasKey("Id"); + + b.ToTable("student"); + }); + modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b => { b.Property("Id") @@ -125,6 +159,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("TodoItemCollections"); }); + modelBuilder.Entity("JsonApiDotNetCoreExample.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Password"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Article", b => { b.HasOne("JsonApiDotNetCoreExample.Models.Author", "Author") @@ -133,6 +181,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade); }); + modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Person", b => + { + b.HasOne("JsonApiDotNetCoreExample.Models.TodoItem", "UnIncludeableItem") + .WithMany() + .HasForeignKey("UnIncludeableItemId"); + }); + modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b => { b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Assignee") diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/StudentDto.cs b/src/Examples/JsonApiDotNetCoreExample/Models/StudentDto.cs new file mode 100644 index 0000000000..a3f0d2ba47 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Models/StudentDto.cs @@ -0,0 +1,15 @@ +using JsonApiDotNetCore.Models; +using System.ComponentModel.DataAnnotations; + +namespace JsonApiDotNetCoreExample.Models +{ + public class StudentDto : Identifiable + { + [Attr("name")] + [Required] + public string Name { get; set; } + + [Attr("address")] + public string Address { get; set; } + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/StudentEntity.cs b/src/Examples/JsonApiDotNetCoreExample/Models/StudentEntity.cs new file mode 100644 index 0000000000..27a3811d7c --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Models/StudentEntity.cs @@ -0,0 +1,22 @@ +using JsonApiDotNetCore.Models; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace JsonApiDotNetCoreExample.Models +{ + [Table("student")] + public class StudentEntity : Identifiable + { + [Column("first-name")] + [Required] + public string FirstName { get; set; } + + [Column("last-name")] + [Required] + [StringLength(255, MinimumLength = 3)] + public string LastName { get; set; } + + [Column("address")] + public string Address { get; set; } + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/.gitignore b/src/Examples/ResourceEntitySeparationExample/.gitignore new file mode 100644 index 0000000000..0ca27f04e1 --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/.gitignore @@ -0,0 +1,234 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Microsoft Azure ApplicationInsights config file +ApplicationInsights.config + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ diff --git a/src/Examples/ResourceEntitySeparationExample/Controllers/StudentsController.cs b/src/Examples/ResourceEntitySeparationExample/Controllers/StudentsController.cs new file mode 100644 index 0000000000..99c43e8115 --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Controllers/StudentsController.cs @@ -0,0 +1,17 @@ +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models; +using Microsoft.Extensions.Logging; + +namespace ResourceEntitySeparationExample.Controllers +{ + public class StudentsController : JsonApiController + { + public StudentsController( + IJsonApiContext jsonApiContext, + IResourceService resourceService, + ILoggerFactory loggerFactory) + : base(jsonApiContext, resourceService, loggerFactory) + { } + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/Profiles/StudentProfile.cs b/src/Examples/ResourceEntitySeparationExample/Profiles/StudentProfile.cs new file mode 100644 index 0000000000..ae7a863417 --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Profiles/StudentProfile.cs @@ -0,0 +1,33 @@ +using AutoMapper; +using JsonApiDotNetCoreExample.Models; + +namespace ResourceEntitySeparationExample.Profiles +{ + public class StudentProfile : Profile + { + public StudentProfile() + { + CreateMap() + .ForMember(e => e.FirstName, opt => opt.MapFrom(d => StringSplit(d.Name, " ", 0))) + .ForMember(e => e.LastName, opt => opt.MapFrom(d => StringSplit(d.Name, " ", 1))); + CreateMap() + .ForMember(d => d.Name, opt => opt.MapFrom(e => e.FirstName + " " + e.LastName)); + } + + private string StringSplit(string value, string split, int pos) + { + if (value == null) + { + return null; + } + + var pieces = value.Split(split); + if (pieces.Length < pos+1) + { + return null; + } + + return pieces[pos]; + } + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/Program.cs b/src/Examples/ResourceEntitySeparationExample/Program.cs new file mode 100644 index 0000000000..d27e714a1c --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Program.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; + +namespace ResourceEntitySeparationExample +{ + public class Program + { + public static void Main(string[] args) + { + BuildWebHost(args).Run(); + } + + public static IWebHost BuildWebHost(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup() + .Build(); + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/Properties/launchSettings.json b/src/Examples/ResourceEntitySeparationExample/Properties/launchSettings.json new file mode 100644 index 0000000000..49b260ed41 --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:57181/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "api/v1/students", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "ResourceEntitySeparationExample": { + "commandName": "Project" + } + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/ResourceEntitySeparationExample.csproj b/src/Examples/ResourceEntitySeparationExample/ResourceEntitySeparationExample.csproj new file mode 100644 index 0000000000..357ac039c5 --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/ResourceEntitySeparationExample.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp2.0 + + + + + + + + diff --git a/src/Examples/ResourceEntitySeparationExample/Startup.cs b/src/Examples/ResourceEntitySeparationExample/Startup.cs new file mode 100644 index 0000000000..49b280105e --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Startup.cs @@ -0,0 +1,78 @@ +using System; +using AutoMapper; +using JsonApiDotNetCore.Data; +using JsonApiDotNetCore.Extensions; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Data; +using JsonApiDotNetCoreExample.Models; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace ResourceEntitySeparationExample +{ + public class Startup + { + public readonly IConfiguration Config; + + public Startup(IHostingEnvironment env) + { + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables(); + + Config = builder.Build(); + } + + public virtual IServiceProvider ConfigureServices(IServiceCollection services) + { + var loggerFactory = new LoggerFactory(); + loggerFactory.AddConsole(LogLevel.Warning); + services.AddSingleton(loggerFactory); + + services.AddDbContext(options => + options.UseNpgsql(GetDbConnectionString()), ServiceLifetime.Transient); + services.AddScoped>(); + + var mvcBuilder = services.AddMvcCore(); + + services.AddJsonApi(options => { + options.Namespace = "api/v1"; + options.DefaultPageSize = 5; + options.IncludeTotalRecordCount = true; + options.BuildContextGraph((builder) => { + builder.AddResource("students"); + }); + }, mvcBuilder); + + services.AddAutoMapper(); + + services.AddScoped, MappingResourceService>(); + + var provider = services.BuildServiceProvider(); + var appContext = provider.GetRequiredService(); + if (appContext == null) + throw new ArgumentException(); + + return provider; + } + + public virtual void Configure( + IApplicationBuilder app, + IHostingEnvironment env, + ILoggerFactory loggerFactory, + AppDbContext context) + { + context.Database.EnsureCreated(); + loggerFactory.AddConsole(Config.GetSection("Logging")); + app.UseJsonApi(); + } + + public string GetDbConnectionString() => Config["Data:DefaultConnection"]; + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/appsettings.Development.json b/src/Examples/ResourceEntitySeparationExample/appsettings.Development.json new file mode 100644 index 0000000000..e203e9407e --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/appsettings.json b/src/Examples/ResourceEntitySeparationExample/appsettings.json new file mode 100644 index 0000000000..c35c2b7555 --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/appsettings.json @@ -0,0 +1,11 @@ +{ + "Data": { + "DefaultConnection": "Host=localhost;Port=5432;Database=JsonApiDotNetCoreExample;User ID=postgres;Password=postgres" + }, + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Warning" + } + } +} diff --git a/src/JsonApiDotNetCore/AssemblyInfo.cs b/src/JsonApiDotNetCore/AssemblyInfo.cs index 47e8eee668..dc8b9ba84c 100644 --- a/src/JsonApiDotNetCore/AssemblyInfo.cs +++ b/src/JsonApiDotNetCore/AssemblyInfo.cs @@ -3,4 +3,5 @@ [assembly:InternalsVisibleTo("UnitTests")] [assembly:InternalsVisibleTo("JsonApiDotNetCoreExampleTests")] [assembly:InternalsVisibleTo("NoEntityFrameworkTests")] -[assembly:InternalsVisibleTo("Benchmarks")] \ No newline at end of file +[assembly:InternalsVisibleTo("Benchmarks")] +[assembly:InternalsVisibleTo("ResourceEntitySeparationExampleTests")] diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj index 6c7dda3460..b2879cfa7d 100755 --- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj +++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj @@ -20,6 +20,7 @@ + @@ -28,7 +29,7 @@ - +