ChangeToken.OnChange not fire on custom configuration provider - c#

I'll try to create custom config provider that will take keys from database. As written in manuals, I created this provider and it's work fine. All keys are loaded on start and all works fine.
But now I'm trying to use IOptionsSnapshot and reload keys from db as they change. But nothing happens.
Can anyone tell me what's going wrong? Here is my code:
public class EFConfigProvider : ConfigurationProvider
{
private DateTime lastLoaded;
public EFConfigProvider(Action<DbContextOptionsBuilder> optionsAction)
{
OptionsAction = optionsAction;
lastLoaded = DateTime.Now;
ChangeToken.OnChange(
() => Watch(),
() => {
Thread.Sleep(250);
this.Load();
});
}
public new IChangeToken GetReloadToken()
{
return Watch();
}
Action<DbContextOptionsBuilder> OptionsAction { get; }
// Load config data from EF DB.
public override void Load()
{
this.Data.Clear();
var builder = new DbContextOptionsBuilder<ConfigContext>();
OptionsAction(builder);
using (var dbContext = new ConfigContext(builder.Options))
{
// Save Load Fact
dbContext.SaveLoadFact();
// Load Partners Settings
GetPartners(dbContext);
}
}
private IChangeToken Watch()
{
return new DatabaseChangeToken();
}
}
public class DatabaseChangeToken : IChangeToken
{
public bool HasChanged
{
get
{
return true;
}
}
public bool ActiveChangeCallbacks => false;
public IDisposable RegisterChangeCallback(Action<object> callback, object state) => EmptyDisposable.Instance;
internal class EmptyDisposable : IDisposable
{
public static EmptyDisposable Instance { get; } = new EmptyDisposable();
private EmptyDisposable() { }
public void Dispose() { }
}
}
What I did to start to worked it:
I add in class EFConfigProvider variable
private ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken();
I add in constructor
// Start Periodic task to refresh the DB
PeriodicTask.Run(() =>
{
//Refresh();
OnReload();
}, TimeSpan.FromSeconds(reload));
I Add class for periodTask
public class PeriodicTask
{
public static async Task Run(Action action, TimeSpan period, CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(period, cancellationToken);
if (!cancellationToken.IsCancellationRequested)
action();
}
}
public static Task Run(Action action, TimeSpan period)
{
return Run(action, period, CancellationToken.None);
}
}
Add method on Reload
protected new void OnReload()
{
var previousToken = Interlocked.Exchange(ref _reloadToken, new ConfigurationReloadToken());
previousToken.OnReload();
}
Add change of using change token
ChangeToken.OnChange(
() => { return this._reloadToken; },
() => {
Thread.Sleep(250);
this.Load();
});

