how to update search results using signalR - signalr

I've just started learning signalR and I'm trying to implement a search feature.
How would i go about periodically updating a user's search result. My initial idea is to run a timed job via IRegisteredObject to trigger a check from client with search params like so:
public class BackgroundTimer : IRegisteredObject
{
private Timer taskTimer;
private IHubContext hub;
public BackgroundTimer()
{
HostingEnvironment.RegisterObject(this);
hub = GlobalHost.ConnectionManager.GetHubContext<SearchHub>();
taskTimer = new Timer(OnTimerElapsed, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5));
}
private void OnTimerElapsed(object sender)
{
hub.Clients.All.checkForUpdates();
}
}
public class SearchHub : Hub
{
public void Search(string searchText)
{
string jsonResult = string.Empty;
//TODO save result to jsonResult
Clients.Caller.broadcastMessage(jsonResult);
}
}
However i can't help but think there are much more efficient ways to accomplish this. Any advice pls

With this code you are just doing what the clients could instead, poll the server each second. Either publish a message on all actions that alter the search result and act on that. Or use SqlDependency.

Related

Capture ALL WebAPI requests

I would like to capture and save in a log file all the requests that my WebAPI should handle.
Just tried to save the Request.Content from the controller constructor but unfortunately,
the request object is null from the controller constructor scope.
Hope to learn an efficient way to do it.
I would just hook into web api tracing...
http://www.asp.net/web-api/overview/testing-and-debugging/tracing-in-aspnet-web-api
From the above article, you can implement ITraceWriter like so. This example uses System.Diagnostics.Trace.WriteLine, but you could plug in writing to a file here as well.
public class SimpleTracer : ITraceWriter
{
public void Trace(HttpRequestMessage request, string category, TraceLevel level,
Action<TraceRecord> traceAction)
{
TraceRecord rec = new TraceRecord(request, category, level);
traceAction(rec);
WriteTrace(rec);
}
protected void WriteTrace(TraceRecord rec)
{
var message = string.Format("{0};{1};{2}",
rec.Operator, rec.Operation, rec.Message);
System.Diagnostics.Trace.WriteLine(message, rec.Category);
}
}
As you can see from the Trace method, you get access to the HttpRequestMessage here.
I ended up implementing middleware to deal with it.
public class GlobalRequestLogger : OwinMiddleware
{
public override Task Invoke(IOwinContext context)
{
// Implement logging code here
}
}
Then in your Startup.cs:
app.Use<GlobalRequestLogger>();

Quartz.net and Ninject: how to bind implementation to my job using NInject

