/// <summary> /// Initializes a new instance of the <see cref="SerializerConfigurationValidator"/> class. /// </summary> /// <param name="codecProvider"> /// The codec provider. /// </param> /// <param name="options"> /// The type manifest options. /// </param> /// <param name="serviceProvider"> /// The service provider. /// </param> public SerializerConfigurationValidator(ICodecProvider codecProvider, IOptions <TypeManifestOptions> options, IServiceProvider serviceProvider) { _codecProvider = codecProvider; _options = options.Value; var configEnabled = _options.EnableConfigurationAnalysis; if (configEnabled.HasValue) { _enabled = configEnabled.Value; } else { // Enable in development envioronment by default. var environment = serviceProvider.GetService <IHostEnvironment>(); _enabled = environment is not null && environment.IsDevelopment(); } }
/// <summary> /// Analyzes grain interface methods to find parameter types and return types which are not serializable. /// </summary> /// <param name="codecProvider"> /// The codec provider. /// </param> /// <param name="options"> /// The type manifest options. /// </param> /// <returns> /// A collection of types which have serializability issues. /// </returns> public static Dictionary <Type, SerializerConfigurationComplaint> AnalyzeSerializerAvailability(ICodecProvider codecProvider, TypeManifestOptions options) { var allComplaints = new Dictionary <Type, SerializerConfigurationComplaint>(); foreach (var @interface in options.Interfaces) { foreach (var method in @interface.GetMethods()) { if (typeof(Task).IsAssignableFrom(method.ReturnType)) { if (method.ReturnType.IsConstructedGenericType && typeof(Task <>).IsAssignableFrom(method.ReturnType.GetGenericTypeDefinition())) { VisitType(method.ReturnType.GetGenericArguments()[0], method); } } if (method.ReturnType.IsConstructedGenericType && typeof(ValueTask <>).IsAssignableFrom(method.ReturnType.GetGenericTypeDefinition())) { VisitType(method.ReturnType.GetGenericArguments()[0], method); } foreach (var param in method.GetParameters()) { VisitType(param.ParameterType, method); } } } return(allComplaints); void VisitType(Type type, MethodInfo methodInfo) { if (!IsEligibleType(type)) { return; } var hasCodec = codecProvider.TryGetCodec(type) is not null; var hasCopier = codecProvider.TryGetDeepCopier(type) is not null; if (!hasCodec || !hasCopier) { if (!allComplaints.TryGetValue(type, out var complaint)) { complaint = allComplaints[type] = new() { HasSerializer = hasCodec, HasCopier = hasCopier, }; } if (!complaint.Methods.TryGetValue(methodInfo.DeclaringType, out var methodList)) { methodList = complaint.Methods[methodInfo.DeclaringType] = new HashSet <MethodInfo>(); } methodList.Add(methodInfo); } } bool IsEligibleType(Type type) { if (type.IsGenericTypeParameter || type.IsGenericMethodParameter || type.ContainsGenericParameters) { return(false); } return(true); } }