Updated: I found that overriding OnReload() broke the notification of changes (I was using OptionsMonitor, which didn't see the changes). So I changed my implementation to not do that. It's calling the default OnReload() which handles the notification of changes to OptionsMonitor.
Your updated implementation really helped me out, so thank you!
But rather than using your custom PeriodicTask, you could just use a Timer. The effect is just the same though.
Here is my implementation, which reloads the data every 5 minutes:
using System;
using System.Linq;
using System.Threading;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Primitives;
using Timer = System.Timers.Timer;
namespace MyProject.Classes.Configuration {
public class MyConfigProvider : ConfigurationProvider {
private readonly DbContextOptions<MyDbContext> _dbOptions;
private readonly Timer _reloadTimer = new Timer();
private ConfigurationReloadToken _reloadToken = new ConfigurationReloadToken();
public MyConfigProvider(Action<DbContextOptionsBuilder> dbOptionsAction) {
var builder = new DbContextOptionsBuilder<MyDbContext>();
dbOptionsAction(builder);
_dbOptions = builder.Options;
_reloadTimer.AutoReset = false;
_reloadTimer.Interval = TimeSpan.FromMinutes(5).TotalMilliseconds;
_reloadTimer.Elapsed += (s, e) => { Load(); };
}
public override void Load() {
try {
using (var db = new MyDbContext(_dbOptions)) {
var settings = db.Settings.AsNoTracking().ToList();
Data.Clear();
foreach (var s in settings) {
Data.Add(s.Name, s.Value);
}
}
OnReload();
} finally {
_reloadTimer.Start();
}
}
}
}

I am having same requirement and I reached to you code and I tried the same thing and not working.
But your code and some other investigation lead me to the hint.
public class CustomConfigurationProvider : ConfigurationProvider
{
private readonly string applicationName;
private readonly bool reloadOnChange;
private readonly IConfiguration configuration;
public CustomConfigurationProvider(string applicationName, bool reloadOnChange)
{
this.applicationName = applicationName;
this.reloadOnChange = reloadOnChange;
if(reloadOnChange)
{
ChangeToken.OnChange(
() => GetReloadToken(), // listener to token change
() =>
{
Thread.Sleep(250);
this.Load();
});
}
}
public override async void Load()
{
Data.Clear();
Data = read data from database;
if (Condition to check if data in database changed)
{
OnReload(); // This will create new token and trigger change so what is register in OnChange above will be called again which is this.Load()
}
}
}
I also refereed https://www.mikesdotnetting.com/article/301/loading-asp-net-core-mvc-views-from-a-database-or-other-location
and
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/primitives/change-tokens
Hope this helps.

Related

How to Schedule in the Initial state of MassTransit saga

I created a state machine saga that will receive multiple messages and only after a given time period elapses, I want it to continue its work. I figured the only way to do it with mass transit is to go with the scheduling capabilities of the framework.
The saga code (shortened for brevity) is given below:
public class CheckFeedSubmissionStateMachine : MassTransitStateMachine<CheckFeedSubmissionState>
{
public State? WaitingForTimeoutExpiration { get; private set; }
public State? FetchingSubmissionData { get; private set; }
public Event<CheckFeedSubmissionCommand> CheckFeedSubmissionCommandReceived { get; private set; }
public Event<FeedSubmissionListReceivedEvent> FeedSubmissionListReceived { get; private set; }
public Event<FeedSubmissionListErrorReceivedEvent> FeedSubmissionListErrorReceived { get; private set; }
public Event<FeedSubmissionResultReceivedEvent> FeedSubmissionResultReceived { get; private set; }
public Event<FeedSubmissionResultErrorReceivedEvent> FeedSubmissionResultErrorReceived { get; private set; }
public Schedule<CheckFeedSubmissionState, SchedulingCompletionTimeoutExpired> ScheduleCompletionTimeout { get; private set; }
private readonly int _scheduleDelay;
public CheckFeedSubmissionStateMachine(IOptions<SagasOptions> options)
{
_scheduleDelay = int.Parse(options.Value.CheckFeedSubmissionStateMachine["ScheduleDelay"]);
Configure();
BuildProcess();
}
private void Configure()
{
Event(
() => CheckFeedSubmissionCommandReceived,
e => e.CorrelateById(x => x.Message.PartnerGuid));
Schedule(() => ScheduleCompletionTimeout, instance => instance.SchedulingCompletionTimeoutTokenId, s =>
{
s.Delay = TimeSpan.FromSeconds(_scheduleDelay);
s.Received = r => r.CorrelateById(context => context.Message.CorrelationId);
});
InstanceState(state => state.CurrentState);
}
private void BuildProcess()
{
Initially(
When(CheckFeedSubmissionCommandReceived)
.Then(InitializeState)
.Then(StoreSubmissionIds)
.Schedule(ScheduleCompletionTimeout, ScheduleEvent)
.TransitionTo(WaitingForTimeoutExpiration));
During(WaitingForTimeoutExpiration,
When(CheckFeedSubmissionCommandReceived)
.Then(StoreSubmissionIds),
When(ScheduleCompletionTimeout.Received)
.Activity(QueueGetFeedSubmissionListRequest)
.TransitionTo(FetchingSubmissionData));
// the rest ommited for brevity
}
private void InitializeState(BehaviorContext<CheckFeedSubmissionState, CheckFeedSubmissionCommand> ctx) =>
ctx.Instance.PartnerId = ctx.Data.PartnerId;
private void StoreSubmissionIds(BehaviorContext<CheckFeedSubmissionState, CheckFeedSubmissionCommand> ctx)
{
ctx.Instance.SubmissionIdToStatusMap[ctx.Data.FeedSubmissionId] = FeedProcessingStatus.Submitted;
ctx.Instance.SubmissionIdsToCorrelationIdsMap[ctx.Data.FeedSubmissionId] = ctx.Data.CorrelationId;
}
private Task<SchedulingCompletionTimeoutExpired> ScheduleEvent<TEvent>(
ConsumeEventContext<CheckFeedSubmissionState, TEvent> ctx) where TEvent : class =>
ctx.Init<SchedulingCompletionTimeoutExpired>(new { ctx.Instance.CorrelationId });
private EventActivityBinder<CheckFeedSubmissionState, SchedulingCompletionTimeoutExpired> QueueGetFeedSubmissionListRequest(
IStateMachineActivitySelector<CheckFeedSubmissionState, SchedulingCompletionTimeoutExpired> sel) =>
sel.OfType<QueueGetFeedSubmissionListActivity>();
}
The one test that I created for it aims at checking if both published messages have been preserved in the saga, the code below:
[Fact]
public async Task GivenCheckFeedSubmissionCommand_WhenAnotherCheckFeedSubmissionCommandIsReceived_ThenTheSagaStoresBothSubmissionIds()
{
var (harness, sagaHarness) = GetTestComponents();
var partnerGuid = Guid.NewGuid();
await harness.Start();
try
{
await harness.Bus.Publish(GetInitiatingEvent("1", partnerGuid));
await Consumption<CheckFeedSubmissionCommand>(harness, sagaHarness, 1);
await harness.Bus.Publish(GetInitiatingEvent("2", partnerGuid));
await Consumption<CheckFeedSubmissionCommand>(harness, sagaHarness, 2);
var state = sagaHarness.Sagas.Contains(partnerGuid);
state.CurrentState.Should().Be("WaitingForTimeoutExpiration");
state.SubmissionIdsToCorrelationIdsMap.Should().ContainKeys("1", "2");
}
finally
{
await harness.Stop();
}
}
private static (InMemoryTestHarness, IStateMachineSagaTestHarness<CheckFeedSubmissionState, CheckFeedSubmissionStateMachine>) GetTestComponents() =>
TestHarnessFactory.Create<CheckFeedSubmissionState, CheckFeedSubmissionStateMachine>(
sp => sp
.AddSingleton(Options.Create(new SagasOptions
{
CheckFeedSubmissionStateMachine = new Dictionary<string, string>
{
["ScheduleDelay"] = "0"
}
})));
private static CheckFeedSubmissionCommand GetInitiatingEvent(string feedSubmissionId, Guid partnerGuid) =>
new(Guid.NewGuid(), "1", partnerGuid, feedSubmissionId);
private static async Task Consumption<TEvent>(
InMemoryTestHarness harness,
IStateMachineSagaTestHarness<CheckFeedSubmissionState, CheckFeedSubmissionStateMachine> sagaHarness,
int expectedCount)
where TEvent : class
{
if (expectedCount == 1)
{
var harnessConsumed = harness.Consumed.SelectAsync<TEvent>().Any();
var sagaConsumed = sagaHarness.Consumed.SelectAsync<TEvent>().Any();
await Task.WhenAll(harnessConsumed, sagaConsumed);
}
else
{
int harnessConsumedCount;
int sagaConsumedCount;
do
{
var harnessConsumedTask = harness.Consumed.SelectAsync<TEvent>().Count();
var sagaConsumedTask = sagaHarness.Consumed.SelectAsync<TEvent>().Count();
harnessConsumedCount = await harnessConsumedTask;
sagaConsumedCount = await sagaConsumedTask;
await Task.Delay(1000);
} while (harnessConsumedCount < expectedCount && sagaConsumedCount < expectedCount);
}
}
The problem is that when I invoke this line .Schedule(ScheduleCompletionTimeout, ScheduleEvent) in the Initially/When phase, it somehow interferes with state switching and the saga does not switch to the next state - it stays in the Initial state indefinitely. I confirmed it both by inspecting the state variable in the test and by setting a breakpoint in the InitializeState method - it gets hit twice. When I remove that line doing the scheduling, the test passes, though I can't do that, because I need it. Any help?
It's likely you don't have a scheduler configured for the bus with the test harness. If you had logging enabled for the test, you'd see the error in the logs.
The bus configuration for the test harness should let you add the scheduler:
configurator.UseDelayedMessageScheduler();
There is a configuration event on the test harness, OnConfigureInMemoryBus or something like that, which you can use to configure the bus.

How do I pass dependent classes to extension methods?

I have a requirement to connect to the server and collect data for processing. Below is my core class, which is responsible for looping through all the servers and try connecting them for processing.
public class CoreDataProcessingEngine : ICoreDataProcessingEngine
{
private readonly COMLib.ServerGateway _aServerGw;
private COMLib.ServerErrorInfo _aServerErrorInfo;
Public CoreDataProcessingEngine()
{
_aServerGw = new COMLib.ServerGateway();
_aServerErrorInfo = new COMLib.ServerErrorInfo();
}
//When service starts, I am collecting all the server details from config and trying to connect ONE BY ONE.
public async Task Start()
{
List<Server> servers = ConfigurationManager.GetSection("servers") as List<Server>;
foreach (var serverdetails in servers)
{
var data = Task.Run(() => ConnectToServer(serverdetails ));
}
}
}
Here is my ConnectToServer method
private async void ConnectToGateway(ServerDetails serverdetails )
{
await _aServerGw.connectToServerByName(serverdetails.serveraddress);
}
I have extended the connectToServerByName method as follow , which is in separate static class.
public static class ComLibraryExtensions
{
public static Task connectToServerByName(this ProxyGW #this, string serveraddress)
{
var tcs = new TaskCompletionSource<object>();
Action onSuccess = null;
Action<int> onFailed = null;
onSuccess = () =>
{
#this.onConnectSucceeded -= HandleManager_OnConnectSucceeded;
#this.onConnectFailed -= HandleManager_OnConnectFailed;
tcs.TrySetResult(null);
};
onFailed = hr =>
{
#this.onConnectSucceeded -= HandleManager_OnConnectSucceeded;
#this.onConnectFailed -= HandleManager_OnConnectFailed;
tcs.TrySetException(Marshal.GetExceptionForHR(hr));
};
#this.onConnectSucceeded += HandleManager_OnConnectSucceeded;
#this.onConnectFailed += HandleManager_OnConnectFailed;
#this.connectToGatewayByNameEx(serveraddress);
return tcs.Task;
}
private static void HandleManager_OnConnectFailed(int hr)
{
//How do I get access to dependent objects here?
//Like ILogger ??
_logger.Information(hr);
}
private static void HandleManager_OnConnectSucceeded()
{
//How do I get access #this??
#this.enableNotifications(true);//fails , it says #this does not exists
}
}
Question is:
How do I get access to _aServerGw in HandleManager_OnConnectSucceeded event, because I want to set some property based on the success event.
How do I get access to dependent objects here in extension classes like ILogger?

How to implement a state machine with Automatonymous in C#

I am trying to implement a simple example/demo for a state machine using Automatonymous with RabbitMQ. Unfortunately I could not find one to rebuild / learn from (I found the ShoppingWeb, but in my eyes it's anything but simple). Also in my opinion the documentation is lacking information.
This is the state machine example I thought of (sorry, it's pretty ugly):
Please note that this example is completely made up and it's not important if it makes sense or not. This project's purpose is to get "warm" with Automatonymous.
What I want to do / to have is:
Four applications running:
The state machine itself
The "requester" sending requests to be interpreted
The "validator" or "parser" checking if the provided request is valid
The "interpreter" interpreting the given request
An example of this could be:
Requester sends "x=5"
Validator checks if a "=" is contained
Intepreter says "5"
My implementation of the state machine looks like this:
public class InterpreterStateMachine : MassTransitStateMachine<InterpreterInstance>
{
public InterpreterStateMachine()
{
InstanceState(x => x.CurrentState);
Event(() => Requesting, x => x.CorrelateBy(request => request.Request.RequestString, context => context.Message.Request.RequestString)
.SelectId(context => Guid.NewGuid()));
Event(() => Validating, x => x.CorrelateBy(request => request.Request.RequestString, context => context.Message.Request.RequestString));
Event(() => Interpreting, x => x.CorrelateBy(request => request.Request.RequestString, context => context.Message.Request.RequestString));
Initially(
When(Requesting)
.Then(context =>
{
context.Instance.Request = new Request(context.Data.Request.RequestString);
})
.ThenAsync(context => Console.Out.WriteLineAsync($"Request received: {context.Data.Request.RequestString}"))
.Publish(context => new ValidationNeededEvent(context.Instance))
.TransitionTo(Requested)
);
During(Requested,
When(Validating)
.Then(context =>
{
context.Instance.Request.IsValid = context.Data.Request.IsValid;
if (!context.Data.Request.IsValid)
{
this.TransitionToState(context.Instance, Error);
}
else
{
this.TransitionToState(context.Instance, RequestValid);
}
})
.ThenAsync(context => Console.Out.WriteLineAsync($"Request '{context.Data.Request.RequestString}' validated with {context.Instance.Request.IsValid}"))
.Publish(context => new InterpretationNeededEvent(context.Instance))
,
Ignore(Requesting),
Ignore(Interpreting)
);
During(RequestValid,
When(Interpreting)
.Then((context) =>
{
//do something
})
.ThenAsync(context => Console.Out.WriteLineAsync($"Request '{context.Data.Request.RequestString}' interpreted with {context.Data.Answer}"))
.Publish(context => new AnswerReadyEvent(context.Instance))
.TransitionTo(AnswerReady)
.Finalize(),
Ignore(Requesting),
Ignore(Validating)
);
SetCompletedWhenFinalized();
}
public State Requested { get; private set; }
public State RequestValid { get; private set; }
public State AnswerReady { get; private set; }
public State Error { get; private set; }
//Someone is sending a request to interprete
public Event<IRequesting> Requesting { get; private set; }
//Request is validated
public Event<IValidating> Validating { get; private set; }
//Request is interpreted
public Event<IInterpreting> Interpreting { get; private set; }
class ValidationNeededEvent : IValidationNeeded
{
readonly InterpreterInstance _instance;
public ValidationNeededEvent(InterpreterInstance instance)
{
_instance = instance;
}
public Guid RequestId => _instance.CorrelationId;
public Request Request => _instance.Request;
}
class InterpretationNeededEvent : IInterpretationNeeded
{
readonly InterpreterInstance _instance;
public InterpretationNeededEvent(InterpreterInstance instance)
{
_instance = instance;
}
public Guid RequestId => _instance.CorrelationId;
}
class AnswerReadyEvent : IAnswerReady
{
readonly InterpreterInstance _instance;
public AnswerReadyEvent(InterpreterInstance instance)
{
_instance = instance;
}
public Guid RequestId => _instance.CorrelationId;
}
}
Then I have services like this:
public class RequestService : ServiceControl
{
readonly IScheduler scheduler;
IBusControl busControl;
BusHandle busHandle;
InterpreterStateMachine machine;
InMemorySagaRepository<InterpreterInstance> repository;
public RequestService()
{
scheduler = CreateScheduler();
}
public bool Start(HostControl hostControl)
{
Console.WriteLine("Creating bus...");
machine = new InterpreterStateMachine();
repository = new InMemorySagaRepository<InterpreterInstance>();
busControl = Bus.Factory.CreateUsingRabbitMq(x =>
{
IRabbitMqHost host = x.Host(new Uri(/*rabbitMQ server*/), h =>
{
/*credentials*/
});
x.UseInMemoryScheduler();
x.ReceiveEndpoint(host, "interpreting_answer", e =>
{
e.PrefetchCount = 5; //?
e.StateMachineSaga(machine, repository);
});
x.ReceiveEndpoint(host, "2", e =>
{
e.PrefetchCount = 1;
x.UseMessageScheduler(e.InputAddress);
//Scheduling !?
e.Consumer(() => new ScheduleMessageConsumer(scheduler));
e.Consumer(() => new CancelScheduledMessageConsumer(scheduler));
});
});
Console.WriteLine("Starting bus...");
try
{
busHandle = MassTransit.Util.TaskUtil.Await<BusHandle>(() => busControl.StartAsync());
scheduler.JobFactory = new MassTransitJobFactory(busControl);
scheduler.Start();
}
catch (Exception)
{
scheduler.Shutdown();
throw;
}
return true;
}
public bool Stop(HostControl hostControl)
{
Console.WriteLine("Stopping bus...");
scheduler.Standby();
if (busHandle != null) busHandle.Stop();
scheduler.Shutdown();
return true;
}
static IScheduler CreateScheduler()
{
ISchedulerFactory schedulerFactory = new StdSchedulerFactory();
IScheduler scheduler = MassTransit.Util.TaskUtil.Await<IScheduler>(() => schedulerFactory.GetScheduler()); ;
return scheduler;
}
}
My questions are:
How do I send the "intial" request, so that the state machine will transition to my initial state
How do I "react" within the consumers to check the data that were sent and then send new data like in 1?
Okay I figured it out. I probably had problems because I'm not only new to Masstransit/Automatonymous and RabbitMQ, but also don't have much experience with C# yet.
So if anyone ever will have the same problem, here is what you need:
Given the above example there are three different types plus some small interfaces needed:
A sender (in this case the "requester") including a specific consumer
A service that consumes specific message types (the "validator" and "interpreter")
A service that holds the state machine without a specific consumer
Some "contracts", which are interfaces defining the type of message that's sent/consumed
1) This is the sender:
using InterpreterStateMachine.Contracts;
using MassTransit;
using System;
using System.Threading.Tasks;
namespace InterpreterStateMachine.Requester
{
class Program
{
private static IBusControl _busControl;
static void Main(string[] args)
{
var busControl = ConfigureBus();
busControl.Start();
Console.WriteLine("Enter request or quit to exit: ");
while (true)
{
Console.Write("> ");
String value = Console.ReadLine();
if ("quit".Equals(value,StringComparison.OrdinalIgnoreCase))
break;
if (value != null)
{
String[] values = value.Split(';');
foreach (String v in values)
{
busControl.Publish<IRequesting>(new
{
Request = new Request(v),
TimeStamp = DateTime.UtcNow
});
}
}
}
busControl.Stop();
}
static IBusControl ConfigureBus()
{
if (null == _busControl)
{
_busControl = Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var host = cfg.Host(new Uri(/*rabbitMQ server*/), h =>
{
/*credentials*/
});
cfg.ReceiveEndpoint(host, "answer_ready", e =>
{
e.Durable = true;
//here the consumer is registered
e.Consumer<AnswerConsumer>();
});
});
_busControl.Start();
}
return _busControl;
}
//here comes the actual logic of the consumer, which consumes a "contract"
class AnswerConsumer : IConsumer<IAnswerReady>
{
public async Task Consume(ConsumeContext<IAnswerReady> context)
{
await Console.Out.WriteLineAsync($"\nReceived Answer for \"{context.Message.Request.RequestString}\": {context.Message.Answer}.");
await Console.Out.WriteAsync(">");
}
}
}
}
2) This is the service (here it is the validation sercive)
using InterpreterStateMachine.Contracts;
using MassTransit;
using MassTransit.QuartzIntegration;
using MassTransit.RabbitMqTransport;
using Quartz;
using Quartz.Impl;
using System;
using System.Threading.Tasks;
using Topshelf;
namespace InterpreterStateMachine.Validator
{
public class ValidationService : ServiceControl
{
readonly IScheduler _scheduler;
static IBusControl _busControl;
BusHandle _busHandle;
public static IBus Bus => _busControl;
public ValidationService()
{
_scheduler = CreateScheduler();
}
public bool Start(HostControl hostControl)
{
Console.WriteLine("Creating bus...");
_busControl = MassTransit.Bus.Factory.CreateUsingRabbitMq(x =>
{
IRabbitMqHost host = x.Host(new Uri(/*rabbitMQ server*/), h =>
{
/*credentials*/
});
x.UseInMemoryScheduler();
x.UseMessageScheduler(new Uri(RabbitMqServerAddress));
x.ReceiveEndpoint(host, "validation_needed", e =>
{
e.PrefetchCount = 1;
e.Durable = true;
//again this is how the consumer is registered
e.Consumer<RequestConsumer>();
});
});
Console.WriteLine("Starting bus...");
try
{
_busHandle = MassTransit.Util.TaskUtil.Await<BusHandle>(() => _busControl.StartAsync());
_scheduler.JobFactory = new MassTransitJobFactory(_busControl);
_scheduler.Start();
}
catch (Exception)
{
_scheduler.Shutdown();
throw;
}
return true;
}
public bool Stop(HostControl hostControl)
{
Console.WriteLine("Stopping bus...");
_scheduler.Standby();
_busHandle?.Stop();
_scheduler.Shutdown();
return true;
}
static IScheduler CreateScheduler()
{
ISchedulerFactory schedulerFactory = new StdSchedulerFactory();
IScheduler scheduler = MassTransit.Util.TaskUtil.Await<IScheduler>(() => schedulerFactory.GetScheduler());
return scheduler;
}
}
//again here comes the actual consumer logic, look how the message is re-published after it was checked
class RequestConsumer : IConsumer<IValidationNeeded>
{
public async Task Consume(ConsumeContext<IValidationNeeded> context)
{
await Console.Out.WriteLineAsync($"(c) Received {context.Message.Request.RequestString} for validation (Id: {context.Message.RequestId}).");
context.Message.Request.IsValid = context.Message.Request.RequestString.Contains("=");
//send the new message on the "old" context
await context.Publish<IValidating>(new
{
Request = context.Message.Request,
IsValid = context.Message.Request.IsValid,
TimeStamp = DateTime.UtcNow,
RequestId = context.Message.RequestId
});
}
}
}
The validator consumes the contract "IValidationNeeded" and then publishes the contract "IValidating", which then will be consumed by the state machine itself (the "Validating" event).
3) The difference between a consumer service and the sate machine service lies withing the "ReceiveEndpoint". Here is no consumer registered, but the state machine is set:
...
InterpreterStateMachine _machine = new InterpreterStateMachine();
InMemorySagaRepository<InterpreterInstance> _repository = new InMemorySagaRepository<InterpreterInstance>();
...
x.ReceiveEndpoint(host, "state_machine", e =>
{
e.PrefetchCount = 1;
//here the state machine is set
e.StateMachineSaga(_machine, _repository);
e.Durable = false;
});
4) Last but not least, the contracts are pretty small and look like this:
using System;
namespace InterpreterStateMachine.Contracts
{
public interface IValidationNeeded
{
Guid RequestId { get; }
Request Request { get; }
}
}
So overall it's pretty straightforward, I just had to use my brain :D
I hope this will help someone.