I am actually working in an ASP.Net MVC 4 web application where we are using NInject for dependency injection. We are also using UnitOfWork and Repositories based on Entity framework.
We would like to use Quartz.net in our application to start some custom job periodically. I would like that NInject bind automatically the services that we need in our job.
It could be something like this:
public class DispatchingJob : IJob
{
private readonly IDispatchingManagementService _dispatchingManagementService;
public DispatchingJob(IDispatchingManagementService dispatchingManagementService )
{
_dispatchingManagementService = dispatchingManagementService ;
}
public void Execute(IJobExecutionContext context)
{
LogManager.Instance.Info(string.Format("Dispatching job started at: {0}", DateTime.Now));
_dispatchingManagementService.DispatchAtomicChecks();
LogManager.Instance.Info(string.Format("Dispatching job ended at: {0}", DateTime.Now));
}
}
So far, in our NInjectWebCommon binding is configured like this (using request scope):
kernel.Bind<IDispatchingManagementService>().To<DispatchingManagementService>();
Is it possible to inject the correct implementation into our custom job using NInject ? and how to do it ? I have read already few posts on stack overflow, however i need some advises and some example using NInject.
Use a JobFactory in your Quartz schedule, and resolve your job instance there.
So, in your NInject config set up the job (I'm guessing at the correct NInject syntax here)
// Assuming you only have one IJob
kernel.Bind<IJob>().To<DispatchingJob>();
Then, create a JobFactory: [edit: this is a modified version of #BatteryBackupUnit's answer here]
public class NInjectJobFactory : IJobFactory
{
private readonly IResolutionRoot resolutionRoot;
public NinjectJobFactory(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
// If you have multiple jobs, specify the name as
// bundle.JobDetail.JobType.Name, or pass the type, whatever
// NInject wants..
return (IJob)this.resolutionRoot.Get<IJob>();
}
public void ReturnJob(IJob job)
{
this.resolutionRoot.Release(job);
}
}
Then, when you create the scheduler, assign the JobFactory to it:
private IScheduler GetSchedule(IResolutionRoot root)
{
var schedule = new StdSchedulerFactory().GetScheduler();
schedule.JobFactory = new NInjectJobFactory(root);
return schedule;
}
Quartz will then use the JobFactory to create the job, and NInject will resolve the dependencies for you.
Regarding scoping of the IUnitOfWork, as per a comment of the answer i linked, you can do
// default for web requests
Bind<IUnitOfWork>().To<UnitOfWork>()
.InRequestScope();
// fall back to `InCallScope()` when there's no web request.
Bind<IUnitOfWork>().To<UnitOfWork>()
.When(x => HttpContext.Current == null)
.InCallScope();
There's only one caveat that you should be aware of:
With incorrect usage of async in a web request, you may mistakenly be resolving a IUnitOfWork in a worker thread where HttpContext.Current is null. Now without the fallback binding, this would fail with an exception which would show you that you've done something wrong. With the fallback binding however, the issue may present itself in an obscured way. That is, it may work sometimes, but sometimes not. This is because there will be two (or even more) IUnitOfWork instances for the same request.
To remedy this, we can make the binding more specific. For this, we need some parameter to tell us to use another than InRequestScope(). Have a look at:
public class NonRequestScopedParameter : Ninject.Parameters.IParameter
{
public bool Equals(IParameter other)
{
if (other == null)
{
return false;
}
return other is NonRequestScopedParameter;
}
public object GetValue(IContext context, ITarget target)
{
throw new NotSupportedException("this parameter does not provide a value");
}
public string Name
{
get { return typeof(NonRequestScopedParameter).Name; }
}
// this is very important
public bool ShouldInherit
{
get { return true; }
}
}
now adapt the job factory as follows:
public class NInjectJobFactory : IJobFactory
{
private readonly IResolutionRoot resolutionRoot;
public NinjectJobFactory(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
return (IJob) this.resolutionRoot.Get(
bundle.JobDetail.JobType,
new NonrequestScopedParameter()); // parameter goes here
}
public void ReturnJob(IJob job)
{
this.resolutionRoot.Release(job);
}
}
and adapt the IUnitOfWork bindings:
Bind<IUnitOfWork>().To<UnitOfWork>()
.InRequestScope();
Bind<IUnitOfWork>().To<UnitOfWork>()
.When(x => x.Parameters.OfType<NonRequestScopedParameter>().Any())
.InCallScope();
This way, if you use async wrong, there'll still be an exception, but IUnitOfWork scoping will still work for quartz tasks.
For any users that could be interested, here is the solution that finally worked for me.
I have made it working doing some adjustment to match my project. Please note that in the method NewJob, I have replaced the call to Kernel.Get by _resolutionRoot.Get.
As you can find here:
public class JobFactory : IJobFactory
{
private readonly IResolutionRoot _resolutionRoot;
public JobFactory(IResolutionRoot resolutionRoot)
{
this._resolutionRoot = resolutionRoot;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
return (IJob)_resolutionRoot.Get(
bundle.JobDetail.JobType, new NonRequestScopedParameter()); // parameter goes here
}
catch (Exception ex)
{
LogManager.Instance.Info(string.Format("Exception raised in JobFactory"));
}
}
public void ReturnJob(IJob job)
{
}
}
And here is the call schedule my job:
public static void RegisterScheduler(IKernel kernel)
{
try
{
var scheduler = new StdSchedulerFactory().GetScheduler();
scheduler.JobFactory = new JobFactory(kernel);
....
}
}
Thank you very much for your help
Thanks so much for your response. I have implemented something like that and the binding is working :):
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
var resolver = DependencyResolver.Current;
var myJob = (IJob)resolver.GetService(typeof(IJob));
return myJob;
}
As I told before I am using in my project a service and unit of work (based on EF) that are both injected with NInject.
public class DispatchingManagementService : IDispatchingManagementService
{
private readonly IUnitOfWork _unitOfWork;
public DispatchingManagementService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
}
Please find here how I am binding the implementations:
kernel.Bind<IUnitOfWork>().To<EfUnitOfWork>()
kernel.Bind<IDispatchingManagementService>().To<DispatchingManagementService>();
kernel.Bind<IJob>().To<DispatchingJob>();
To resume, the binding of IUnitOfWork is done for:
- Eevery time a new request is coming to my application ASP.Net MVC: Request scope
- Every time I am running the job: InCallScope
What are the best practices according to the behavior of EF ? I have find information to use CallInScope. Is it possible to tell NInject to get a scope ByRequest everytime a new request is coming to the application, and a InCallScope everytime my job is running ? How to do that ?
Thank you very much for your help

