You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Tight coupling between the repository layer and the the model attributes (Attr, HasOne, etc ... ) makes it hard to add very straight forward business logic in a custom service layer
Why?
The following internal snippet from DefaultEntityRepository shows how updating resources is currently reliant on JsonApiContext and the Attr attribute. The JsonApiContext property AttributesToUpdate contains a list of the AttrAttributes of updated properties of the model, which reflects update request that was sent to the application. These are then used to update values on the entity loaded from the database:
Note that the reliance here on JsonApiContext is fine, because reflectively inspecting the updatedEntity object and checking for any updated properties would not work. This is because it is not possible to distinguish between a null-value (of eg a string property) that
was set to null as a result of an update request,
is just null because it was never instantiated by the deserializer, which means the property wasn't targeted by the request
The problem is the reliance on AttrAttribute here. To see why, consider the following model
publicclassCompany:Identifiable<int>{// This exposed property will be targeted by our example request[Attr("company-name")]publicstringName{get;set;}// This exposed property will NOT be targeted by our example request,// but we will update in our custom service layer containing business logic[Attr("foobar")]publicstringFoobarExposed{get;set;}// This property is not exposed and as such can never be targeted by a // request, but we still want to update it using business logicpublicstringFoobarInternal{get;set;}}
Assume a request is updating only the exposed company-name attribute, and we'll set FoobarExposed and FoorbarInternal as a part of the business logic in our custom service layer:
publicclassCompanyService:EntityResourceService<Company>{privatereadonlyIJsonApiContext_jsonApiContext;privatereadonlyResourceGraph_graph;publicCompanyService( ...):base( ...){}publicoverrideTask<Company>UpdateAsync(intid,Companyresource){// This alone will not work: we need to add // FoobarExposed to _jsonApiContext.AttributesToUpdate// and for that we need to access the `[Attr("foobar")]`resource.FoobarExposed="foobar"// So we get `[Attr("foobar")]` from the resource graph. This is inconvenient!varfoobarExposedAttrAttribute=_graph.GetContextEntity<Company>().Attributes.Single( ...);_jsonApiContext.AttributesToUpdate.Add(foobarExposedAttrAttribute, ...);// This alone will again not work, but now there isn't even a `[Attr]`// that we can access from the resource graph because it isn't set in the modelresource.FoobarInternal="internal foobar"// so we need to instantiate it manually and set a bunch of internal properties using // reflection. This is NOT ok!varfoobarInternalDummyAttrAttribute=DoABunchOfSmellyReflection( ...);_jsonApiContext.AttributesToUpdate.Add(foobarInternalDummyAttrAttribute, ...);returnbase.UpdateAsync(resource);}}
My comments in this example should illustrate how hard it is for a developer to do such a seemingly straight forward thing. My main concern with it is that the developer needs to know about the internals of JsonApiDotNetCore to be able to custom update a property, because the dev needs to know:
about the internal usage of jsonApiContext.AttributesToUpdate
about how to instantiate a AttrAttribute and which internal properties to set.
Solution
We need to get rid of the reliance of the model attributes in the repository layer. These attributes should only be used to configure which properties are exposed externally, and therefore the DeSerializer and Serializer should care about them, but they shouldn't be required in the service/repository layer. To that end, I believe we need to consider two things
Replace the AttrAttribute in jsonApiContext.AttributesToUpdate with either
native PropertyInfos to internally communicate which properties are targeted
this will significantly complicate things to support entity-resource separation. But I am really wondering what the usecase is for this feature anyway? Do we need this fluff?
a new Attribute class that is required on EVERY property that is used by JADNC, so that we can easily communicate internally
feels very verbose and feels like it shouldn't be strictly necessary
Create a helper method that allows the developer to mark properties as updated without exposing the internal workings of jsonApiContext.AttributesToUpdate, something like jsonApiContext.MarkAsUpdated( company => company.FoobarInternal )
The text was updated successfully, but these errors were encountered:
So one of the things we should note here that could be a solution is a " entity core"esque implementation of the dbcontext for our resourcegraph. An example:
The issue
Attr
,HasOne
, etc ... ) makes it hard to add very straight forward business logic in a custom service layerWhy?
The following internal snippet from
DefaultEntityRepository
shows how updating resources is currently reliant onJsonApiContext
and theAttr
attribute. TheJsonApiContext
propertyAttributesToUpdate
contains a list of theAttrAttribute
s of updated properties of the model, which reflects update request that was sent to the application. These are then used to update values on the entity loaded from the database:Note that the reliance here on
JsonApiContext
is fine, because reflectively inspecting theupdatedEntity
object and checking for any updated properties would not work. This is because it is not possible to distinguish between anull
-value (of eg astring
property) thatnull
as a result of an update request,null
because it was never instantiated by the deserializer, which means the property wasn't targeted by the requestThe problem is the reliance on
AttrAttribute
here. To see why, consider the following modelAssume a request is updating only the exposed
company-name
attribute, and we'll setFoobarExposed
andFoorbarInternal
as a part of the business logic in our custom service layer:My comments in this example should illustrate how hard it is for a developer to do such a seemingly straight forward thing. My main concern with it is that the developer needs to know about the internals of
JsonApiDotNetCore
to be able to custom update a property, because the dev needs to know:jsonApiContext.AttributesToUpdate
AttrAttribute
and which internal properties to set.Solution
We need to get rid of the reliance of the model attributes in the repository layer. These attributes should only be used to configure which properties are exposed externally, and therefore the
DeSerializer
andSerializer
should care about them, but they shouldn't be required in the service/repository layer. To that end, I believe we need to consider two thingsAttrAttribute
injsonApiContext.AttributesToUpdate
with eitherPropertyInfo
s to internally communicate which properties are targetedAttribute
class that is required on EVERY property that is used by JADNC, so that we can easily communicate internallyjsonApiContext.AttributesToUpdate
, something likejsonApiContext.MarkAsUpdated( company => company.FoobarInternal )
The text was updated successfully, but these errors were encountered: