我使用service remoting将服务A与服务B连接起来。在部署时,我注意到服务A可以在服务B中调用方法,而服务B仍然是启动。在这种情况下,AddStringToDictionary
会引发NullReferenceException
,因为尚未调用RunAsync
。
public interface IServiceB : IService
{
Task AddStringToDictionary(string key, string value);
}
internal sealed class ServiceB : StatefulService, IServiceB
{
private IReliableDictionary<string, string> myDictionary;
public ServiceB(StatefulServiceContext context)
: base(context)
{
// StateMananger is null here.
}
public async Task AddStringToDictionary(string key, string value)
{
using (var tx = StateManager.CreateTransaction())
{
// myDictionary is null if this is called before RunAsync.
await this.myDictionary.SetAsync(tx, key, value);
await tx.CommitAsync();
}
}
protected override async Task RunAsync(CancellationToken cancellationToken)
{
this.myDictionary = await this.StateManager.GetOrAddAsync<IReliableDictionary<string, string>>("myDictionary");
}
}
我知道我可以在StateManager.GetOrAddAsync
中使用AddStringToDictionary
方法,但我可能想要初始化更多字段。构造函数也没用,因为StateManager
还没有存在。我目前的解决方案是添加一个字段:
private bool serviceAvailable = false;
我在true
结束时将此字段设置为RunAsync
。然后,每种公共方法都会调用ThrowIfServiceUnavailable()
。
private void ThrowIfServiceUnavailable()
{
if (!serviceAvailable)
{
throw new ServiceUnavailableException();
}
}
public async Task AddStringToDictionary(string key, string value)
{
ThrowIfServiceUnavailable();
...
}
目前,我每次使用远程调用方法时都必须处理ServiceUnavailableException
。我的问题是:有没有更好的方法来处理这种情况或这是一个很好的解决方案?是否可以将自定义异常添加到ServiceProxy
的暂时异常中以重试?
答案 0 :(得分:0)
Here's an implementation that blocks calls while the service is being initialized. It uses ManualResetEventSlim
to block.
And it uses SemaphoreSlim
to enforce single threaded access to an async operation.
All Service operations call await WaitForInitializeAsync(CancellationToken.None);
And the implementation of this method is:
private async Task WaitForInitializeAsync(CancellationToken cancellationToken)
{
if (_initializer.IsSet) return;
await Task.Run(() => InitializeAsync(cancellationToken), cancellationToken);
_initializer.Wait(cancellationToken);
}
The implementation of InitializeAsync:
private async Task InitializeAsync(CancellationToken cancellationToken)
{
if (_initializer.IsSet) return;
try
{
_semaphore.Wait(cancellationToken);
if (_initializer.IsSet) return;
[initializer logic here]