synchronously invoke client side method with SignalR

SignalR does not have the ability to have client methods which returns a value. So I am trying to create a helper class to make this possible.
So this is what I am trying to do:
Server side: Call client method and provide unique request id Client(clientId).GetValue(requestId)
Server side: Save requestId and wait for answer using ManualResetEvent
Client side: Inside void GetValue(Guid requestId) call server method hubProxy.Invoke("GetValueFinished", requestId, 10)
Server side: find waiting method by requestId => set return value => set signal
Server side: Method not longer waiting vor ManualResetEvent and returns retrieved value.
I am able to get it work unfortunately. Here is my code:
public static class MethodHandler
{
private static ConcurrentDictionary<Guid, ReturnWaiter> runningMethodWaiters = new ConcurrentDictionary<Guid,ReturnWaiter>();
public static TResult GetValue<TResult>(Action<Guid> requestValue)
{
Guid key = Guid.NewGuid();
ReturnWaiter returnWaiter = new ReturnWaiter(key);
runningMethodWaiters.TryAdd(key, returnWaiter);
requestValue.Invoke(key);
returnWaiter.Signal.WaitOne();
return (TResult)returnWaiter.Value;
}
public static void GetValueResult(Guid key, object value)
{
ReturnWaiter waiter;
if (runningMethodWaiters.TryRemove(key, out waiter))
{
waiter.Value = value;
}
}
}
internal class ReturnWaiter
{
private ManualResetEvent _signal = new ManualResetEvent(false);
public ManualResetEvent Signal { get { return _signal; } }
public Guid Key {get; private set;}
public ReturnWaiter(Guid key)
{
Key = key;
}
private object _value;
public object Value
{
get { return _value; }
set
{
_value = value;
Signal.Set();
}
}
}
Using this MethodHandler class I need to have two method server side:
public int GetValue(string clientId)
{
return MethodHandler.GetValue<int>(key => Clients(clientId).Client.GetValue(key));
}
public void GetValueResult(Guid key, object value)
{
MethodHandler.GetValueResult(key, value);
}
Client side implementation is like this:
// Method registration
_hubProxy.On("GetValue", new Action<Guid>(GetValue));
public void GetValue(Guid requestId)
{
int result = 10;
_hubConnection.Invoke("GetValueResult", requestId, result);
}
PROBLEM:
if I call server side GetValue("clientid"). The client method will not be invoked. If I comment out returnWaiter.Signal.WaitOne();, client side GetValue is called and server side GetValueResult is called. But of course this time the method has already returned.
I thought is has to do with the ManualResetEvent but even using while(!returnWaiter.HasValue) Thread.Sleep(100); will not fix this issue.
Any ideas how to fix this issue?
Thanks in advance!
First, I think that, rather than asking for help in how to make it synchronous, it would be best if you just told us what it is you're trying to do so we could suggest a proper approach to do it.
You don't show your MethodHandler::Retrieve method, but I can guess pretty much what it looks like and it's not even the real problem. I have to tell you in the nicest possible way that this is a really bad idea. It will simply never scale. This would only work with a single SignalR server instance because you're relying on machine specific resources (e.g. kernel objects behind the ManualResetEvent) to provide the blocking. Maybe you don't need to scale beyond one server to meet your requirements, but this still a terrible waste of resources even on a single server.
You're actually on the right track with the client calling back with the requestId as a correlating identifier. Why can't you use that correlation to resume logical execution of whatever process you are in the middle of on the server side? That way no resources are held around while waiting for the message to be delivered to the client, processed and then the follow up message, GetValueResult in your sample, to be sent back a the server instance.
Problem solved:
The problem only occured in Hub.OnConnected and Hub.OnDisconnected. I don't have an exact explanation why, but probably these methods must be able to finish before it will handle your method call to the client.
So I changed code:
public override Task OnConnected()
{
// NOT WORKING
Debug.Print(MethodHandler.GetValue<int>(key => Clients(Context.ConnectionId).Client.GetValue(key)));
// WORKING
new Thread(() => Debug.Print(MethodHandler.GetValue<int>(key => Clients(Context.ConnectionId).Client.GetValue(key)))).Start();
return base.OnConnected();
}

