diff --git a/.gitignore b/.gitignore index 6630942ad5..0ca27f04e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,234 @@ -\.vs/ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +# User-specific files +*.suo *.user -.couscous/ -docs/Template-Dark/ -.idea/ -**/_site/ -log.txt +*.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/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 0ccf59d162..9c0ada4e4b 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Data/AppDbContext.cs @@ -1,6 +1,7 @@ using JsonApiDotNetCoreExample.Models; using JsonApiDotNetCore.Models; using Microsoft.EntityFrameworkCore; +using JsonApiDotNetCoreExample.Models.Entities; namespace JsonApiDotNetCoreExample.Data { @@ -24,6 +25,20 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasOne(t => t.Owner) .WithMany(p => p.TodoItems) .HasForeignKey(t => t.OwnerId); + + modelBuilder.Entity() + .HasKey(r => new { r.CourseId, r.StudentId }); + + modelBuilder.Entity() + .HasOne(r => r.Course) + .WithMany(c => c.Students) + .HasForeignKey(r => r.CourseId) + ; + + modelBuilder.Entity() + .HasOne(r => r.Student) + .WithMany(s => s.Courses) + .HasForeignKey(r => r.StudentId); } public DbSet TodoItems { get; set; } @@ -39,6 +54,12 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) public DbSet Authors { get; set; } public DbSet NonJsonApiResources { get; set; } public DbSet Users { get; set; } + + public DbSet Courses { get; set; } + public DbSet Departments { get; set; } + public DbSet Registrations { get; set; } + public DbSet Students { get; set; } + public DbSet PersonRoles { get; set; } } } diff --git a/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj b/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj index fd657e83bf..d56e91f21e 100755 --- a/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj +++ b/src/Examples/JsonApiDotNetCoreExample/JsonApiDotNetCoreExample.csproj @@ -13,6 +13,8 @@ + + diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.Designer.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.Designer.cs deleted file mode 100644 index c9788bf82f..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.Designer.cs +++ /dev/null @@ -1,162 +0,0 @@ -// -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 Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -namespace JsonApiDotNetCoreExample.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20180327120810_initial")] - partial class initial - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) - .HasAnnotation("ProductVersion", "2.0.1-rtm-125"); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Article", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AuthorId"); - - b.Property("Name"); - - b.HasKey("Id"); - - b.HasIndex("AuthorId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Author", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Name"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.CamelCasedModel", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("CompoundAttr"); - - b.HasKey("Id"); - - b.ToTable("CamelCasedModels"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("FirstName"); - - b.Property("LastName"); - - b.HasKey("Id"); - - b.ToTable("People"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AchievedDate"); - - b.Property("AssigneeId"); - - b.Property("CollectionId"); - - b.Property("CreatedDate") - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - - b.Property("Description"); - - b.Property("GuidProperty"); - - b.Property("Ordinal"); - - b.Property("OwnerId"); - - b.HasKey("Id"); - - b.HasIndex("AssigneeId"); - - b.HasIndex("CollectionId"); - - b.HasIndex("OwnerId"); - - b.ToTable("TodoItems"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Name"); - - b.Property("OwnerId"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.ToTable("TodoItemCollections"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Article", b => - { - b.HasOne("JsonApiDotNetCoreExample.Models.Author", "Author") - .WithMany("Articles") - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b => - { - b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Assignee") - .WithMany("AssignedTodoItems") - .HasForeignKey("AssigneeId"); - - b.HasOne("JsonApiDotNetCoreExample.Models.TodoItemCollection", "Collection") - .WithMany("TodoItems") - .HasForeignKey("CollectionId"); - - b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner") - .WithMany("TodoItems") - .HasForeignKey("OwnerId"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b => - { - b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner") - .WithMany("TodoItemCollections") - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.cs deleted file mode 100644 index ffbf105255..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Migrations/20180327120810_initial.cs +++ /dev/null @@ -1,175 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using System; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -namespace JsonApiDotNetCoreExample.Migrations -{ - public partial class initial : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Authors", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), - Name = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Authors", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "CamelCasedModels", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), - CompoundAttr = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_CamelCasedModels", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "People", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), - FirstName = table.Column(nullable: true), - LastName = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_People", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Articles", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), - AuthorId = table.Column(nullable: false), - Name = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Articles", x => x.Id); - table.ForeignKey( - name: "FK_Articles_Authors_AuthorId", - column: x => x.AuthorId, - principalTable: "Authors", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "TodoItemCollections", - columns: table => new - { - Id = table.Column(nullable: false), - Name = table.Column(nullable: true), - OwnerId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_TodoItemCollections", x => x.Id); - table.ForeignKey( - name: "FK_TodoItemCollections_People_OwnerId", - column: x => x.OwnerId, - principalTable: "People", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "TodoItems", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn), - AchievedDate = table.Column(nullable: true), - AssigneeId = table.Column(nullable: true), - CollectionId = table.Column(nullable: true), - CreatedDate = table.Column(nullable: false, defaultValueSql: "CURRENT_TIMESTAMP"), - Description = table.Column(nullable: true), - GuidProperty = table.Column(nullable: false), - Ordinal = table.Column(nullable: false), - OwnerId = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_TodoItems", x => x.Id); - table.ForeignKey( - name: "FK_TodoItems_People_AssigneeId", - column: x => x.AssigneeId, - principalTable: "People", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_TodoItems_TodoItemCollections_CollectionId", - column: x => x.CollectionId, - principalTable: "TodoItemCollections", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_TodoItems_People_OwnerId", - column: x => x.OwnerId, - principalTable: "People", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateIndex( - name: "IX_Articles_AuthorId", - table: "Articles", - column: "AuthorId"); - - migrationBuilder.CreateIndex( - name: "IX_TodoItemCollections_OwnerId", - table: "TodoItemCollections", - column: "OwnerId"); - - migrationBuilder.CreateIndex( - name: "IX_TodoItems_AssigneeId", - table: "TodoItems", - column: "AssigneeId"); - - migrationBuilder.CreateIndex( - name: "IX_TodoItems_CollectionId", - table: "TodoItems", - column: "CollectionId"); - - migrationBuilder.CreateIndex( - name: "IX_TodoItems_OwnerId", - table: "TodoItems", - column: "OwnerId"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Articles"); - - migrationBuilder.DropTable( - name: "CamelCasedModels"); - - migrationBuilder.DropTable( - name: "TodoItems"); - - migrationBuilder.DropTable( - name: "Authors"); - - migrationBuilder.DropTable( - name: "TodoItemCollections"); - - migrationBuilder.DropTable( - name: "People"); - } - } -} diff --git a/src/Examples/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs b/src/Examples/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs deleted file mode 100755 index 08c284393e..0000000000 --- a/src/Examples/JsonApiDotNetCoreExample/Migrations/AppDbContextModelSnapshot.cs +++ /dev/null @@ -1,161 +0,0 @@ -// -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 Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -namespace JsonApiDotNetCoreExample.Migrations -{ - [DbContext(typeof(AppDbContext))] - partial class AppDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.SerialColumn) - .HasAnnotation("ProductVersion", "2.0.1-rtm-125"); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Article", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AuthorId"); - - b.Property("Name"); - - b.HasKey("Id"); - - b.HasIndex("AuthorId"); - - b.ToTable("Articles"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Author", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Name"); - - b.HasKey("Id"); - - b.ToTable("Authors"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.CamelCasedModel", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("CompoundAttr"); - - b.HasKey("Id"); - - b.ToTable("CamelCasedModels"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("FirstName"); - - b.Property("LastName"); - - b.HasKey("Id"); - - b.ToTable("People"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("AchievedDate"); - - b.Property("AssigneeId"); - - b.Property("CollectionId"); - - b.Property("CreatedDate") - .ValueGeneratedOnAdd() - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - - b.Property("Description"); - - b.Property("GuidProperty"); - - b.Property("Ordinal"); - - b.Property("OwnerId"); - - b.HasKey("Id"); - - b.HasIndex("AssigneeId"); - - b.HasIndex("CollectionId"); - - b.HasIndex("OwnerId"); - - b.ToTable("TodoItems"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("Name"); - - b.Property("OwnerId"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.ToTable("TodoItemCollections"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.Article", b => - { - b.HasOne("JsonApiDotNetCoreExample.Models.Author", "Author") - .WithMany("Articles") - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Cascade); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItem", b => - { - b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Assignee") - .WithMany("AssignedTodoItems") - .HasForeignKey("AssigneeId"); - - b.HasOne("JsonApiDotNetCoreExample.Models.TodoItemCollection", "Collection") - .WithMany("TodoItems") - .HasForeignKey("CollectionId"); - - b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner") - .WithMany("TodoItems") - .HasForeignKey("OwnerId"); - }); - - modelBuilder.Entity("JsonApiDotNetCoreExample.Models.TodoItemCollection", b => - { - b.HasOne("JsonApiDotNetCoreExample.Models.Person", "Owner") - .WithMany("TodoItemCollections") - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Entities/CourseEntity.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Entities/CourseEntity.cs new file mode 100644 index 0000000000..a5e2c45f52 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Entities/CourseEntity.cs @@ -0,0 +1,31 @@ +using JsonApiDotNetCore.Models; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace JsonApiDotNetCoreExample.Models.Entities +{ + [Table("Course")] + public class CourseEntity : Identifiable + { + [Column("number")] + [Required] + public int Number { get; set; } + + [Column("title")] + [Required] + [StringLength(255)] + public string Title { get; set; } + + [Column("description")] + [StringLength(4000)] + public string Description { get; set; } + + public DepartmentEntity Department { get; set; } + + [Column("department_id")] + public int? DepartmentId { get; set; } + + public List Students { get; set; } + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Entities/CourseStudentEntity.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Entities/CourseStudentEntity.cs new file mode 100644 index 0000000000..3fe23cdc67 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Entities/CourseStudentEntity.cs @@ -0,0 +1,50 @@ +using JsonApiDotNetCore.Models; +using Microsoft.EntityFrameworkCore.Infrastructure; +using System.ComponentModel.DataAnnotations.Schema; + +namespace JsonApiDotNetCoreExample.Models.Entities +{ + [Table("CourseStudent")] + public class CourseStudentEntity : Identifiable + { + private CourseEntity _course; + private StudentEntity _student; + private ILazyLoader _loader { get; set; } + private CourseStudentEntity(ILazyLoader loader) + { + _loader = loader; + } + + public CourseStudentEntity(int courseId, int studentId) + { + CourseId = courseId; + StudentId = studentId; + } + + public CourseStudentEntity(CourseEntity course, StudentEntity student) + { + Course = course; + CourseId = course.Id; + Student = student; + StudentId = student.Id; + } + + [Column("course_id")] + public int CourseId { get; set; } + + public CourseEntity Course + { + get => _loader.Load(this, ref _course); + set => _course = value; + } + + [Column("student_id")] + public int StudentId { get; set; } + + public StudentEntity Student + { + get => _loader.Load(this, ref _student); + set => _student = value; + } + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Entities/DepartmentEntity.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Entities/DepartmentEntity.cs new file mode 100644 index 0000000000..337de4279f --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Entities/DepartmentEntity.cs @@ -0,0 +1,17 @@ +using JsonApiDotNetCore.Models; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace JsonApiDotNetCoreExample.Models.Entities +{ + [Table("Department")] + public class DepartmentEntity : Identifiable + { + [Required] + [StringLength(255, MinimumLength = 3)] + public string Name { get; set; } + + public List Courses { get; set; } + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Entities/StudentEntity.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Entities/StudentEntity.cs new file mode 100644 index 0000000000..1e23a471c5 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Entities/StudentEntity.cs @@ -0,0 +1,25 @@ +using JsonApiDotNetCore.Models; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace JsonApiDotNetCoreExample.Models.Entities +{ + [Table("Student")] + public class StudentEntity : Identifiable + { + [Column("firstname")] + [Required] + public string FirstName { get; set; } + + [Column("lastname")] + [Required] + [StringLength(255, MinimumLength = 3)] + public string LastName { get; set; } + + [Column("address")] + public string Address { get; set; } + + public List Courses { get; set; } + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs index 0a72ce2bd1..fdb2f1f174 100644 --- a/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Person.cs @@ -32,7 +32,7 @@ public class Person : Identifiable, IHasMeta public virtual PersonRole Role { get; set; } public int? PersonRoleId { get; set; } - [HasOne("unincludeable-item", Link.All, canInclude: false)] + [HasOne("unincludeable-item", documentLinks: Link.All, canInclude: false)] public virtual TodoItem UnIncludeableItem { get; set; } public Dictionary GetMeta(IJsonApiContext context) diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Resources/CourseResource.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Resources/CourseResource.cs new file mode 100644 index 0000000000..cacd9e11d1 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Resources/CourseResource.cs @@ -0,0 +1,27 @@ +using JsonApiDotNetCore.Models; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace JsonApiDotNetCoreExample.Models.Resources +{ + public class CourseResource : Identifiable + { + [Attr("number")] + [Required] + public int Number { get; set; } + + [Attr("title")] + [Required] + public string Title { get; set; } + + [Attr("description")] + public string Description { get; set; } + + [HasOne("department")] + public DepartmentResource Department { get; set; } + public int? DepartmentId { get; set; } + + [HasMany("students")] + public List Students { get; set; } + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Resources/CourseStudentResource.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Resources/CourseStudentResource.cs new file mode 100644 index 0000000000..c6a6619ab8 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Resources/CourseStudentResource.cs @@ -0,0 +1,23 @@ +using JsonApiDotNetCore.Models; + +namespace JsonApiDotNetCoreExample.Models.Resources +{ + /// + /// Note: EF Core *requires* the creation of an additional entity + /// for many to many relationships and no longer implicitly creates + /// it. While it may not make sense to create a corresponding "resource" + /// for that relationship, due to the need to make the underlying + /// framework and mapping understand the explicit navigation entity, + /// a mirroring DTO resource is also required. + /// + public class CourseStudentResource : Identifiable + { + [HasOne("course")] + public CourseResource Course { get; set; } + public int CourseId { get; set; } + + [HasOne("student")] + public StudentResource Student { get; set; } + public int StudentId { get; set; } + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Resources/DepartmentResource.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Resources/DepartmentResource.cs new file mode 100644 index 0000000000..d9d6725b71 --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Resources/DepartmentResource.cs @@ -0,0 +1,14 @@ +using JsonApiDotNetCore.Models; +using System.Collections.Generic; + +namespace JsonApiDotNetCoreExample.Models.Resources +{ + public class DepartmentResource : Identifiable + { + [Attr("name")] + public string Name { get; set; } + + [HasMany("courses")] + public List Courses { get; set; } + } +} diff --git a/src/Examples/JsonApiDotNetCoreExample/Models/Resources/StudentResource.cs b/src/Examples/JsonApiDotNetCoreExample/Models/Resources/StudentResource.cs new file mode 100644 index 0000000000..086d81614b --- /dev/null +++ b/src/Examples/JsonApiDotNetCoreExample/Models/Resources/StudentResource.cs @@ -0,0 +1,23 @@ +using JsonApiDotNetCore.Models; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace JsonApiDotNetCoreExample.Models.Resources +{ + public class StudentResource : Identifiable + { + [Attr("firstname")] + [Required] + public string FirstName { get; set; } + + [Attr("lastname")] + [Required] + public string LastName { get; set; } + + [Attr("address")] + public string Address { get; set; } + + [HasMany("courses")] + public List Courses { get; set; } + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/Controllers/CoursesController.cs b/src/Examples/ResourceEntitySeparationExample/Controllers/CoursesController.cs new file mode 100644 index 0000000000..6809ace0bb --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Controllers/CoursesController.cs @@ -0,0 +1,17 @@ +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models.Resources; +using Microsoft.Extensions.Logging; + +namespace ResourceEntitySeparationExample.Controllers +{ + public class CoursesController : JsonApiController + { + public CoursesController( + IJsonApiContext jsonApiContext, + IResourceService resourceService, + ILoggerFactory loggerFactory) + : base(jsonApiContext, resourceService, loggerFactory) + { } + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/Controllers/DepartmentsController.cs b/src/Examples/ResourceEntitySeparationExample/Controllers/DepartmentsController.cs new file mode 100644 index 0000000000..08f3ab33ad --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Controllers/DepartmentsController.cs @@ -0,0 +1,18 @@ +using System; +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models.Resources; +using Microsoft.Extensions.Logging; + +namespace ResourceEntitySeparationExample.Controllers +{ + public class DepartmentsController : JsonApiController + { + public DepartmentsController( + IJsonApiContext jsonApiContext, + IResourceService resourceService, + ILoggerFactory loggerFactory) + : base(jsonApiContext, resourceService, loggerFactory) + { } + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/Controllers/StudentsController.cs b/src/Examples/ResourceEntitySeparationExample/Controllers/StudentsController.cs new file mode 100644 index 0000000000..34d5d33031 --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Controllers/StudentsController.cs @@ -0,0 +1,17 @@ +using JsonApiDotNetCore.Controllers; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Models.Resources; +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/Models/AutoMapperAdapter.cs b/src/Examples/ResourceEntitySeparationExample/Models/AutoMapperAdapter.cs new file mode 100644 index 0000000000..732b45babb --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Models/AutoMapperAdapter.cs @@ -0,0 +1,25 @@ +using AutoMapper; +using JsonApiDotNetCore.Models; + +namespace ResourceEntitySeparationExample.Models +{ + public class AutoMapperAdapter : IResourceMapper + { + private readonly IMapper _mapper; + + public AutoMapperAdapter(IMapper mapper) + { + _mapper = mapper; + } + + public TDestination Map(object source) + { + return _mapper.Map(source); + } + + public TDestination Map(TSource source) + { + return _mapper.Map(source); + } + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/Profiles/CourseProfile.cs b/src/Examples/ResourceEntitySeparationExample/Profiles/CourseProfile.cs new file mode 100644 index 0000000000..cc817045af --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Profiles/CourseProfile.cs @@ -0,0 +1,40 @@ +using AutoMapper; +using JsonApiDotNetCoreExample.Models.Entities; +using JsonApiDotNetCoreExample.Models.Resources; +using System.Collections.Generic; + +namespace ResourceEntitySeparationExample.Profiles +{ + public class CourseProfile : Profile + { + public CourseProfile() + { + CreateMap() + .ForMember(r => r.Students, opt => opt.MapFrom(e => StudentsFromRegistrations(e.Students))) + .ForMember(r => r.Department, opt => opt.MapFrom(e => new DepartmentResource + { + Id = e.Department.Id, + Name = e.Department.Name + })); + + CreateMap(); + } + + private ICollection StudentsFromRegistrations(ICollection registrations) + { + ICollection students = new HashSet(); + foreach(CourseStudentEntity reg in registrations) + { + StudentEntity e = reg.Student; + students.Add(new StudentResource + { + Id = e.Id, + FirstName = e.FirstName, + LastName = e.LastName, + Address = e.Address + }); + } + return students.Count == 0 ? null : students; + } + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/Profiles/DepartmentProfile.cs b/src/Examples/ResourceEntitySeparationExample/Profiles/DepartmentProfile.cs new file mode 100644 index 0000000000..c8f26fd125 --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Profiles/DepartmentProfile.cs @@ -0,0 +1,14 @@ +using AutoMapper; +using JsonApiDotNetCoreExample.Models.Entities; +using JsonApiDotNetCoreExample.Models.Resources; + +namespace ResourceEntitySeparationExample.Profiles +{ + public class DepartmentProfile : Profile + { + public DepartmentProfile() + { + CreateMap(); + } + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/Profiles/StudentProfile.cs b/src/Examples/ResourceEntitySeparationExample/Profiles/StudentProfile.cs new file mode 100644 index 0000000000..160f64c79a --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Profiles/StudentProfile.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using AutoMapper; +using JsonApiDotNetCoreExample.Models.Entities; +using JsonApiDotNetCoreExample.Models.Resources; + +namespace ResourceEntitySeparationExample.Profiles +{ + public class StudentProfile : Profile + { + public StudentProfile() + { + CreateMap() + .ForMember(d => d.Courses, opt => opt.MapFrom(e => CoursesFromRegistrations(e.Courses))); + + CreateMap(); + } + + private ICollection CoursesFromRegistrations(ICollection registrations) + { + ICollection courses = new HashSet(); + foreach (CourseStudentEntity reg in registrations) + { + CourseEntity e = reg.Course; + courses.Add(new CourseResource + { + Id = e.Id, + Number = e.Number, + Title = e.Title, + Description = e.Description + }); + } + return courses.Count == 0 ? null : courses; + } + } +} diff --git a/src/Examples/ResourceEntitySeparationExample/Program.cs b/src/Examples/ResourceEntitySeparationExample/Program.cs new file mode 100644 index 0000000000..3af82b653a --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Program.cs @@ -0,0 +1,15 @@ +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..fa6088fa63 --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/ResourceEntitySeparationExample.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp2.0 + + + + + + + + + + + + diff --git a/src/Examples/ResourceEntitySeparationExample/Startup.cs b/src/Examples/ResourceEntitySeparationExample/Startup.cs new file mode 100644 index 0000000000..5e0f045d13 --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/Startup.cs @@ -0,0 +1,87 @@ +using System; +using AutoMapper; +using JsonApiDotNetCore.Data; +using JsonApiDotNetCore.Extensions; +using JsonApiDotNetCore.Models; +using JsonApiDotNetCore.Services; +using JsonApiDotNetCoreExample.Data; +using JsonApiDotNetCoreExample.Models.Entities; +using JsonApiDotNetCoreExample.Models.Resources; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using ResourceEntitySeparationExample.Models; + +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 = 10; + options.IncludeTotalRecordCount = true; + options.BuildContextGraph((builder) => { + builder.AddResource("courses"); + builder.AddResource("departments"); + builder.AddResource("students"); + }); + }, mvcBuilder); + + services.AddAutoMapper(); + services.AddScoped(); + + services.AddScoped, EntityResourceService>(); + services.AddScoped, EntityResourceService>(); + services.AddScoped, EntityResourceService>(); + + 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..dc8fc4fae6 --- /dev/null +++ b/src/Examples/ResourceEntitySeparationExample/appsettings.json @@ -0,0 +1,12 @@ +{ + "Data": { + "DefaultConnection": "Host=localhost;Port=5432;Database=JsonApiDotNetCoreExample;User ID=postgres;Password=postgres" + }, + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Warning", + "Microsoft.EntityFrameworkCore.Database.Command": "Information" + } + } +} 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/Data/DefaultEntityRepository.cs b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs index d1b82e30bc..8cd3200a4e 100644 --- a/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs +++ b/src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs @@ -75,9 +75,7 @@ public virtual async Task GetAndIncludeAsync(TId id, string relationshi { _logger.LogDebug($"[JADN] GetAndIncludeAsync({id}, {relationshipName})"); - var result = await Get() - .Include(relationshipName) - .SingleOrDefaultAsync(e => e.Id.Equals(id)); + var result = await Include(Get(), relationshipName).SingleOrDefaultAsync(e => e.Id.Equals(id)); return result; } @@ -178,7 +176,6 @@ public virtual IQueryable Include(IQueryable entities, string throw new JsonApiException(400, $"Including the relationship {relationshipName} on {entity.EntityName} is not allowed"); } return entities.Include(relationship.InternalRelationshipName); - } public virtual async Task> PageAsync(IQueryable entities, int pageSize, int pageNumber) diff --git a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs index a327861f69..0199096316 100644 --- a/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs +++ b/src/JsonApiDotNetCore/Extensions/IServiceCollectionExtensions.cs @@ -93,7 +93,7 @@ public static void AddJsonApiInternals( if (jsonApiOptions.ContextGraph.UsesDbContext == false) { services.AddScoped(); - services.AddSingleton(new DbContextOptionsBuilder().Options); + services.AddSingleton(new DbContextOptionsBuilder().Options); } if (jsonApiOptions.EnableOperations) @@ -123,8 +123,8 @@ public static void AddJsonApiInternals( services.AddScoped(typeof(IResourceService<>), typeof(EntityResourceService<>)); services.AddScoped(typeof(IResourceService<,>), typeof(EntityResourceService<,>)); - services.AddSingleton(jsonApiOptions); - services.AddSingleton(jsonApiOptions.ContextGraph); + services.AddSingleton(jsonApiOptions); + services.AddSingleton(jsonApiOptions.ContextGraph); services.AddScoped(); services.AddSingleton(); services.AddScoped(); diff --git a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj index 6c7dda3460..224c39d594 100755 --- a/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj +++ b/src/JsonApiDotNetCore/JsonApiDotNetCore.csproj @@ -28,7 +28,7 @@ - +