Skip to content

Commit

Permalink
feature(caller): Extends ConfigHttpRequestMessage to support the same…
Browse files Browse the repository at this point in the history
… lifecycle as CallerBase (#203)

* feat(Caller): CallerBase supports the current ServiceProvider life cycle

* test(Caller): Perfect unit tests

* Refactor: Optimize Factory

* style: Format code

* fix(Caller): Support empty services in CallerBase constructor

* fix(Caller): fix Code Smells

* test(Caller): Perfect unit tests

* style: format code

* test(Caller): Perfect unit tests

* doc(Caller): Modify the Readme

* chore: remove the blank line
  • Loading branch information
zhenlei520 authored Aug 16, 2022
1 parent 8fe2d38 commit 657b3a4
Show file tree
Hide file tree
Showing 29 changed files with 365 additions and 167 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) MASA Stack All rights reserved.
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.

namespace Masa.BuildingBlocks.Data;

public abstract class AbstractMasaFactory<TService, TRelationOptions> : IMasaFactory<TService>
where TService : class
where TRelationOptions : MasaRelationOptions<TService>
{
protected abstract string DefaultServiceNotFoundMessage { get; }
protected abstract string SpecifyServiceNotFoundMessage { get; }
protected abstract MasaFactoryOptions<TRelationOptions> FactoryOptions { get; }

protected readonly IServiceProvider ServiceProvider;

protected AbstractMasaFactory(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}

private static MasaRelationOptions<TService>? GetDefaultOptions(List<TRelationOptions> optionsList)
{
return optionsList.SingleOrDefault(c => c.Name == Options.DefaultName) ??
optionsList.FirstOrDefault();
}

public virtual TService Create()
{
var defaultOptions = GetDefaultOptions(FactoryOptions.Options);
if (defaultOptions == null)
throw new NotImplementedException(DefaultServiceNotFoundMessage);

return defaultOptions.Func.Invoke(ServiceProvider);
}

public virtual TService Create(string name)
{
var options = FactoryOptions.Options.SingleOrDefault(c => c.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
if (options == null)
throw new NotImplementedException(string.Format(SpecifyServiceNotFoundMessage, name));

return options.Func.Invoke(ServiceProvider);
}
}
11 changes: 11 additions & 0 deletions src/BuildingBlocks/Data/Masa.BuildingBlocks.Data/IMasaFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) MASA Stack All rights reserved.
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.

namespace Masa.BuildingBlocks.Data;