GlobalHost.ConnectionManager.GetHubContext & Clients.Others

I seem to be having a problem calling:
Clients.Others.SomeJavascriptFunction;
When I use
GlobalHost.ConnectionManager.GetHubContext("MyHub");
I seem to be only able to get this to work within a Hub.
Can anybody shed some light on this.
Regards
Mike
Update
After David's comment
I found that I could do the following:
public class MyHub1 : Hub
{
public static HubConnectionContext MyProperty { get; set; }
public void Start()
{
MyProperty = Clients;
}
}
Then call Start in my js
$.connection.hub.start().done(function () {
sig.server.start();
});
Which then allows me to call my function from my api controller
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle) {
var changes = _contextProvider.SaveChanges(saveBundle);
var stuff = MyHub1.MyProperty;
stuff.Others.refreshToDos();
return changes;
}
Maybe a complete hack but seems to work.
Can anyone see any problems with this?
Others only makes sense when you have a connection id to exclude. Others is shorthand for Clients.AllExcept(Context.ConnectionId). When you're outside the hub there's no current connection id so you can't use Others.
You need to pass the connection id from the client to the API to want to use to do AllExcept.

Signalr & Nancyfx integration

My app flow is as follows (simplified for clarity):
User GETs a page from "/page1"
User performs actions on the page (adds text, clicks, etc..), while Signalr communicates this data to the server, which performs heavy calculations in the background, and the results of those are returned to the page (lets call those "X").
When the user is finished with the page, he clicks a link to "/page2", that is returned by Nancy. This page is built using a Model that is dependent on X.
So, how do I build that Model based on X? How can signalr write to the user session in a way that Nancy can pick up on?
(I'm looking for a "clean" way)
Pending formal integration of Signalr & Nancy, this is what I came with. Basically, I share an IOC container between the two, and use an object (singleton lifetime) that maps users to state.
How to share an IOC container using the built in TinyIOC:
Extend Signalr's DefaultDependencyResolver
public class TinyIoCDependencyResolver : DefaultDependencyResolver
{
private readonly TinyIoCContainer m_Container;
public TinyIoCDependencyResolver(TinyIoCContainer container)
{
m_Container = container;
}
public override object GetService(Type serviceType)
{
return m_Container.CanResolve(serviceType) ? m_Container.Resolve(serviceType) : base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
var objects = m_Container.CanResolve(serviceType) ? m_Container.ResolveAll(serviceType) : new object[] { };
return objects.Concat(base.GetServices(serviceType));
}
}
Replace Signalr's default DependencyResolver with our new one
public class Bootstrapper : DefaultNancyBootstrapper
{
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
CookieBasedSessions.Enable(pipelines);
// Replace UserToStateMap with your class of choice
container.Register<IUserToStateMap, UserToStateMap>();
GlobalHost.DependencyResolver = new TinyIoCDependencyResolver(container);
RouteTable.Routes.MapHubs();
}
}
Add IUserToStateMap as a dependency in your hubs and Nancy modules
public class MyModule : NancyModule
{
public MyModule(IUserToStateMap userToStateMap)
{
Get["/"] = o =>
{
var userId = Session["userId"];
var state = userToStateMap[userId];
return state.Foo;
};
}
}
public class MyHub : Hub
{
private readonly IUserToStateMap m_UserToStateMap;
public MyHub(IUserToStateMap userToStateMap)
{
m_UserToStateMap = userToStateMap;
}
public string MySignalrMethod(string userId)
{
var state = userToStateMap[userId];
return state.Bar;
}
}
What I would really want, is a way to easily share state between the two based on the connection ID or something like that, but in the meantime this solution works for me.
Did you arrive hear looking for a simple example of how to integrate Nancy and SignalR? I know I did.
Try this question instead (I self-answered it).
SignalR plus NancyFX : A simple but well worked example

Resources