diff --git a/Source/Kernel/Concepts/Jobs/IJobTypes.cs b/Source/Kernel/Concepts/Jobs/IJobTypes.cs index 1d2b44bf0..2b2c18240 100644 --- a/Source/Kernel/Concepts/Jobs/IJobTypes.cs +++ b/Source/Kernel/Concepts/Jobs/IJobTypes.cs @@ -55,4 +55,11 @@ enum GetForError /// The . /// or . Result GetRequestClrTypeFor(JobType type); + + /// + /// Gets the job request associated with the . + /// + /// The job request clr type. + /// or . + Result GetJobClrTypeFromRequestClrType(Type jobRequestClrType); } diff --git a/Source/Kernel/Grains/Jobs/JobTypes.cs b/Source/Kernel/Grains/Jobs/JobTypes.cs index bd62ae88f..29db45b3e 100644 --- a/Source/Kernel/Grains/Jobs/JobTypes.cs +++ b/Source/Kernel/Grains/Jobs/JobTypes.cs @@ -17,6 +17,7 @@ public class JobTypes : IJobTypes readonly Dictionary _jobTypes = []; readonly Dictionary _jobTypePerType = []; readonly Dictionary _jobRequestTypes = []; + readonly Dictionary _jobRequestTypeToJobType = []; /// /// Initializes an instance of the class. @@ -56,6 +57,14 @@ public class JobTypes : IJobTypes : IJobTypes.GetRequestClrTypeForError.CouldNotFindType; } + /// + public Result GetJobClrTypeFromRequestClrType(Type jobRequestClrType) + { + return _jobRequestTypeToJobType.TryGetValue(jobRequestClrType, out var jobClrType) + ? jobClrType + : IJobTypes.GetRequestClrTypeForError.CouldNotFindType; + } + void InitializeMap(ITypes types) { PopulateJobTypes(types); @@ -64,7 +73,9 @@ void InitializeMap(ITypes types) void PopulateJobTypes(ITypes types) { - foreach (var jobClrType in types.FindMultiple().Where(type => type is { IsClass: true, IsAbstract: false } && type != typeof(NullJob))) + foreach (var jobClrType in types.FindMultiple() + .Where(type => type is { IsClass: true, IsAbstract: false, IsInterface: false, IsGenericType: false } + && type != typeof(NullJob) && type.Assembly.FullName != typeof(IJob).Assembly.FullName)) { var jobTypeAttribute = jobClrType.GetCustomAttribute(); var jobType = jobTypeAttribute?.JobType ?? jobClrType; @@ -95,7 +106,9 @@ void PopulateJobRequestTypes() throw new JobTypeMustHaveARequestType(jobType, jobClrType); default: // First generic argument of IJob is the type of the request - _jobRequestTypes.Add(jobType, jobInterfaces[0].GetGenericArguments()[0]); + var requestType = jobInterfaces[0].GetGenericArguments()[0]; + _jobRequestTypes.Add(jobType, requestType); + _jobRequestTypeToJobType.Add(requestType, jobClrType); break; } } diff --git a/Source/Kernel/Storage.MongoDB/Jobs/JobStateSerializer.cs b/Source/Kernel/Storage.MongoDB/Jobs/JobStateSerializer.cs index 3a5199890..e6dba98c0 100644 --- a/Source/Kernel/Storage.MongoDB/Jobs/JobStateSerializer.cs +++ b/Source/Kernel/Storage.MongoDB/Jobs/JobStateSerializer.cs @@ -1,9 +1,11 @@ // Copyright (c) Cratis. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Cratis.Applications.MongoDB; using Cratis.Chronicle.Concepts.Jobs; using Cratis.Chronicle.Storage.Jobs; using Cratis.Strings; +using Microsoft.Extensions.DependencyInjection; using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; @@ -15,21 +17,67 @@ namespace Cratis.Chronicle.Storage.MongoDB.Jobs; /// The . public class JobStateSerializer(IJobTypes jobTypes) : SerializerBase { + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, JobState value) + { + var requestType = value.Request.GetType(); + var requestSerializer = BsonSerializer.SerializerRegistry.GetSerializer(requestType); + } /// public override JobState Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { + var jobRequestElementName = nameof(JobState.Request).ToCamelCase(); var rawBsonDocument = context.Reader.ReadRawBsonDocument(); using var rawDocument = new RawBsonDocument(rawBsonDocument); + rawDocument.Remove(jobRequestElementName); var bsonDocument = rawDocument.ToBsonDocument(); var jobTypeString = bsonDocument.GetValue(nameof(JobState.Type).ToCamelCase()).AsString; var jobRequestType = jobTypes.GetRequestClrTypeFor(new(jobTypeString)).AsT0; - var jobRequestElementName = nameof(JobState.Request).ToCamelCase(); var request = (IJobRequest)BsonSerializer.Deserialize( bsonDocument.GetElement(jobRequestElementName).ToBsonDocument(), jobRequestType); - bsonDocument.Remove(jobRequestElementName); + // bsonDocument.Remove(jobRequestElementName); var jobState = BsonSerializer.Deserialize(bsonDocument); jobState.Request = request; return jobState; } -} \ No newline at end of file +} + +public class JobRequestSerializer(IJobTypes jobTypes) : SerializerBase + where TJobRequest : IJobRequest +{ + /// + public override TJobRequest Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + var rawBsonDocument = context.Reader.ReadRawBsonDocument(); + using var rawDocument = new RawBsonDocument(rawBsonDocument); + var bsonDocument = rawDocument.ToBsonDocument(); + return BsonSerializer.Deserialize(bsonDocument); + } +} + +/// +/// Represents a for concepts. +/// +public class JobRequestSerializationProvider(IServiceProvider serviceProvider) : IBsonSerializationProvider +{ + /// + /// Creates an instance of a serializer of the concept of the given type param T. + /// + /// The Concept type. + /// for the specific type. + public static JobRequestSerializer CreateSerializer(IServiceProvider serviceProvider) + where T : class, IJobRequest + => ActivatorUtilities.CreateInstance>(serviceProvider); + + /// + public IBsonSerializer GetSerializer(Type type) + { + if (type.IsAssignableTo(typeof(IJobRequest))) + { + var createSerializerMethod = GetType().GetMethod(nameof(CreateSerializer))!.MakeGenericMethod(type); + return (createSerializerMethod.Invoke(null, [serviceProvider]) as IBsonSerializer)!; + } + + return null!; + } +}