ControllerからSignalR Core Hubメソッドを呼び出すにはどうすればよいですか?
Microsoft.AspNetCore.SignalR(1.0.0-alpha2-final)でASP.NET Core 2.0を使用しています。
Excel、SolidEdgeと通信するWindowsサービスがあります...操作が完了すると、ASP.NET Coreアプリケーションのコントローラーにリクエストを送信します。次に、SignalRを使用してサーバーに接続されているすべてのクライアントに、外部プログラムが何らかのタスクを完了したことを通知する必要があります。
ウィンドウサービスの動作を変更することはできません。 (ウィンドウサービスからSignalRに接続できません)。
古いSignalR(GlobalHost.ConnectionManager.GetHubContext
)の解決策をたくさん見つけましたが、多くの変更が加えられ、それらの解決策は機能しなくなりました。
私のコントローラー:
[Route("API/vardesigncomm")]
public class VarDesignCommController : Controller
{
[HttpPut("ProcessVarDesignCommResponse/{id}")]
public async Task<IActionResult> ProcessVarDesignCommResponse(int id)
{
//call method TaskCompleted in Hub !!!! How?
return new JsonResult(true);
}
}
私のハブ:
public class VarDesignHub : Hub
{
public async Task TaskCompleted(int id)
{
await Clients.All.InvokeAsync("Completed", id);
}
}
ソリューション1
もう1つの可能性は、次のようにHubContextをコントローラーに挿入することです。
public VarDesignCommController(IHubContext<VarDesignHub> hubcontext)
{
HubContext = hubcontext;
...
}
private IHubContext<VarDesignHub> HubContext
{
get;
set;
}
その後、あなたも呼び出すことができます
await this.HubContext.Clients.All.InvokeAsync("Completed", id);
ただし、すべてのクライアントで呼び出しメソッドを指示します。
ソリューション2
型付きハブを使用することもできます。サーバーがクライアントで呼び出すことができるメソッドを定義するインターフェイスを簡単に作成します。
public interface ITypedHubClient
{
Task BroadcastMessage(string name, string message);
}
ハブから継承:
public class ChatHub : Hub<ITypedHubClient>
{
public void Send(string name, string message)
{
Clients.All.BroadcastMessage(name, message);
}
}
入力したハブコンテキストをコントローラーに挿入し、それを操作します。
[Route("api/demo")]
public class DemoController : Controller
{
IHubContext<ChatHub, ITypedHubClient> _chatHubContext;
public DemoController(IHubContext<ChatHub, ITypedHubClient> chatHubContext)
{
_chatHubContext = chatHubContext;
}
// GET: api/values
[HttpGet]
public IEnumerable<string> Get()
{
_chatHubContext.Clients.All.BroadcastMessage("test", "test");
return new string[] { "value1", "value2" };
}
}
現在の答えは、提起された質問には答えません。
簡単な答えは、MVCコントローラーなどからハブメソッドを直接呼び出すことはできないということです。これは仕様です。ハブには、サーバーメソッドやコントローラーメソッドではなく、SignalR Coreクライアントが呼び出すエンドポイントが含まれていると考えてください。
Microsoft says (これはSignalR Core以前のドキュメントですが、SignalR Coreにも適用されます):
Hubクラスをインスタンス化したり、サーバー上の独自のコードからそのメソッドを呼び出したりすることはありません。これらはすべて、SignalR Hubsパイプラインによって行われます。 SignalRは、クライアントがサーバーに接続、切断、またはメソッド呼び出しを行うときなど、ハブ操作を処理する必要があるたびに、ハブクラスの新しいインスタンスを作成します。
Hubクラスのインスタンスは一時的なものであるため、それらを使用して、あるメソッド呼び出しから次のメソッド呼び出しまでの状態を維持することはできません。サーバーがクライアントからメソッド呼び出しを受け取るたびに、Hubクラスの新しいインスタンスがメッセージを処理します。複数の接続とメソッド呼び出しを通じて状態を維持するには、データベースなどの他のメソッド、ハブクラスの静的変数、またはハブから派生していない別のクラスを使用します。 Hubクラスの静的変数などのメソッドを使用してデータをメモリに保持すると、アプリドメインがリサイクルされるときにデータが失われます。
Hubクラスの外部で実行される独自のコードからクライアントにメッセージを送信する場合、Hubクラスインスタンスをインスタンス化することではできませんが、HubクラスのSignalRコンテキストオブジェクトへの参照を取得することで実行できます...
呼び出す必要があるコードがハブにある場合は、どこからでもアクセスできる外部クラスまたはサービスにコードを配置することをお勧めします。
ASP.NET Coreの簡単な組み込みDIフレームワークを使用した例を次に示します。
呼び出す必要があるコードがDoStuff.csにあると仮定します。
public class DoStuff : IDoStuff
{
public string GetData()
{
return "MyData";
}
}
public interface IDoStuff
{
string GetData();
}
Startup.csで、組み込みコンテナーを使用してシングルトンを構成します。
services.AddSingleton<IDoStuff, DoStuff>();
完全なStartup.csは次のようになります。
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddSignalR();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSingleton<IDoStuff, DoStuff>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseSignalR(routes =>
{
routes.MapHub<MyHub>("/myhub");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
ハブクラスの場合、シングルトンを注入し、メソッドで使用します。
public class MyHub : Hub
{
private readonly IDoStuff _doStuff;
public MyHub(IDoStuff doStuff)
{
_doStuff = doStuff;
}
public string GetData()
{
return _doStuff.GetData();
}
}
次に、コントローラーで、IHubContextとシングルトンを注入します。
public class HomeController : Controller
{
private readonly IDoStuff _doStuff;
private readonly IHubContext<MyHub> _hub;
public HomeController(IDoStuff doStuff, IHubContext<MyHub> hub)
{
_doStuff = doStuff;
_hub = hub;
}
public async Task<IActionResult> Index()
{
var data = _doStuff.GetData();
await _hub.Clients.All.SendAsync("show_data", data);
return View();
}
}
もちろん、Javascriptまたは他のクライアントにshow_dataコールバックを設定する必要があります。
注入されたハブコンテキストを使用して、すべてのSignalRクライアントにデータを送信していることに注意してください:_hub.Clients.All.SendAsync(...)
これは今ではよく文書化されています here
IHubContextのインスタンスをコンストラクターに追加することにより、コントローラーに注入できます。
public class HomeController : Controller { private readonly IHubContext<NotificationHub> _hubContext; public HomeController(IHubContext<NotificationHub> hubContext) { _hubContext = hubContext; } }
IHubContextのインスタンスへのアクセスにより、ハブ自体にいるかのようにハブメソッドを呼び出すことができます。
public async Task<IActionResult> Index() { await _hubContext.Clients.All.SendAsync("Notify", $"Home page loaded at: {DateTime.Now}"); return View(); }