From 5a387806633d6c80868733d379f79a2143c4d642 Mon Sep 17 00:00:00 2001 From: Simon Hofer Date: Tue, 9 Aug 2016 15:03:04 +0200 Subject: [PATCH 1/4] Feat: added handler methods to intercept jsonapi entity framework for custom entity manipulations --- .../EntityFrameworkDocumentMaterializer.cs | 47 +++++++++++++++++-- JSONAPI/Http/IDocumentMaterializer.cs | 8 ++++ .../IRelatedResourceDocumentMaterializer.cs | 8 ++++ JSONAPI/Http/JsonApiController.cs | 6 +++ JSONAPI/Http/MappedDocumentMaterializer.cs | 3 ++ ...ManyRelatedResourceDocumentMaterializer.cs | 3 ++ ...oOneRelatedResourceDocumentMaterializer.cs | 3 ++ 7 files changed, 73 insertions(+), 5 deletions(-) diff --git a/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs b/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs index d7b3ce73..bac897ff 100644 --- a/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs +++ b/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Linq.Expressions; using System.Net.Http; +using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using JSONAPI.Core; @@ -47,6 +48,8 @@ public EntityFrameworkDocumentMaterializer( _baseUrlService = baseUrlService; } + public IPrincipal Principal { get; set; } + public virtual Task GetRecords(HttpRequestMessage request, CancellationToken cancellationToken) { var query = _dbContext.Set().AsQueryable(); @@ -68,18 +71,19 @@ public virtual async Task CreateRecord(ISingleResourceD HttpRequestMessage request, CancellationToken cancellationToken) { var apiBaseUrl = GetBaseUrlFromRequest(request); - var newRecord = await MaterializeAsync(requestDocument.PrimaryData, cancellationToken); + var newRecord = await OnCreate(MaterializeAsync(requestDocument.PrimaryData, cancellationToken), Principal); await _dbContext.SaveChangesAsync(cancellationToken); var returnDocument = _singleResourceDocumentBuilder.BuildDocument(newRecord, apiBaseUrl, null, null); return returnDocument; } + public virtual async Task UpdateRecord(string id, ISingleResourceDocument requestDocument, HttpRequestMessage request, CancellationToken cancellationToken) { var apiBaseUrl = GetBaseUrlFromRequest(request); - var newRecord = await MaterializeAsync(requestDocument.PrimaryData, cancellationToken); + var newRecord = await OnUpdate(MaterializeAsync(requestDocument.PrimaryData, cancellationToken), Principal); var returnDocument = _singleResourceDocumentBuilder.BuildDocument(newRecord, apiBaseUrl, null, null); await _dbContext.SaveChangesAsync(cancellationToken); @@ -88,7 +92,7 @@ public virtual async Task UpdateRecord(string id, ISing public virtual async Task DeleteRecord(string id, HttpRequestMessage request, CancellationToken cancellationToken) { - var singleResource = await _dbContext.Set().FindAsync(cancellationToken, Convert.ChangeType(id, _resourceTypeRegistration.IdProperty.PropertyType)); + var singleResource = await OnDelete(_dbContext.Set().FindAsync(cancellationToken, Convert.ChangeType(id, _resourceTypeRegistration.IdProperty.PropertyType)), Principal); _dbContext.Set().Remove(singleResource); await _dbContext.SaveChangesAsync(cancellationToken); @@ -107,9 +111,9 @@ protected string GetBaseUrlFromRequest(HttpRequestMessage request) /// Convert a resource object into a material record managed by EntityFramework. /// /// - protected virtual async Task MaterializeAsync(IResourceObject resourceObject, CancellationToken cancellationToken) + protected virtual async Task MaterializeAsync(IResourceObject resourceObject, CancellationToken cancellationToken) { - return await _entityFrameworkResourceObjectMaterializer.MaterializeResourceObject(resourceObject, cancellationToken); + return (T) await _entityFrameworkResourceObjectMaterializer.MaterializeResourceObject(resourceObject, cancellationToken); } /// @@ -155,6 +159,39 @@ protected async Task GetRelatedToOne(string i return _singleResourceDocumentBuilder.BuildDocument(relatedResource, GetBaseUrlFromRequest(request), null, null); } + + /// + /// Manipulate entity before create. + /// + /// + /// + /// + protected virtual async Task OnCreate(Task record, IPrincipal principal) + { + return await record; + } + + /// + /// Manipulate entity before update. + /// + /// + /// + /// + protected virtual async Task OnUpdate(Task record, IPrincipal principal) + { + return await record; + } + /// + /// Manipulate entity before delete. + /// + /// + /// + /// + protected virtual async Task OnDelete(Task record, IPrincipal principal) + { + return await record; + } + private IQueryable Filter(Expression> predicate, params Expression>[] includes) where TResource : class { diff --git a/JSONAPI/Http/IDocumentMaterializer.cs b/JSONAPI/Http/IDocumentMaterializer.cs index b4eb4bce..58f67599 100644 --- a/JSONAPI/Http/IDocumentMaterializer.cs +++ b/JSONAPI/Http/IDocumentMaterializer.cs @@ -1,4 +1,5 @@ using System.Net.Http; +using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using JSONAPI.Documents; @@ -10,6 +11,13 @@ namespace JSONAPI.Http /// public interface IDocumentMaterializer { + + /// + /// Holds the current users principal and will be set by after locating the materializer. + /// + /// + IPrincipal Principal { get; set; } + /// /// Returns a document containing records that are filtered, sorted, /// and paginated according to query parameters present in the provided request. diff --git a/JSONAPI/Http/IRelatedResourceDocumentMaterializer.cs b/JSONAPI/Http/IRelatedResourceDocumentMaterializer.cs index 893c9069..2197086f 100644 --- a/JSONAPI/Http/IRelatedResourceDocumentMaterializer.cs +++ b/JSONAPI/Http/IRelatedResourceDocumentMaterializer.cs @@ -1,4 +1,5 @@ using System.Net.Http; +using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using JSONAPI.Documents; @@ -10,6 +11,13 @@ namespace JSONAPI.Http /// public interface IRelatedResourceDocumentMaterializer { + + /// + /// Holds the current users principal and will be set by after locating the materializer. + /// + /// + IPrincipal Principal { get; set; } + /// /// Builds a document containing the results of the relationship. /// diff --git a/JSONAPI/Http/JsonApiController.cs b/JSONAPI/Http/JsonApiController.cs index 09deb1b1..afdff5fe 100644 --- a/JSONAPI/Http/JsonApiController.cs +++ b/JSONAPI/Http/JsonApiController.cs @@ -27,6 +27,7 @@ public JsonApiController(IDocumentMaterializerLocator documentMaterializerLocato public virtual async Task GetResourceCollection(string resourceType, CancellationToken cancellationToken) { var materializer = _documentMaterializerLocator.GetMaterializerByResourceTypeName(resourceType); + materializer.Principal = RequestContext.Principal; var document = await materializer.GetRecords(Request, cancellationToken); return Ok(document); } @@ -37,6 +38,7 @@ public virtual async Task GetResourceCollection(string resour public virtual async Task Get(string resourceType, string id, CancellationToken cancellationToken) { var materializer = _documentMaterializerLocator.GetMaterializerByResourceTypeName(resourceType); + materializer.Principal = RequestContext.Principal; var document = await materializer.GetRecordById(id, Request, cancellationToken); return Ok(document); } @@ -48,6 +50,7 @@ public virtual async Task Get(string resourceType, string id, public virtual async Task GetRelatedResource(string resourceType, string id, string relationshipName, CancellationToken cancellationToken) { var materializer = _documentMaterializerLocator.GetRelatedResourceMaterializer(resourceType, relationshipName); + materializer.Principal = RequestContext.Principal; var document = await materializer.GetRelatedResourceDocument(id, Request, cancellationToken); return Ok(document); } @@ -58,6 +61,7 @@ public virtual async Task GetRelatedResource(string resourceT public virtual async Task Post(string resourceType, [FromBody]ISingleResourceDocument requestDocument, CancellationToken cancellationToken) { var materializer = _documentMaterializerLocator.GetMaterializerByResourceTypeName(resourceType); + materializer.Principal = RequestContext.Principal; var document = await materializer.CreateRecord(requestDocument, Request, cancellationToken); return Ok(document); } @@ -68,6 +72,7 @@ public virtual async Task Post(string resourceType, [FromBody public virtual async Task Patch(string resourceType, string id, [FromBody]ISingleResourceDocument requestDocument, CancellationToken cancellationToken) { var materializer = _documentMaterializerLocator.GetMaterializerByResourceTypeName(resourceType); + materializer.Principal = RequestContext.Principal; var document = await materializer.UpdateRecord(id, requestDocument, Request, cancellationToken); return Ok(document); } @@ -78,6 +83,7 @@ public virtual async Task Patch(string resourceType, string i public virtual async Task Delete(string resourceType, string id, CancellationToken cancellationToken) { var materializer = _documentMaterializerLocator.GetMaterializerByResourceTypeName(resourceType); + materializer.Principal = RequestContext.Principal; var document = await materializer.DeleteRecord(id, Request, cancellationToken); return Ok(document); } diff --git a/JSONAPI/Http/MappedDocumentMaterializer.cs b/JSONAPI/Http/MappedDocumentMaterializer.cs index 4c53c2e0..980df2d9 100644 --- a/JSONAPI/Http/MappedDocumentMaterializer.cs +++ b/JSONAPI/Http/MappedDocumentMaterializer.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Linq.Expressions; using System.Net.Http; +using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using JSONAPI.Core; @@ -64,6 +65,8 @@ private string ResourceTypeName get { return _resourceTypeRegistry.GetRegistrationForType(typeof (TDto)).ResourceTypeName; } } + public IPrincipal Principal { get; set; } + public virtual async Task GetRecords(HttpRequestMessage request, CancellationToken cancellationToken) { var entityQuery = GetQuery(); diff --git a/JSONAPI/Http/QueryableToManyRelatedResourceDocumentMaterializer.cs b/JSONAPI/Http/QueryableToManyRelatedResourceDocumentMaterializer.cs index 7d190960..d7cfc150 100644 --- a/JSONAPI/Http/QueryableToManyRelatedResourceDocumentMaterializer.cs +++ b/JSONAPI/Http/QueryableToManyRelatedResourceDocumentMaterializer.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Net.Http; +using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using JSONAPI.Documents; @@ -27,6 +28,8 @@ protected QueryableToManyRelatedResourceDocumentMaterializer( _sortExpressionExtractor = sortExpressionExtractor; } + public IPrincipal Principal { get; set; } + public async Task GetRelatedResourceDocument(string primaryResourceId, HttpRequestMessage request, CancellationToken cancellationToken) { diff --git a/JSONAPI/Http/QueryableToOneRelatedResourceDocumentMaterializer.cs b/JSONAPI/Http/QueryableToOneRelatedResourceDocumentMaterializer.cs index da9b23f4..d4de3685 100644 --- a/JSONAPI/Http/QueryableToOneRelatedResourceDocumentMaterializer.cs +++ b/JSONAPI/Http/QueryableToOneRelatedResourceDocumentMaterializer.cs @@ -1,4 +1,5 @@ using System.Net.Http; +using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using JSONAPI.Documents; @@ -25,6 +26,8 @@ protected QueryableToOneRelatedResourceDocumentMaterializer( _baseUrlService = baseUrlService; } + public IPrincipal Principal { get; set; } + public async Task GetRelatedResourceDocument(string primaryResourceId, HttpRequestMessage request, CancellationToken cancellationToken) { From 2217e92f42ee0bda6bc5456c70b24347dddb61e8 Mon Sep 17 00:00:00 2001 From: Simon Hofer Date: Tue, 9 Aug 2016 15:49:56 +0200 Subject: [PATCH 2/4] make the dbcontext accessible to subclasses for interception --- .../EntityFrameworkDocumentMaterializer.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs b/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs index bac897ff..25a1e201 100644 --- a/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs +++ b/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs @@ -19,7 +19,7 @@ namespace JSONAPI.EntityFramework.Http /// public class EntityFrameworkDocumentMaterializer : IDocumentMaterializer where T : class { - private readonly DbContext _dbContext; + protected readonly DbContext DbContext; private readonly IResourceTypeRegistration _resourceTypeRegistration; private readonly IQueryableResourceCollectionDocumentBuilder _queryableResourceCollectionDocumentBuilder; private readonly ISingleResourceDocumentBuilder _singleResourceDocumentBuilder; @@ -39,7 +39,7 @@ public EntityFrameworkDocumentMaterializer( ISortExpressionExtractor sortExpressionExtractor, IBaseUrlService baseUrlService) { - _dbContext = dbContext; + DbContext = dbContext; _resourceTypeRegistration = resourceTypeRegistration; _queryableResourceCollectionDocumentBuilder = queryableResourceCollectionDocumentBuilder; _singleResourceDocumentBuilder = singleResourceDocumentBuilder; @@ -52,7 +52,7 @@ public EntityFrameworkDocumentMaterializer( public virtual Task GetRecords(HttpRequestMessage request, CancellationToken cancellationToken) { - var query = _dbContext.Set().AsQueryable(); + var query = DbContext.Set().AsQueryable(); var sortExpressions = _sortExpressionExtractor.ExtractSortExpressions(request); return _queryableResourceCollectionDocumentBuilder.BuildDocument(query, request, sortExpressions, cancellationToken); } @@ -72,7 +72,7 @@ public virtual async Task CreateRecord(ISingleResourceD { var apiBaseUrl = GetBaseUrlFromRequest(request); var newRecord = await OnCreate(MaterializeAsync(requestDocument.PrimaryData, cancellationToken), Principal); - await _dbContext.SaveChangesAsync(cancellationToken); + await DbContext.SaveChangesAsync(cancellationToken); var returnDocument = _singleResourceDocumentBuilder.BuildDocument(newRecord, apiBaseUrl, null, null); return returnDocument; @@ -85,16 +85,16 @@ public virtual async Task UpdateRecord(string id, ISing var apiBaseUrl = GetBaseUrlFromRequest(request); var newRecord = await OnUpdate(MaterializeAsync(requestDocument.PrimaryData, cancellationToken), Principal); var returnDocument = _singleResourceDocumentBuilder.BuildDocument(newRecord, apiBaseUrl, null, null); - await _dbContext.SaveChangesAsync(cancellationToken); + await DbContext.SaveChangesAsync(cancellationToken); return returnDocument; } public virtual async Task DeleteRecord(string id, HttpRequestMessage request, CancellationToken cancellationToken) { - var singleResource = await OnDelete(_dbContext.Set().FindAsync(cancellationToken, Convert.ChangeType(id, _resourceTypeRegistration.IdProperty.PropertyType)), Principal); - _dbContext.Set().Remove(singleResource); - await _dbContext.SaveChangesAsync(cancellationToken); + var singleResource = await OnDelete(DbContext.Set().FindAsync(cancellationToken, Convert.ChangeType(id, _resourceTypeRegistration.IdProperty.PropertyType)), Principal); + DbContext.Set().Remove(singleResource); + await DbContext.SaveChangesAsync(cancellationToken); return null; } @@ -195,7 +195,7 @@ protected virtual async Task OnDelete(Task record, IPrincipal principal) private IQueryable Filter(Expression> predicate, params Expression>[] includes) where TResource : class { - IQueryable query = _dbContext.Set(); + IQueryable query = DbContext.Set(); if (includes != null && includes.Any()) query = includes.Aggregate(query, (current, include) => current.Include(include)); From dcd2f6922fb268c34753c64165e67f1f1ced140a Mon Sep 17 00:00:00 2001 From: Simon Hofer Date: Tue, 9 Aug 2016 18:57:25 +0200 Subject: [PATCH 3/4] @csantero implemented your suggestions --- .../EntityFrameworkDocumentMaterializer.cs | 18 ++++------ JSONAPI/Http/IDocumentMaterializer.cs | 8 ----- .../IRelatedResourceDocumentMaterializer.cs | 8 ----- JSONAPI/Http/JsonApiController.cs | 6 ---- JSONAPI/Http/MappedDocumentMaterializer.cs | 3 -- ...ManyRelatedResourceDocumentMaterializer.cs | 3 -- ...oOneRelatedResourceDocumentMaterializer.cs | 3 -- README.md | 36 +++++++++++++++++++ 8 files changed, 42 insertions(+), 43 deletions(-) diff --git a/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs b/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs index 25a1e201..2c771c27 100644 --- a/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs +++ b/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Linq.Expressions; using System.Net.Http; -using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using JSONAPI.Core; @@ -48,8 +47,6 @@ public EntityFrameworkDocumentMaterializer( _baseUrlService = baseUrlService; } - public IPrincipal Principal { get; set; } - public virtual Task GetRecords(HttpRequestMessage request, CancellationToken cancellationToken) { var query = DbContext.Set().AsQueryable(); @@ -71,7 +68,7 @@ public virtual async Task CreateRecord(ISingleResourceD HttpRequestMessage request, CancellationToken cancellationToken) { var apiBaseUrl = GetBaseUrlFromRequest(request); - var newRecord = await OnCreate(MaterializeAsync(requestDocument.PrimaryData, cancellationToken), Principal); + var newRecord = await OnCreate(MaterializeAsync(requestDocument.PrimaryData, cancellationToken)); await DbContext.SaveChangesAsync(cancellationToken); var returnDocument = _singleResourceDocumentBuilder.BuildDocument(newRecord, apiBaseUrl, null, null); @@ -83,7 +80,7 @@ public virtual async Task UpdateRecord(string id, ISing HttpRequestMessage request, CancellationToken cancellationToken) { var apiBaseUrl = GetBaseUrlFromRequest(request); - var newRecord = await OnUpdate(MaterializeAsync(requestDocument.PrimaryData, cancellationToken), Principal); + var newRecord = await OnUpdate(MaterializeAsync(requestDocument.PrimaryData, cancellationToken)); var returnDocument = _singleResourceDocumentBuilder.BuildDocument(newRecord, apiBaseUrl, null, null); await DbContext.SaveChangesAsync(cancellationToken); @@ -92,7 +89,7 @@ public virtual async Task UpdateRecord(string id, ISing public virtual async Task DeleteRecord(string id, HttpRequestMessage request, CancellationToken cancellationToken) { - var singleResource = await OnDelete(DbContext.Set().FindAsync(cancellationToken, Convert.ChangeType(id, _resourceTypeRegistration.IdProperty.PropertyType)), Principal); + var singleResource = await OnDelete(DbContext.Set().FindAsync(cancellationToken, Convert.ChangeType(id, _resourceTypeRegistration.IdProperty.PropertyType))); DbContext.Set().Remove(singleResource); await DbContext.SaveChangesAsync(cancellationToken); @@ -164,9 +161,8 @@ protected async Task GetRelatedToOne(string i /// Manipulate entity before create. /// /// - /// /// - protected virtual async Task OnCreate(Task record, IPrincipal principal) + protected virtual async Task OnCreate(Task record) { return await record; } @@ -175,9 +171,8 @@ protected virtual async Task OnCreate(Task record, IPrincipal principal) /// Manipulate entity before update. /// /// - /// /// - protected virtual async Task OnUpdate(Task record, IPrincipal principal) + protected virtual async Task OnUpdate(Task record) { return await record; } @@ -185,9 +180,8 @@ protected virtual async Task OnUpdate(Task record, IPrincipal principal) /// Manipulate entity before delete. /// /// - /// /// - protected virtual async Task OnDelete(Task record, IPrincipal principal) + protected virtual async Task OnDelete(Task record) { return await record; } diff --git a/JSONAPI/Http/IDocumentMaterializer.cs b/JSONAPI/Http/IDocumentMaterializer.cs index 58f67599..b4eb4bce 100644 --- a/JSONAPI/Http/IDocumentMaterializer.cs +++ b/JSONAPI/Http/IDocumentMaterializer.cs @@ -1,5 +1,4 @@ using System.Net.Http; -using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using JSONAPI.Documents; @@ -11,13 +10,6 @@ namespace JSONAPI.Http /// public interface IDocumentMaterializer { - - /// - /// Holds the current users principal and will be set by after locating the materializer. - /// - /// - IPrincipal Principal { get; set; } - /// /// Returns a document containing records that are filtered, sorted, /// and paginated according to query parameters present in the provided request. diff --git a/JSONAPI/Http/IRelatedResourceDocumentMaterializer.cs b/JSONAPI/Http/IRelatedResourceDocumentMaterializer.cs index 2197086f..893c9069 100644 --- a/JSONAPI/Http/IRelatedResourceDocumentMaterializer.cs +++ b/JSONAPI/Http/IRelatedResourceDocumentMaterializer.cs @@ -1,5 +1,4 @@ using System.Net.Http; -using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using JSONAPI.Documents; @@ -11,13 +10,6 @@ namespace JSONAPI.Http /// public interface IRelatedResourceDocumentMaterializer { - - /// - /// Holds the current users principal and will be set by after locating the materializer. - /// - /// - IPrincipal Principal { get; set; } - /// /// Builds a document containing the results of the relationship. /// diff --git a/JSONAPI/Http/JsonApiController.cs b/JSONAPI/Http/JsonApiController.cs index afdff5fe..09deb1b1 100644 --- a/JSONAPI/Http/JsonApiController.cs +++ b/JSONAPI/Http/JsonApiController.cs @@ -27,7 +27,6 @@ public JsonApiController(IDocumentMaterializerLocator documentMaterializerLocato public virtual async Task GetResourceCollection(string resourceType, CancellationToken cancellationToken) { var materializer = _documentMaterializerLocator.GetMaterializerByResourceTypeName(resourceType); - materializer.Principal = RequestContext.Principal; var document = await materializer.GetRecords(Request, cancellationToken); return Ok(document); } @@ -38,7 +37,6 @@ public virtual async Task GetResourceCollection(string resour public virtual async Task Get(string resourceType, string id, CancellationToken cancellationToken) { var materializer = _documentMaterializerLocator.GetMaterializerByResourceTypeName(resourceType); - materializer.Principal = RequestContext.Principal; var document = await materializer.GetRecordById(id, Request, cancellationToken); return Ok(document); } @@ -50,7 +48,6 @@ public virtual async Task Get(string resourceType, string id, public virtual async Task GetRelatedResource(string resourceType, string id, string relationshipName, CancellationToken cancellationToken) { var materializer = _documentMaterializerLocator.GetRelatedResourceMaterializer(resourceType, relationshipName); - materializer.Principal = RequestContext.Principal; var document = await materializer.GetRelatedResourceDocument(id, Request, cancellationToken); return Ok(document); } @@ -61,7 +58,6 @@ public virtual async Task GetRelatedResource(string resourceT public virtual async Task Post(string resourceType, [FromBody]ISingleResourceDocument requestDocument, CancellationToken cancellationToken) { var materializer = _documentMaterializerLocator.GetMaterializerByResourceTypeName(resourceType); - materializer.Principal = RequestContext.Principal; var document = await materializer.CreateRecord(requestDocument, Request, cancellationToken); return Ok(document); } @@ -72,7 +68,6 @@ public virtual async Task Post(string resourceType, [FromBody public virtual async Task Patch(string resourceType, string id, [FromBody]ISingleResourceDocument requestDocument, CancellationToken cancellationToken) { var materializer = _documentMaterializerLocator.GetMaterializerByResourceTypeName(resourceType); - materializer.Principal = RequestContext.Principal; var document = await materializer.UpdateRecord(id, requestDocument, Request, cancellationToken); return Ok(document); } @@ -83,7 +78,6 @@ public virtual async Task Patch(string resourceType, string i public virtual async Task Delete(string resourceType, string id, CancellationToken cancellationToken) { var materializer = _documentMaterializerLocator.GetMaterializerByResourceTypeName(resourceType); - materializer.Principal = RequestContext.Principal; var document = await materializer.DeleteRecord(id, Request, cancellationToken); return Ok(document); } diff --git a/JSONAPI/Http/MappedDocumentMaterializer.cs b/JSONAPI/Http/MappedDocumentMaterializer.cs index 980df2d9..4c53c2e0 100644 --- a/JSONAPI/Http/MappedDocumentMaterializer.cs +++ b/JSONAPI/Http/MappedDocumentMaterializer.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Linq.Expressions; using System.Net.Http; -using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using JSONAPI.Core; @@ -65,8 +64,6 @@ private string ResourceTypeName get { return _resourceTypeRegistry.GetRegistrationForType(typeof (TDto)).ResourceTypeName; } } - public IPrincipal Principal { get; set; } - public virtual async Task GetRecords(HttpRequestMessage request, CancellationToken cancellationToken) { var entityQuery = GetQuery(); diff --git a/JSONAPI/Http/QueryableToManyRelatedResourceDocumentMaterializer.cs b/JSONAPI/Http/QueryableToManyRelatedResourceDocumentMaterializer.cs index d7cfc150..7d190960 100644 --- a/JSONAPI/Http/QueryableToManyRelatedResourceDocumentMaterializer.cs +++ b/JSONAPI/Http/QueryableToManyRelatedResourceDocumentMaterializer.cs @@ -1,6 +1,5 @@ using System.Linq; using System.Net.Http; -using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using JSONAPI.Documents; @@ -28,8 +27,6 @@ protected QueryableToManyRelatedResourceDocumentMaterializer( _sortExpressionExtractor = sortExpressionExtractor; } - public IPrincipal Principal { get; set; } - public async Task GetRelatedResourceDocument(string primaryResourceId, HttpRequestMessage request, CancellationToken cancellationToken) { diff --git a/JSONAPI/Http/QueryableToOneRelatedResourceDocumentMaterializer.cs b/JSONAPI/Http/QueryableToOneRelatedResourceDocumentMaterializer.cs index d4de3685..da9b23f4 100644 --- a/JSONAPI/Http/QueryableToOneRelatedResourceDocumentMaterializer.cs +++ b/JSONAPI/Http/QueryableToOneRelatedResourceDocumentMaterializer.cs @@ -1,5 +1,4 @@ using System.Net.Http; -using System.Security.Principal; using System.Threading; using System.Threading.Tasks; using JSONAPI.Documents; @@ -26,8 +25,6 @@ protected QueryableToOneRelatedResourceDocumentMaterializer( _baseUrlService = baseUrlService; } - public IPrincipal Principal { get; set; } - public async Task GetRelatedResourceDocument(string primaryResourceId, HttpRequestMessage request, CancellationToken cancellationToken) { diff --git a/README.md b/README.md index b930f0e6..5f9ed21c 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,42 @@ The classes in the `JSONAPI.EntityFramework` namespace take great advantage of t - [ ] Add some hints about the configuration of JSONAPI.EntityFramework +## Manipulate entities before JSONAPI.EntityFramework persists them +To change your entities before they get persisted you can extend the `EntityFrameworkDocumentMaterializer` class. You need to register your custom DocumentMaterializer in your `JsonApiConfiguration` like that: +```C# +configuration.RegisterEntityFrameworkResourceType(c =>c.UseDocumentMaterializer()); +``` +Afterwards you can override the `OnCreate`, `OnUpdate` or `OnDelete` methods in your `CustomDocumentMaterializer`. + +```C# +protected override async Task OnCreate(Task record) +{ + var entity = await base.OnUpdate(record); + entity.CreatedOn = DateTime.Now; + entity.CreatedBy = Principal?.Identity; + return entity; +} +``` + +> :information_source: HINT: To get the `Principal` you can add the following part into your `Startup.cs` which registers the `Principal` in Autofac and define a constructor Parameter on your `CustomDocumentMaterializer` of type `IPrincipal`. + +```C# + +configurator.OnApplicationLifetimeScopeCreating(builder => +{ +// ... +builder.Register(ctx => HttpContext.Current.GetOwinContext()).As(); +builder.Register((c, p) => + { + var owin = c.Resolve(); + return owin.Authentication.User; + }) + .As() + .InstancePerRequest(); +} +``` + + ## Set the context path of JSONAPI.EntityFramework Per default the routes created for the registered models from EntityFramework will appear in root folder. This can conflict with folders of the file system or other routes you may want to serve from the same project. From ed88c51d3ae689ebfd6edd1383906565bb865228 Mon Sep 17 00:00:00 2001 From: Simon Hofer Date: Wed, 10 Aug 2016 08:02:37 +0200 Subject: [PATCH 4/4] implemented as void method --- .../EntityFrameworkDocumentMaterializer.cs | 29 ++++++++++--------- README.md | 6 ++-- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs b/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs index 2c771c27..0b86166f 100644 --- a/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs +++ b/JSONAPI.EntityFramework/Http/EntityFrameworkDocumentMaterializer.cs @@ -68,9 +68,10 @@ public virtual async Task CreateRecord(ISingleResourceD HttpRequestMessage request, CancellationToken cancellationToken) { var apiBaseUrl = GetBaseUrlFromRequest(request); - var newRecord = await OnCreate(MaterializeAsync(requestDocument.PrimaryData, cancellationToken)); + var newRecord = MaterializeAsync(requestDocument.PrimaryData, cancellationToken); + await OnCreate(newRecord); await DbContext.SaveChangesAsync(cancellationToken); - var returnDocument = _singleResourceDocumentBuilder.BuildDocument(newRecord, apiBaseUrl, null, null); + var returnDocument = _singleResourceDocumentBuilder.BuildDocument(await newRecord, apiBaseUrl, null, null); return returnDocument; } @@ -80,8 +81,9 @@ public virtual async Task UpdateRecord(string id, ISing HttpRequestMessage request, CancellationToken cancellationToken) { var apiBaseUrl = GetBaseUrlFromRequest(request); - var newRecord = await OnUpdate(MaterializeAsync(requestDocument.PrimaryData, cancellationToken)); - var returnDocument = _singleResourceDocumentBuilder.BuildDocument(newRecord, apiBaseUrl, null, null); + var newRecord = MaterializeAsync(requestDocument.PrimaryData, cancellationToken); + await OnUpdate(newRecord); + var returnDocument = _singleResourceDocumentBuilder.BuildDocument(await newRecord, apiBaseUrl, null, null); await DbContext.SaveChangesAsync(cancellationToken); return returnDocument; @@ -89,8 +91,9 @@ public virtual async Task UpdateRecord(string id, ISing public virtual async Task DeleteRecord(string id, HttpRequestMessage request, CancellationToken cancellationToken) { - var singleResource = await OnDelete(DbContext.Set().FindAsync(cancellationToken, Convert.ChangeType(id, _resourceTypeRegistration.IdProperty.PropertyType))); - DbContext.Set().Remove(singleResource); + var singleResource = DbContext.Set().FindAsync(cancellationToken, Convert.ChangeType(id, _resourceTypeRegistration.IdProperty.PropertyType)); + await OnDelete(singleResource); + DbContext.Set().Remove(await singleResource); await DbContext.SaveChangesAsync(cancellationToken); return null; @@ -162,28 +165,28 @@ protected async Task GetRelatedToOne(string i /// /// /// - protected virtual async Task OnCreate(Task record) + protected virtual async Task OnCreate(Task record) { - return await record; + await record; } /// /// Manipulate entity before update. /// /// - /// - protected virtual async Task OnUpdate(Task record) + protected virtual async Task OnUpdate(Task record) { - return await record; + await record; } + /// /// Manipulate entity before delete. /// /// /// - protected virtual async Task OnDelete(Task record) + protected virtual async Task OnDelete(Task record) { - return await record; + await record; } private IQueryable Filter(Expression> predicate, diff --git a/README.md b/README.md index 5f9ed21c..129060b7 100644 --- a/README.md +++ b/README.md @@ -79,12 +79,12 @@ configuration.RegisterEntityFrameworkResourceType(c =>c.UseDocumen Afterwards you can override the `OnCreate`, `OnUpdate` or `OnDelete` methods in your `CustomDocumentMaterializer`. ```C# -protected override async Task OnCreate(Task record) +protected override async Task OnCreate(Task record) { - var entity = await base.OnUpdate(record); + await base.OnUpdate(record); + var entity = await record; entity.CreatedOn = DateTime.Now; entity.CreatedBy = Principal?.Identity; - return entity; } ```