WCF: How to cache collections from OData in client

Is there a possibility to cache a collection, retrieved using WCF from an OData service.
The situation is the following:
I generated a WCF service client with Visual Studio 2015 using the metadata of the odata service. VS generated a class inheriting from System.Data.Services.Client.DataServiceContext. This class has some properties of type System.Data.Services.Client.DataServiceQuery<T>. The data of some of these properties change seldom. Because of performance reasons I want the WCF client to load these properties just the first time and not every time I use it in the code.
Is there a built in possibility to cache the data of these properties? Or can I tell the service client not to load specific proeprties newly every time.
Assuming the service client class is ODataClient and one of its properties is `Area, for now I get the values in the following way:
var client = new ODataClient("url_to_the_service");
client.IgnoreMissingProperties = true;
var propertyInfo = client.GetType().GetProperty("Area");
var area = propertyInfo.GetValue(client) as IEnumerable<object>;
The reason why I do this in such a complicated way is, that the client should be very generic: The properties to be handled can be configured in a configuration file.
* EDIT *
I already tried to find properties in the System.Data.Services.Client.DataServiceContext class or the System.Data.Services.Client.DataServiceQuery<T> class for the caching. But i wasn't able to find any.
To my knowledge there is no "out of the box" caching concept on the client. There are options for caching the output of a request on the server which is something you might want consider as well. Googling "WCF Caching" would get you a bunch of info on this.
Regarding client side caching...#Evk is correct it is pretty straight forward. Here is an sample using MemoryCache.
using System;
using System.Runtime.Caching;
namespace Services.Util
{
public class CacheWrapper : ICacheWrapper
{
ObjectCache _cache = MemoryCache.Default;
public void ClearCache()
{
MemoryCache.Default.Dispose();
_cache = MemoryCache.Default;
}
public T GetFromCache<T>(string key, Func<T> missedCacheCall)
{
return GetFromCache<T>(key, missedCacheCall, TimeSpan.FromMinutes(5));
}
public T GetFromCache<T>(string key, Func<T> missedCacheCall, TimeSpan timeToLive)
{
var result = _cache.Get(key);
if (result == null)
{
result = missedCacheCall();
if (result != null)
{
_cache.Set(key, result, new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.Add(timeToLive) });
}
}
return (T)result;
}
public void InvalidateCache(string key)
{
_cache.Remove(key);
}
}
}
This is an example of code that uses the cache...
private class DataAccessTestStub
{
public const string DateTimeTicksCacheKey = "GetDateTimeTicks";
ICacheWrapper _cache;
public DataAccessTestStub(ICacheWrapper cache)
{
_cache = cache;
}
public string GetDateTimeTicks()
{
return _cache.GetFromCache(DateTimeTicksCacheKey, () =>
{
var result = DateTime.Now.Ticks.ToString();
Thread.Sleep(100); // Create some delay
return result;
});
}
public string GetDateTimeTicks(TimeSpan timeToLive)
{
return _cache.GetFromCache(DateTimeTicksCacheKey, () =>
{
var result = DateTime.Now.Ticks.ToString();
Thread.Sleep(500); // Create some delay
return result;
}, timeToLive);
}
public void ClearDateTimeTicks()
{
_cache.InvalidateCache(DateTimeTicksCacheKey);
}
public void ClearCache()
{
_cache.ClearCache();
}
}
And some tests if you fancy...
[TestClass]
public class CacheWrapperTest
{
private DataAccessTestStub _dataAccessTestClass;
[TestInitialize]
public void Init()
{
_dataAccessTestClass = new DataAccessTestStub(new CacheWrapper());
}
[TestMethod]
public void GetFromCache_ShouldExecuteCacheMissCall()
{
var original = _dataAccessTestClass.GetDateTimeTicks();
Assert.IsNotNull(original);
}
[TestMethod]
public void GetFromCache_ShouldReturnCachedVersion()
{
var copy1 = _dataAccessTestClass.GetDateTimeTicks();
var copy2 = _dataAccessTestClass.GetDateTimeTicks();
Assert.AreEqual(copy1, copy2);
}
[TestMethod]
public void GetFromCache_ShouldRespectTimeToLive()
{
_dataAccessTestClass.ClearDateTimeTicks();
var copy1 = _dataAccessTestClass.GetDateTimeTicks(TimeSpan.FromSeconds(2));
var copy2 = _dataAccessTestClass.GetDateTimeTicks();
Assert.AreEqual(copy1, copy2);
Thread.Sleep(3000);
var copy3 = _dataAccessTestClass.GetDateTimeTicks();
Assert.AreNotEqual(copy1, copy3);
}
[TestMethod]
public void InvalidateCache_ShouldClearCachedVersion()
{
var original = _dataAccessTestClass.GetDateTimeTicks();
_dataAccessTestClass.ClearDateTimeTicks();
var updatedVersion = _dataAccessTestClass.GetDateTimeTicks();
Assert.AreNotEqual(original, updatedVersion);
}
}

How can I improve and/or modularize my handling of event based tasks?

So I have a server and I'm making calls to it through a wrapped up WebSocket (WebSocket4Net) and one of the requirements of the library I'm building is the ability to await on the return of the request. So I have a class MessageEventHandler that contains events that are triggered by the class MessageHandler as messages come in.
MessageEventHandler ex.
public class MessageEventHandler : IMessageEventHandler
{
public delegate void NodeNameReceived(string name);
public event Interfaces.NodeNameReceived OnNodeNameReceived;
public void NodeNameReceive(string name)
{
if (this.OnNodeNameReceived != null)
{
this.OnNodeNameReceived(name);
}
}
}
MessageHandler ex.
public class MessageHandler : IMessageHandler
{
private IMessageEventHandler eventHandler;
public MessageHandler(IMessageEventHandler eventHandler)
{
this.eventHandler = eventHandler;
}
public void ProcessDataCollectorMessage(string message)
{
var serviceMessage = JsonConvert.DeserializeObject<ServiceMessage>(message);
switch (message.MessageType)
{
case MessageType.GetNodeName:
{
var nodeName = serviceMessage.Data as string;
if (nodeName != null)
{
this.eventHandler.NodeNameReceive(nodeName);
}
break;
}
default:
{
throw new NotImplementedException();
}
}
}
Now building upon those classes I have the class containing my asynchronous function that handles the call to get the node name.
public class ClientServiceInterface : IClientServiceInterface
{
public delegate void RequestReady(ServiceMessage serviceMessage);
public event Interfaces.RequestReady OnRequestReady;
public int ResponseTimeout { get; private set; }
private IMessageEventHandler messageEventHandler;
public ClientServiceInterface(IMessageEventHandler messageEventHandler, int responseTimeout = 5000)
{
this.messageEventHandler = messageEventHandler;
this.ResponseTimeout = responseTimeout;
}
public Task<string> GetNodeNameAsync()
{
var taskCompletionSource = new TaskCompletionSource<string>();
var setHandler = default(NodeNameReceived);
setHandler = name =>
{
taskCompletionSource.SetResult(name);
this.messageEventHandler.OnNodeNameReceived -= setHandler;
};
this.messageEventHandler.OnNodeNameReceived += setHandler;
var ct = new CancellationTokenSource(this.ResponseTimeout);
var registration = new CancellationTokenRegistration();
registration = ct.Token.Register(
() =>
{
taskCompletionSource.TrySetCanceled();
this.messageEventHandler.OnNodeNameReceived -= setHandler;
registration.Dispose();
},
false);
var serviceMessage = new ServiceMessage() { Type = MessageType.GetNodeName };
this.ReadyMessage(serviceMessage);
return taskCompletionSource.Task;
}
}
As you can see I wouldn't call it pretty and I apologize if anyone threw up a little reading it. But this is my first attempt at wrapping a Task with Asynchronous Event. So with that on the table I could use some help.
Is there a better way to accomplish what I'm trying to achieve here? Remembering that I want a user of the library to either subscribe to the event and listen for all callbacks OR they can simply await the return depending on
their needs.
var nodeName = await GetNodeNameAsync();
Console.WriteLine(nodeName);
or
messageEventHandler.OnNodeNameReceived += (name) => Console.WriteLine(name);
GetNodeNameAsync();
Alternatively if my approach is actually 'good' can anyone provide any advice as to how I can write a helper function to abstract out setting up each function in this way? Any help would be greatly appreciated.
So I've written a couple classes to solve the problem I was having. The first of which is my CallbackHandle class which contains the task inside the TaskCompletionSource so each time that a request is made in my example a new callback handle is created.
public class CallbackHandle<T>
{
public CallbackHandle(int timeout)
{
this.TaskCompletionSource = new TaskCompletionSource<T>();
var cts = new CancellationTokenSource(timeout);
cts.Token.Register(
() =>
{
if (this.Cancelled != null)
{
this.Cancelled();
}
});
this.CancellationToken = cts;
}
public event Action Cancelled;
public CancellationTokenSource CancellationToken { get; private set; }
public TaskCompletionSource<T> TaskCompletionSource { get; private set; }
}
Then I have a 'handler' that manages the handles and their creation.
public class CallbackHandler<T>
{
private readonly IList<CallbackHandle<T>> callbackHandles;
private readonly object locker = new object();
public CallbackHandler()
{
this.callbackHandles = new List<CallbackHandle<T>>();
}
public CallbackHandle<T> AddCallback(int timeout)
{
var callback = new CallbackHandle<T>(timeout);
callback.Cancelled += () =>
{
this.callbackHandles.Remove(callback);
callback.TaskCompletionSource.TrySetResult("Error");
};
lock (this.locker)
{
this.callbackHandles.Add(callback);
}
return callback;
}
public void EventTriggered(T eventArgs)
{
lock (this.locker)
{
if (this.callbackHandles.Count > 0)
{
CallbackHandle<T> callback =
this.callbackHandles.First();
if (callback != null)
{
this.callbackHandles.Remove(callback);
callback.TaskCompletionSource.SetResult(eventArgs);
}
}
}
}
}
This is a simplified version of my actual implementation but it should get someone started if they need something similar. So to use this on my ClientServiceInterface class in my example I would start by creating a class level handler and using it like this:
public class ClientServiceInterface : IClientServiceInterface
{
private readonly CallbackHandler<string> getNodeNameHandler;
public ClientServiceInterface(IMessageEventHandler messageEventHandler, int responseTimeout = 5000)
{
this.messageEventHandler = messageEventHandler;
this.ResponseTimeout = responseTimeout;
this.getNodeNameHandler = new
CallbackHandler<string>();
this.messageEventHandler.OnNodeNameReceived += args => this.getNodeNameHandler.EventTriggered(args);
}
public Task<string> GetNodeNameAsync()
{
CallbackHandle<string> callbackHandle = this.getNodeNameHandler.AddCallback(this.ResponseTimeout);
var serviceMessage = new ServiceMessage
{
Type = MessageType.GetNodeName.ToString()
};
this.ReadyMessage(serviceMessage);
return callbackHandle.TaskCompletionSource.Task;
}
// Rest of class declaration removed for brevity
}
Which is much better looking than what I had before (at least in my opinion) and it's easy to extend.
For starters follow a thread-safe pattern:
public void NodeNameReceive(string name)
{
var evt = this.OnNodeNameReceived;
if (evt != null)
{
evt (name);
}
}
If you do not take a reference to the event object it can be set to null between the time you check null and call the method.

Categories

Resources