public interface IMasaFactory<out TService> where TService : class
{
TService Create();

TService Create(string name);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,35 @@

namespace Masa.BuildingBlocks.Data;

public class DefaultIdGeneratorFactory : IIdGeneratorFactory
public class DefaultIdGeneratorFactory : AbstractMasaFactory<IIdGenerator, IdGeneratorRelationOptions>,
IIdGeneratorFactory
{
private readonly IServiceProvider _serviceProvider;
private readonly IOptions<IdGeneratorFactoryOptions> _idGeneratorFactoryOptions;
private readonly IdGeneratorRelationOptions? _defaultIdGeneratorOptions;
private IGuidGenerator? _guidGenerator;
private ISequentialGuidGenerator? _sequentialGuidGenerator;
private ISnowflakeGenerator? _snowflakeGenerator;

public IGuidGenerator GuidGenerator => _guidGenerator ??=
_serviceProvider.GetService<IGuidGenerator>() ?? throw new Exception($"Unsupported {nameof(GuidGenerator)}");
ServiceProvider.GetService<IGuidGenerator>() ?? throw new Exception($"Unsupported {nameof(GuidGenerator)}");

public ISequentialGuidGenerator SequentialGuidGenerator => _sequentialGuidGenerator ??=
_serviceProvider.GetService<ISequentialGuidGenerator>() ?? throw new Exception($"Unsupported {nameof(SequentialGuidGenerator)}");
ServiceProvider.GetService<ISequentialGuidGenerator>() ?? throw new Exception($"Unsupported {nameof(SequentialGuidGenerator)}");

public ISnowflakeGenerator SnowflakeGenerator => _snowflakeGenerator ??=
_serviceProvider.GetService<ISnowflakeGenerator>() ?? throw new Exception($"Unsupported {nameof(SnowflakeGenerator)}");
ServiceProvider.GetService<ISnowflakeGenerator>() ?? throw new Exception($"Unsupported {nameof(SnowflakeGenerator)}");

public DefaultIdGeneratorFactory(IServiceProvider serviceProvider)
protected override string DefaultServiceNotFoundMessage { get; } =
"No default IdGenerator found, you may need service.AddSimpleGuidGenerator()";

protected override string SpecifyServiceNotFoundMessage { get; } =
"Please make sure you have used [{0}] IdGenerator, it was not found";

protected override MasaFactoryOptions<IdGeneratorRelationOptions> FactoryOptions => _optionsMonitor.CurrentValue;

private readonly IOptionsMonitor<IdGeneratorFactoryOptions> _optionsMonitor;

public DefaultIdGeneratorFactory(IServiceProvider serviceProvider) : base(serviceProvider)
{
_serviceProvider = serviceProvider;
_idGeneratorFactoryOptions = serviceProvider.GetRequiredService<IOptions<IdGeneratorFactoryOptions>>();
_defaultIdGeneratorOptions =
_idGeneratorFactoryOptions.Value.Options.FirstOrDefault(generator
=> generator.Name == Microsoft.Extensions.Options.Options.DefaultName) ??
_idGeneratorFactoryOptions.Value.Options.FirstOrDefault();
_optionsMonitor = serviceProvider.GetRequiredService<IOptionsMonitor<IdGeneratorFactoryOptions>>();
}

public IIdGenerator<TOut> Create<TOut>() where TOut : notnull
Expand All @@ -42,21 +45,4 @@ public IIdGenerator<TOut> Create<TOut>(string name) where TOut : notnull
var idGenerator = Create(name);
return idGenerator as IIdGenerator<TOut> ?? throw new Exception($"Unsupported {nameof(IIdGenerator<TOut>)}");
}

public IIdGenerator Create()
{
if (_defaultIdGeneratorOptions == null)
throw new NotImplementedException("No default IdGenerator found, you may need service.AddSimpleGuidGenerator()");

return _defaultIdGeneratorOptions.Func.Invoke(_serviceProvider);
}

public IIdGenerator Create(string name)
{
var idGeneratorOptions = _idGeneratorFactoryOptions.Value.Options.FirstOrDefault(generator => generator.Name == name);
if (idGeneratorOptions == null)
throw new NotImplementedException($"No IdGenerator found for name {name}");

return idGeneratorOptions.Func.Invoke(_serviceProvider);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,18 @@

namespace Masa.BuildingBlocks.Data;

public class DefaultDeserializerFactory : IDeserializerFactory
public class DefaultDeserializerFactory : AbstractMasaFactory<IDeserializer, DeserializerRelationOptions>,
IDeserializerFactory
{
private readonly IServiceProvider _serviceProvider;
private readonly IOptions<DeserializerFactoryOptions> _deserializerFactoryOptions;
private readonly DeserializerRelationOptions? _defaultDeserializerOptions;
protected override string DefaultServiceNotFoundMessage => "Default deserializer not found, you need to add it, like services.AddJson()";

public DefaultDeserializerFactory(IServiceProvider serviceProvider, IOptions<DeserializerFactoryOptions> deserializerFactoryOptions)
{
_serviceProvider = serviceProvider;
_deserializerFactoryOptions = deserializerFactoryOptions;
_defaultDeserializerOptions = deserializerFactoryOptions.Value.Options.FirstOrDefault(options
=> options.Name == Options.DefaultName) ??
deserializerFactoryOptions.Value.Options.FirstOrDefault();
}

public IDeserializer Create()
{
if (_defaultDeserializerOptions == null)
throw new NotImplementedException("Default deserializer not found, you need to add it, like services.AddJson()");
protected override string SpecifyServiceNotFoundMessage => "Please make sure you have used [{0}] deserializer, it was not found";
protected override MasaFactoryOptions<DeserializerRelationOptions> FactoryOptions => _optionsMonitor.CurrentValue;

return _defaultDeserializerOptions.Func.Invoke(_serviceProvider);
}
private readonly IOptionsMonitor<DeserializerFactoryOptions> _optionsMonitor;

public IDeserializer Create(string name)
public DefaultDeserializerFactory(IServiceProvider serviceProvider) : base(serviceProvider)
{
var deserializerOptions =
_deserializerFactoryOptions.Value.Options.FirstOrDefault(options
=> options.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
if (deserializerOptions == null)
throw new NotImplementedException($"No deserializer found for 【{name}");

return deserializerOptions.Func.Invoke(_serviceProvider);
_optionsMonitor = serviceProvider.GetRequiredService<IOptionsMonitor<DeserializerFactoryOptions>>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,18 @@

namespace Masa.BuildingBlocks.Data;

public class DefaultSerializerFactory : ISerializerFactory
public class DefaultSerializerFactory : AbstractMasaFactory<ISerializer, SerializerRelationOptions>,
ISerializerFactory
{
private readonly IServiceProvider _serviceProvider;
private readonly IOptions<SerializerFactoryOptions> _serializerFactoryOptions;
private readonly SerializerRelationOptions? _defaultSerializerOptions;
protected override string DefaultServiceNotFoundMessage => "Default serializer not found, you need to add it, like services.AddJson()";

public DefaultSerializerFactory(IServiceProvider serviceProvider, IOptions<SerializerFactoryOptions> serializerFactoryOptions)
{
_serviceProvider = serviceProvider;
_serializerFactoryOptions = serializerFactoryOptions;
_defaultSerializerOptions = serializerFactoryOptions.Value.Options.FirstOrDefault(options
=> options.Name == Options.DefaultName) ??
serializerFactoryOptions.Value.Options.FirstOrDefault();
}

public ISerializer Create()
{
if (_defaultSerializerOptions == null)
throw new NotImplementedException("Default serializer not found, you need to add it, like services.AddJson()");
protected override string SpecifyServiceNotFoundMessage => "Please make sure you have used [{0}] serializer, it was not found";
protected override MasaFactoryOptions<SerializerRelationOptions> FactoryOptions => _optionsMonitor.CurrentValue;

return _defaultSerializerOptions.Func.Invoke(_serviceProvider);
}
private readonly IOptionsMonitor<SerializerFactoryOptions> _optionsMonitor;

public ISerializer Create(string name)
public DefaultSerializerFactory(IServiceProvider serviceProvider) : base(serviceProvider)
{
var serializerOptions =
_serializerFactoryOptions.Value.Options.FirstOrDefault(options
=> options.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
if (serializerOptions == null)
throw new NotImplementedException($"No serializer found for 【{name}");

return serializerOptions.Func.Invoke(_serviceProvider);
_optionsMonitor = serviceProvider.GetRequiredService<IOptionsMonitor<SerializerFactoryOptions>>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,20 @@

namespace Masa.Contrib.Data;

public class DefaultTypeConvertFactory : ITypeConvertFactory
public class DefaultTypeConvertFactory : AbstractMasaFactory<ITypeConvertProvider, TypeConvertRelationOptions>,
ITypeConvertFactory
{
private readonly IServiceProvider _serviceProvider;
private readonly IOptions<TypeConvertFactoryOptions> _typeConvertFactoryOptions;
private readonly TypeConvertRelationOptions? _defaultOptions;
protected override string DefaultServiceNotFoundMessage
=> "Default typeConvert not found, you need to add it, like services.AddTypeConvert()";

public DefaultTypeConvertFactory(IOptions<TypeConvertFactoryOptions> typeConvertFactoryOptions, IServiceProvider serviceProvider)
{
_typeConvertFactoryOptions = typeConvertFactoryOptions;
_defaultOptions = _typeConvertFactoryOptions.Value.Options.FirstOrDefault(options
=> options.Name == Options.DefaultName) ??
_typeConvertFactoryOptions.Value.Options.FirstOrDefault();
_serviceProvider = serviceProvider;
}
protected override string SpecifyServiceNotFoundMessage => "Please make sure you have used [{0}] typeConvert, it was not found";

public ITypeConvertProvider Create()
{
if (_defaultOptions == null)
throw new NotImplementedException("Default typeConvert not found, you need to add it");
protected override MasaFactoryOptions<TypeConvertRelationOptions> FactoryOptions => _optionsMonitor.CurrentValue;

return _defaultOptions.Func.Invoke(_serviceProvider);
}
private readonly IOptionsMonitor<TypeConvertFactoryOptions> _optionsMonitor;

public ITypeConvertProvider Create(string name)
public DefaultTypeConvertFactory(IServiceProvider serviceProvider) : base(serviceProvider)
{
var typeConvertOptions =
_typeConvertFactoryOptions.Value.Options.FirstOrDefault(options
=> options.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
if (typeConvertOptions == null)
throw new NotImplementedException($"No TypeConvert found for 【{name}");

return typeConvertOptions.Func.Invoke(_serviceProvider);
_optionsMonitor = serviceProvider.GetRequiredService<IOptionsMonitor<TypeConvertFactoryOptions>>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,25 @@ namespace Masa.BuildingBlocks.Service.Caller;
public abstract class AbstractCaller : ICaller
{
private readonly ITypeConvertor _typeConvertor;
public readonly IServiceProvider ServiceProvider;
protected readonly IServiceProvider ServiceProvider;

private IRequestMessage? _requestMessage;
private IResponseMessage? _responseMessage;
protected IRequestMessage RequestMessage => _requestMessage ??= ServiceProvider.GetRequiredService<IRequestMessage>();
protected IResponseMessage ResponseMessage => _responseMessage ??= ServiceProvider.GetRequiredService<IResponseMessage>();
protected Action<HttpRequestMessage>? RequestMessageAction;

protected AbstractCaller(IServiceProvider serviceProvider)
{
_typeConvertor = serviceProvider.GetRequiredService<ITypeConvertor>();
ServiceProvider = serviceProvider;
}

public virtual void ConfigRequestMessage(Action<HttpRequestMessage> action)
{
RequestMessageAction = action;
}

public virtual async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
bool autoThrowUserFriendlyException = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,25 @@ public abstract class CallerBase

private ICaller? _caller;

protected ICaller Caller => _caller ??= ServiceProvider.GetRequiredService<ICallerFactory>().Create(Name!);
protected ICaller Caller
{
get
{
if (_caller == null)
{
_caller = ServiceProvider!.GetRequiredService<ICallerFactory>().Create(Name!);
_caller.ConfigRequestMessage(ConfigHttpRequestMessage);
}
return _caller;
}
}

[Obsolete("CallerProvider has expired, please use Caller")]
protected ICaller CallerProvider => Caller;

private IServiceProvider ServiceProvider { get; }
public IServiceProvider? ServiceProvider { get; private set; }

protected CallerBase() => ServiceProvider = null;

protected CallerBase(IServiceProvider serviceProvider) => ServiceProvider = serviceProvider;

Expand All @@ -27,4 +40,14 @@ public void SetCallerOptions(CallerOptions options, string name)
CallerOptions = options;
Name ??= name;
}

public void SetServiceProvider(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}

protected virtual void ConfigHttpRequestMessage(HttpRequestMessage requestMessage)
{

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace Masa.BuildingBlocks.Service.Caller;

public interface ICaller
{
void ConfigRequestMessage(Action<HttpRequestMessage> action);

Task<TResponse?> SendAsync<TResponse>(
HttpRequestMessage request,
CancellationToken cancellationToken = default);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public static DefaultDaprClientBuilder UseDapr(this CallerOptions callerOptions,
builder.Configure?.Invoke(daprClientBuilder);
});

callerOptions.Services.AddOptions();
AddCallerExtensions.AddCaller(callerOptions, name,
serviceProvider => new DaprCaller(serviceProvider, name, builder.AppId));
return new DefaultDaprClientBuilder(callerOptions.Services, name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public override async Task<HttpRequestMessage> CreateRequestAsync(HttpMethod met
var httpRequestMessage =
await RequestMessage.ProcessHttpRequestMessageAsync(DaprClient.CreateInvokeMethodRequest(method, AppId, methodName));

RequestMessageAction?.Invoke(httpRequestMessage);

DealRequestMessage(Action);

return httpRequestMessage;
Expand All @@ -45,6 +47,8 @@ public override async Task<HttpRequestMessage> CreateRequestAsync<TRequest>(Http
var httpRequestMessage =
await RequestMessage.ProcessHttpRequestMessageAsync(DaprClient.CreateInvokeMethodRequest(method, AppId, methodName), data);

RequestMessageAction?.Invoke(httpRequestMessage);

DealRequestMessage(Action);

return httpRequestMessage;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ public abstract class DaprCallerBase : CallerBase

public virtual Action<DaprClientBuilder>? Configure { get; set; } = null;

protected DaprCallerBase()
{
}

protected DaprCallerBase(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
Expand Down
Loading

0 comments on commit 657b3a4

Please sign in to comment.