I have a class Foo with an event that publishes a FooState enum. I want to turn this event into an observable that replays the last value for new subscribers.
Even if there are no subscribers, any new subscriber should get the last value.
public enum FooState
{
Stopped = 0,
Starting = 1,
Running = 2,
}
public delegate void FooEventHandler(object sender, FooEventArgs e);
public class FooEventArgs : EventArgs
{
public FooEventArgs(FooState fooState)
{
this.State = fooState;
}
public FooState State {get; private set;}
}
public class Foo
{
public event FooEventHandler FooEvent;
public void OnFooEvent(FooState state)
{
var fooEvent = FooEvent;
if(fooEvent != null)
{
fooEvent(this, new FooEventArgs(state));
}
}
}
My attempts so far revolved around using Publish, RefCount and Replay. But none of the combinations I tried work if I subscribe to the observable after I fire the event.
Replay(1).RefCount() works as long there is already at least one subscription but I need to work for the first late subscription as well.
var foo = new Foo();
var obs = Observable.FromEventPattern<FooEventHandler, FooEventArgs>(
h => foo.FooEvent += h,
h => foo.FooEvent -= h)
.DistinctUntilChanged()
.Replay(1)
.RefCount();
// Works if this line is uncomented.
//obs.Subscribe(x => Console.WriteLine("Early Subscriber = " + x.EventArgs.State));
foo.OnFooEvent(FooState.Running);
obs.Subscribe(x => Console.WriteLine("Late Subscriber = " + x.EventArgs.State));
Does anyone know how to do this with Rx?
RefCount connects only after the first subscription. If you want to have fine grained control of when the connection occurs you should use Replay + Connect.
So do instead:
var publishedSource = eventSource.DistinctUntilChanged().Replay(1);
var connection = publishedSource.Connect();
//Subscribe to publishedSource to receive events and dispose of
connection when you are done.
Posted from my phone so apologies for any syntax errors in advance.
Rx is doing the right thing converting your event notifications to your stream and replaying them, but what you are asking is:
"Why when I subscribe to the event, don't I get the initial state".
Events don't work like that. If I do a += on foo.FooEvent, I don't get an immediate trigger with the current value. I only get notified when it changes.
As you have noticed, 'Replay' will replay subsequent events, but not provide the state at the time of subscription.
To solve your problem, you'll need to ensure that the current value is put onto the stream before you hook up the stream for change notifications.
Check out Observable.StartWith().
i.e. Do ".StartWith(foo.State)" before the.DistinctUntilChanged() call (immediately after the .FromEventPattern).
Related
I'm quite new to C# and certainly OOP concepts.. so forgive the stupidity of my question.
I have a system I wish to communicate with, It has a number of commands that can be called with an associated response. (Communication is done via TCP/IP or Serial) (I implemented an Interface with SendMessage so that I can use multiple transport mechanisms)
I want to create a method for each command and then expose these, which is simple enough. The device also lets say 'broadcasts' messages as well which I want to act on, so I was using an event handler for this which works well..
At the moment in the event handler I catch OK and ERROR style messages, but ideally I would like to also be able to send the command from the above method and catch an error and return a bool value based on the command.
Can anyone think of a way I can do something like this and point me in the right direction?
Thanks
David
You can use helper to wait for event. Some ugly code from past:
public class ComWait
{
ManualResetEvent _waitEvent;
SomeEvent _eventHandler;
public ComWait()
{
_waitEvent = new ManualResetEvent(false);
_eventHandler = new SomeEvent(Watch);
}
void Watch()
{
_waitEvent.Set();
}
public bool Wait(int time = 3000)
{
_waitEvent.Reset();
SomeEvent += _eventHandler;
bool result = _waitEvent.WaitOne(time, false);
SomeEvent -= _eventHandler;
return result;
}
}
Usage is
ComWait wait = new ComWait();
if(!wait.Wait())
return; // timeout
// process
It will simply block synchronous method until event is rised or timeout occurs. It should be easy to add parameters: to unblock on specific event and to pass event handler parameters back to caller.
Otherwise I would simply have method inside communication class to use as a blocker:
readonly object _waitLock = new object();
public void Wait()
{
lock (_waitLock)
if (!Monitor.Wait(_waitLock, 3000))
throw new TimeoutException("No communications");
}
Signal at same time as you rise event:
lock (_waitLock)
Monitor.PulseAll(_waitLock);
I'm just starting to force myself to use Entity Framework (I know I'm a long way behind), but I've fallen at the first hurdle.
The program I'm writing simply watches a PLC for a bit to become true. When it does become true an event fires and then values (random at the moment) will be entered into SQL. I then set the bit to false.
The problem I'm having is that the event fires once, the entries get added, and bit gets set false. However the event never triggers again. If I comment out all of the SQL bits then and just set the bit false then it works perfectly and fires multiple times.
Here is most of the code that I've got.
Any help would be really appreciated.
static void Main(string[] args)
{
PlcListener plcListener = new PlcListener();
plcLister.BitChanged += (bitVal) => On_BitChanged(bitVal, plcListener)
plcListner.Start();
}
private static void On_BitChanged(bool bitVal, PlcListener plcListner)
{
SqlEntities sqlEntity = new SqlEntities();
SampleData sampleData = new SampleData(){ Data = new Random().Next(); };
sqlEntity.AddToSampleDatas(sampleData);
sqlEntity.SaveChanges();
plcListener.Confirm();
}
public class PlcListener
{
public void Start()
{
OPCServer opcServer = new OPCServer();
opcServer.DataChanged += On_DataChanged;
}
public void Confirm()
{
//Code to set bit to false
}
public void On_DataChanged(bool bitVal.......)
{
if(bitVal)
{
BitChangedEventHandler handler = BitChanged;
if (handler != null)
{
handler(bitVal);
}
}
}
public delegate void BitChangedEventHandler(bool bitValue);
public event BitChangedEventHandler BitChanged;
}
You did not mention which OpcServer component you are using, but the most probable problem is that the DataChange comes into your application in a COM callback thread and you are not allowed to write back to COM while handling the callback. You need to just record the incoming value and do the actual handling in your own handler thread, which then sets the bit back to false in the PLC.
Consider the situation in which you want to subscribe to an event for one and only one notification. Once the first notification lands, you unsubscribe from all future events. Would the following pattern present any memory issues? It works, but I wasn't sure if the self-referencing closure could keeps things around in memory longer than desired.
public class Entity
{
public event EventHandler NotifyEvent;
}
// And then, elsewhere, for a listen-once handler, we might do this:
Entity entity = new Entity();
Action<object, EventArgs> listener = null;
listener = (sender, args) =>
{
// do something interesting
// unsubscribe, so we only get 1 event notification
entity.NotifyEvent -= new EventHandler(listener);
};
entity.NotifyEvent += new EventHandler(listener);
Note that you have to declare 'listener' and assign a value (null). Otherwise the compiler complains about 'Use of unassigned local variable listener'
There is nothing wrong with this pattern. It's the very same pattern I and many others use for assigning and removing a lambda expression to an event handler.
While I think the general pattern is fine, I wouldn't go through Action<object, EventArgs>. I'd use:
EventHandler listener = null;
listener = (sender, args) =>
{
// do something interesting
// unsubscribe, so we only get 1 event notification
entity.NotifyEvent -= listener;
};
entity.NotifyEvent += listener;
i have the following issue:
In asynchronous context i need to initialize fields of some custom object before i can proceed with other operations on it, so i do:
class ContainingObject
{
private CustomObject _co;
SomeMethod()
{
_co = new CustomObject();
_co.InitObjectAsyncCompleted += (s,e) => DoStuff();
_co.InitObjectAsync();
}
}
class CustomObject
{
public string Field1, Field2, Field3, Field4;
public EventHandler InitObjectAsyncCompleted;
public void InitObjectAsync()
{
}
}
The catch is that fields are also initialized through asynchronous calls to WCF service, and all must be initialized before i raise the InitObjectAsyncCompleted event.
There is quite a number of those fields, each is initialized with different WCF call, and implying i cannot change the WCF part for now, i see two ways to solve the problem:
1) Chain WCF calls, so first call initializes first field, then calls WCF to initialize second field, and so on before all fields are initialized, then i raise "completed" event in last WCF call.
public void InitObjectAsync()
{
var proxy = new ProxyFactory.GetCustomObjectProxy;
proxy.GetDataForField1Completed += (s,e) =>
{
Field1 = e.Result;
proxy.GetDataForField2Completed += (s1,e1) =>
{
Field2 = e1.Result;
//keep this up building a chain of events, when Field4 is filled, raise
// InitObjectAsyncCompleted(this, null);
};
proxy.GetDataForField2();
};
proxy.GetDataForField1();
}
2) Since i know how many method calls should be completed, 4 in this case, i can make a counter.
public void InitObjectAsync()
{
int counter = 0;
var proxy = new ProxyFactory.GetCustomObjectProxy;
proxy.GetDataForField1Completed += (s,e) =>
{
Field1 = e.Result;
if(counter >= 3)
InitObjectAsyncCompleted(this, null);
else
counter++;
};
proxy.GetDataForField1();
proxy.GetDataForField2Completed += (s,e) =>
{
Field2 = e.Result;
if(counter >= 3)
InitObjectAsyncCompleted(this, null);
else
counter++;
};
proxy.GetDataForField2();
//repeat for all fields
}
I don't really like either of solutions, first one builds a pretty big and badly readable chain of events, second is just... crude - can anyone suggest a more elegant way of solving this problem?
If you use the Parallel extensions for .NET 4.0 you can create several asynchronous tasks and join them very easily:
Task[] tasks = new Task[3]
{
Task.Factory.StartNew(() => MethodA()),
Task.Factory.StartNew(() => MethodB()),
Task.Factory.StartNew(() => MethodC())
};
//Block until all tasks complete.
Task.WaitAll(tasks);
Your second approach is a bit easier to understand than the first, but both approaches are a bit fragile.
One alternative is to track the number of outstanding initialization requests and completions, and use this information to decide when to trigger the event. Here's an example of what I mean:
private int _outstandingRequests = 0;
public void InitObjectAsync()
{
RequestField( proxy.GetDataForField1,
proxy.GetDataForField1Completed,
s => Field1 = s );
RequestField( proxy.GetDataForField2,
proxy.GetDataForField2Completed,
s => Field2 = s );
RequestField( proxy.GetDataForField3,
proxy.GetDataForField3Completed,
s => Field3 = s );
// ... and so on...
}
// This method accepts two actions and a event handler reference.
// It composes a lambda to perform the async field assignment and internally
// manages the count of outstanding requests. When the count drops to zero,
// all async requests are finished, and it raises the completed event.
private void RequestField<T>( Action fieldInitAction,
EventHandler fieldInitCompleteEvent,
Action<T> fieldSetter )
{
// maintain the outstanding request count...
_outstandingRequests += 1;
// setup event handler that responds to the field initialize complete
fieldInitCompleteEvent += (s,e) =>
{
fieldSetter( e.Result );
_outstandingRequests -= 1;
// when all outstanding requests finish, raise the completed event
if( _outstandingRequests == 0 )
RaiseInitCompleted();
}
// call the method that asynchronously retrieves the field value...
fieldInitAction();
}
private void RaiseInitCompleted()
{
var initCompleted = InitObjectAsyncCompleted;
if( initCompleted != null )
initCompleted(this, null);
}
Put each WCF call in a little wrapper class. Put those classes in a set (or list if order is important), and make them remove themselves from the set when the call is finished. They should also pulse a Monitor.
Monitor.Enter. Loop through all the WCF calls in the set. Then wait on the Monitor. Every time you get a notification, if the set isn't empty, wait. When you get out of the wait loop, call init and raise the event. You can always time out on the Monitor.Wait if you want to (I often call my locks waitingRoom so it's obvious what's going on).
If you isolate yourself from the fact that it's WCF calls you're waiting on then this is nice and easy to test, too, and you can do things like log any WCF call which fails by identifying it through the wrapper class.
So I'm just playing around with RX and learning it. I started playing with Events, and wanted to know how to subscribe to events, and process the results in batches asynchronously. Allow me to explain with code:
Simple class that raises events:
public class EventRaisingClass
{
public event EventHandler<SomeEventArgs> EventOccured;
//some other code that raises event...
}
public class SomeEventArgs : EventArgs
{
public SomeEventArgs(int data)
{
this.SomeArg = data;
}
public int SomeArg { get; private set; }
}
Then my Main:
public static void Main(string[] args)
{
var eventRaiser = new EventRaisingClass();
IObservable<IEvent<SomeEventArgs>> observable =
Observable.FromEvent<SomeEventArgs>(e => eventRaiser.EventOccured += e, e => eventRaiser.EventOccured -= e);
IObservable<IList<IEvent<SomeEventArgs>>> bufferedEvents = observable.BufferWithCount(100);
//how can I subscribte to bufferedEvents so that the subscription code gets called Async?
bufferedEvents.Subscribe(list => /*do something with list of event args*/); //this happens synchrounously...
}
As you can see in my comments, when you just call subscribe like that, all the subscription code happens synchronously. Is there a way out of the box using RX to have the Subscribe be called on different threads whenever there's a new batch of events to work on?
bufferedEvents.ObserveOn(Scheduler.TaskPool).Subscribe(...
SubscribeOn is to specify the schedule on which so-called "subscription side effects" are happening. For example, your observable can open a file each time somebody subscribes.
ObserveOn is to specify the schedule on which the call to the observer will happen every time when there is a new value. In practice, it is used more often than SubscribeOn.
I believe you're looking for SubscribeOn or ObserveOn, passing an IScheduler. There are several schedulers built-in under System.Concurrency; some of them use whatever thread is current, and others use specific threads.
This video has more info on the scheduler concept.
The Rx team also recently released a hands-on labs document which is the closest thing to a tutorial right now.