-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add the persistence of service models, for possible caching and other useful purposes... #6
Comments
Hi @scott-mcdonald. If this functionality is not yet implemented, could you please give some hints on how (and where) it should be implemented? I think I'm ready to implement it and contribute to the repository because we use this library in our project and face memory leaks (our DocumentContext contains a lot of configurations). Thanks. |
@yurii-pelekh So the name of this issue is misleading now that I think about this; what I meant was the persistence of the service models. This would be useful for persisting the computation cost of creating a service model, persisting the service model as a file, and subsequent "executions" would look for the file and if it exists, load the service model and use it immediately without rebuilding it. That is not that hard, but not simple either and to date have not added this feature... Therefore I am going to change the name of this issue respectively... But I don't think that is what you asking, It sounds like you are stating over time in some process you are creating multiple I do find that hard to believe if this is what you are stating. Typically whenever I am building a using var documentContext = Create DocumentContext here... Build document with Fluent API or some higher level framework... You will have to give me more information, specifically on how you determined |
Hi @scott-mcdonald! Thanks for the reply. Here is an example of my DocumentContext:
As you have noticed, my DocumentContext contains hundreds of Configuration objects. For all subsequent HTTP requests, all those Configuration objects are created from scratch. I noticed that there are a lot of memory allocations during request handling, and the root cause is in the DocumentContext. Because of that, GC (garbage collection) executes more often than expected. So my idea was not to create each Configuration object for each new subsequent request but to make them static so that the same objects are used in the future (thus reducing memory allocations and GC time). Could you please tell me, what may be wrong with my implementation of DocumentContext (I guess we should split it into multiple smaller ones or so), and whether it is possible to make Configurations static so they are not allocated for each new request? Thank you. |
Well, actually it should be easy, something like this:
|
Now I have clarity on exactly what you are talking about. Yes adding the configurations directly in the First some groundwork: JsonApiFramework represents a framework for just serialization/deserialization of I personally use Microsoft DI from the I will go ahead and give you at a high level how I abstract away the configuration of a service model and the creation of JsonApiFramework document context objects so it scales efficiently in some higher level frameworks that I have created. Note the following is incomplete and I'll leave it as an exercise if you want to even use it or go with something simpler like you have above. There is no correct answer, just sharing from one peer to another... First I have the following abstractions: /// <summary>Represents a registry of JSON API framework components needed when working
/// with the JSON API framework.</summary>
public interface IApiJsonApiFrameworkRegistry
{
#region Methods
/// <summary>Gets the JSON API framework service model, i.e. the API schema.</summary>
IServiceModel GetServiceModel();
/// <summary>Gets the JSON API framework options for creating a JSON API document context
/// which is used for serializing/deserializing {json:api} documents from/to POCO objects.</summary>
IDocumentContextOptions GetDocumentContextOptions();
#endregion
} /// <summary>Represents a factory to create a JSON API framework service model.</summary>
public interface IApiServiceModelFactory
{
#region Factory Methods
/// <summary>Creates a JSON API framework service model.</summary>
IServiceModel CreateServiceModel();
#endregion
} Some simple implementations.... internal class ApiJsonApiFrameworkRegistry : IApiJsonApiFrameworkRegistry
{
#region Properties
private IServiceModel ApiServiceModel { get; }
private IDocumentContextOptions ApiDocumentContextOptions { get; }
#endregion
#region Constructors
public ApiJsonApiFrameworkRegistry(IApiServiceModelFactory apiServiceModelFactory)
{
var apiServiceModel = apiServiceModelFactory.CreateServiceModel();
this.ApiServiceModel = apiServiceModel;
var apiDocumentContextOptions = CreateDocumentContextOptions(apiServiceModel);
this.ApiDocumentContextOptions = apiDocumentContextOptions;
}
#endregion
#region IApiJsonApiFrameworkRegistry Implementation
public IServiceModel GetServiceModel()
{
return this.ApiServiceModel;
}
public IDocumentContextOptions GetDocumentContextOptions()
{
return this.ApiDocumentContextOptions;
}
#endregion
#region Methods
private static DocumentContextOptions<DocumentContext> CreateDocumentContextOptions(
IServiceModel apiServiceModel)
{
var apiOptions = new DocumentContextOptions<DocumentContext>();
var apiOptionsBuilder = new DocumentContextOptionsBuilder(apiOptions);
apiOptionsBuilder.UseServiceModel(apiServiceModel);
// Use apiOptionsBuilder to do higher level things here if needed...
return apiOptions;
}
#endregion
} internal class ApiServiceModelFactory(IEnumerable<IComplexTypeBuilder> apiComplexTypeBuilders,
IEnumerable<IResourceTypeBuilder> apiResourceTypeBuilders)
: IApiServiceModelFactory
{
#region Properties
private IEnumerable<IComplexTypeBuilder> ApiComplexTypeBuilders { get; } = apiComplexTypeBuilders;
private IEnumerable<IResourceTypeBuilder> ApiResourceTypeBuilders { get; } = apiResourceTypeBuilders;
#endregion
#region IApiServiceModelFactory Implementation
public IServiceModel CreateServiceModel()
{
var apiServiceModelBuilder = new ServiceModelBuilder();
// Add complex/resource type configurations to the service model builder.
AddComplexTypeConfigurations(apiServiceModelBuilder);
AddResourceTypeConfigurations(apiServiceModelBuilder);
// Create service model based on conventions and configurations.
var apiConventions = CreateConventions();
var apiServiceModel = apiServiceModelBuilder.Create(apiConventions);
return apiServiceModel;
}
#endregion
#region Implementation Methods
private void AddComplexTypeConfigurations(IServiceModelBuilder apiServiceModelBuilder)
{
foreach (var apiComplexTypeBuilder in this.ApiComplexTypeBuilders)
{
apiServiceModelBuilder.Configurations.Add(apiComplexTypeBuilder);
}
}
private void AddResourceTypeConfigurations(IServiceModelBuilder apiServiceModelBuilder)
{
foreach (var apiResourceTypeBuilder in this.ApiResourceTypeBuilders)
{
apiServiceModelBuilder.Configurations.Add(apiResourceTypeBuilder);
}
}
private static IConventions CreateConventions()
{
var apiConventionsBuilder = new ConventionsBuilder();
// Use JSON API standard member naming convention for JSON API resource attributes.
// For example, FirstName in POCO becomes "firstName" as a JSON API attribute.
apiConventionsBuilder.ApiAttributeNamingConventions()
.AddCamelCaseNamingConvention();
// Use JSON API standard member naming and singular conventions of the POCO type
// name as the JSON API type name.
// For example, Article POCO type becomes "article" as the JSON API type.
apiConventionsBuilder.ApiTypeNamingConventions()
.AddSingularNamingConvention()
.AddCamelCaseNamingConvention();
// Discover all public properties as JSON API resource attributes.
// For example, FirstName property in POCO becomes an attribute of a JSON API resource.
apiConventionsBuilder.ResourceTypeConventions()
.AddPropertyDiscoveryConvention();
apiConventionsBuilder.ComplexTypeConventions()
.AddPropertyDiscoveryConvention();
var conventions = apiConventionsBuilder.Create();
return conventions;
}
#endregion
} From here I use DI, to create singletons for Now in application code where you need to build public static DocumentContext CreateJsonApiDocumentContext(IJsonApiFrameworkRegistry jsonApiFrameworkRegistry)
{
var jsonApiDocumentContextOptions = jsonApiFrameworkRegistry.GetDocumentContextOptions();
var jsonApiDocumentContext = new DocumentContext(jsonApiDocumentContextOptions);
return jsonApiDocumentContext;
} So this is roughly what I am currently doing, this could also be improved, maybe... Use this as you see fit but note I left some parts out to keep it as simple as possible... Good luck... |
@scott-mcdonald, thank you very much for this detailed answer! I appreciate it. I'll follow the tips you mentioned, and it should help. Thanks! |
Add caching of service models as they are static. Before building a new service model by user configuration and conventions, check if the service model is in cache and use it if it is. Otherwise build service model and cache it for future cache hits.
The text was updated successfully, but these errors were encountered: