I am connecting a web api service to a backend-stateless service.
The Backservice is called MyProject.Management.Company and its code is:
internal sealed class Company: StatelessService,ICompanyManagement
{
private readonly CompanyManagementImpl _impl;
public Tenents(StatelessServiceContext context, CompanyManagementImpl impl)
: base(context)
{
this._impl = impl;
}
/// <summary>
/// Optional override to create listeners (e.g., TCP, HTTP) for this service replica to handle client or user requests.
/// </summary>
/// <returns>A collection of listeners.</returns>
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[]
{
new ServiceInstanceListener(serviceContext => new FabricTransportServiceRemotingListener(serviceContext, this), "ServiceEndpoint")
};
}
/// <summary>
/// This is the main entry point for your service instance.
/// </summary>
/// <param name="cancellationToken">Canceled when Service Fabric needs to shut down this service instance.</param>
protected override async Task RunAsync(CancellationToken cancellationToken)
{
// TODO: Replace the following sample code with your own logic
// or remove this RunAsync override if it's not needed in your service.
long iterations = 0;
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
ServiceEventSource.Current.ServiceMessage(this.Context, "Working-{0}", ++iterations);
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
}
public Task CreateCompany(Company company)
{
return _impl.CreateCompany(company);
}
public Task<List<Company>> GetAllCompanies()
{
return _impl.GetAllCompanies();
}
public Task<Company> GetCompanyById(string companyId)
{
return _impl.GetCompanyById(companyId);
}
}
The code is Listener code is adopted from This Blog Post and even the documentation code doesn't compile documentation the CreateServiceRemotingListenervextension method doesn't exist.
The ICompanyManagement is an interface inherting from IService interface and the implementation is realized via the CompanyManagament that in this phase just returns static objects.
The API is called MyProject.Portal and the controller code is:
public class CompanyController : Controller
{
ICompanyManagement _proxy;
public CompanyController(StatelessServiceContext context)
{
string serviceUri = $"{context.CodePackageActivationContext.ApplicationName}" + "/MyProject.Management.Company";
_proxy = ServiceProxy.Create<ICompanyManagement>(new Uri(serviceUri));
}
// GET: api/Company
[HttpGet]
public async Task<JsonResult> Get()
{
try
{
var result = await _proxy.GetAllCompanies();
return this.Json(result);
}
catch (Exception ex)
{
throw ex;
}
}
}
When the code run it returns the following error.
NamedEndpoint 'V2Listener' not found in the address
'{"Endpoints":{"ServiceEndpoint":"localhost:59286+12a705ed-11a5-4bf5-bafd-84179c966257-131719261525940414-9e876439-9294-4ec9-8b33-05f17515aaf4"}}'
for partition '12a705ed-11a5-4bf5-bafd-84179c966257'
Finally: I am using .netcore v2, service fabric v6.2.274.
Right after usings in your ICompanyManagement file add following line:
[assembly: FabricTransportServiceRemotingProvider(RemotingListener = RemotingListener.V2Listener, RemotingClient = RemotingClient.V2Client)]
In your service (CompanyManagement) manifest (ServiceManifest.xml file) make sure that endpoint is set to version 2:
<Resources>
<Endpoints>
<Endpoint Name="ServiceEndpointV2" />
</Endpoints>
</Resources>
Change your CreateServiceInstanceListeners method to:
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return this.CreateServiceRemotingInstanceListeners();
}
Finally in your web api controller register your service proxy like this:
ICompanyManagement companyManagementClient = ServiceProxy.Create<ICompanyManagement>(new Uri($"fabric:/{applicationName}/{serviceName}"));
If you follow these steps it will work.
Related
I created a .net core web api project. It has gotten kinda big and I want to program a "delete" operation which deletes a lot of stuff from the database. Since there are a lot of things to delete, this will be a long running process. So I thought maybe I can run this in the background and just write status updates somewhere for the user to see whats happening.
I googled this and I found BackgroundWorkerQueue and thought this might be my solution.
So I registered the service and everything and here is my method that calls it:
public class DeleteController : ControllerBase {
private readonly BackgroundWorkerQueue _backgroundWorkerQueue;
public AdminController(BackgroundWorkerQueue backgroundWorkerQueue){
_backgroundWorkerQueue = backgroundWorkerQueue;
}
public async Task<ActionResult> HugeDeleteMethod(int id)
{
// some prechecks here...
// and here I thought I'd start the background task
_backgroundWorkerQueue.QueueBackgroundWorkItem(async token =>
{
var a = _context.StatusTable.Find(id);
a.Status += "Blablablabla\n";
_context.StatusTable.Update(a);
await _context.SaveChangesAsync();
//now start doing delete operations
});
}
}
And that class looks like this:
public class BackgroundWorkerQueue
{
private ConcurrentQueue<Func<CancellationToken, Task>> _workItems = new ConcurrentQueue<Func<CancellationToken, Task>>();
private SemaphoreSlim _signal = new SemaphoreSlim(0);
public async Task<Func<CancellationToken, Task>> DequeueAsync(CancellationToken cancellationToken)
{
await _signal.WaitAsync(cancellationToken);
_workItems.TryDequeue(out var workItem);
return workItem;
}
public void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
_workItems.Enqueue(workItem);
_signal.Release();
}
}
There is also a DeleteService, which is also called in my startup, but I am not sure what it does:
public class DeleteService : BackgroundService
{
private readonly BackgroundWorkerQueue queue;
public NukeService(BackgroundWorkerQueue queue)
{
this.queue = queue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem = await queue.DequeueAsync(stoppingToken);
await workItem(stoppingToken);
}
}
}
Both are added in my startup.cs:
services.AddHostedService<DeleteService>();
services.AddSingleton<BackgroundWorkerQueue>();
Well, maybe I'm going about this all wrong. This is never called it seems, the StatusTable field "Status" is always empty. So how do I do this?
You just need to subclass BackgroundService class or implement IHostedService and than register your service as hosted service.
This will run a service in the background. Than in your service you can leverage the BlockingQueue that will perform tasks only when they are added, e.g. like this:
public class MyService : BackgroundService {
private readonly BlockingCollection<long> queue;
public MyService(){
this.queue = new BlockingCollection<long>();
Task.Run(async () => await this.Execute());
}
public void AddId(long id) {
this.queue.Add(id);
}
private async Task Execute()
{
foreach (var id in this.queue.GetConsumingEnumerable())
{
... do your stuff ...
}
}
}
services.AddHostedService<MyService>();
Here is the docu: Background services in .net core
I have just implemented the Bearer token and I have added the Authorize attribute to my controller class and that works fine. It looks like this:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
What I would like to do is to create a more complex response from the server when it fails, rather then the standard 401.
I tried filters but they are not invoked at all.
Any ideas how to do this?
Have a custom scheme, custom authorization handler and poof!
Notice that I injected the Handler in ConfigureServices:
services.AddAuthentication(options =>
{
options.DefaultScheme = ApiKeyAuthenticationOptions.DefaultScheme;
options.DefaultAuthenticateScheme = ApiKeyAuthenticationOptions.DefaultScheme;
})
.AddScheme<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>(
ApiKeyAuthenticationOptions.DefaultScheme, o => { });
ApiKeyAuthenticationOptions
public class ApiKeyAuthenticationOptions : AuthenticationSchemeOptions
{
public const string DefaultScheme = "API Key";
public string Scheme => DefaultScheme;
public string AuthenticationType = DefaultScheme;
public const string HeaderKey = "X-Api-Key";
}
ApiKeyAuthenticationHandler
/// <summary>
/// An Auth handler to handle authentication for a .NET Core project via Api keys.
///
/// This helps to resolve dependency issues when utilises a non-conventional method.
/// https://stackoverflow.com/questions/47324129/no-authenticationscheme-was-specified-and-there-was-no-defaultchallengescheme-f
/// </summary>
public class ApiKeyAuthenticationHandler : AuthenticationHandler<ApiKeyAuthenticationOptions>
{
private readonly IServiceProvider _serviceProvider;
public ApiKeyAuthenticationHandler(IOptionsMonitor<ApiKeyAuthenticationOptions> options, ILoggerFactory logger,
UrlEncoder encoder, ISystemClock clock, IServiceProvider serviceProvider)
: base (options, logger, encoder, clock)
{
_serviceProvider = serviceProvider;
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var token = Request.Headers[ApiKeyAuthenticationOptions.HeaderKey];
if (string.IsNullOrEmpty(token)) {
return Task.FromResult (AuthenticateResult.Fail ("Token is null"));
}
var customRedisEvent = _serviceProvider.GetRequiredService<ICustomRedisEvent>();
var isValidToken = customRedisEvent.Exists(token, RedisDatabases.ApiKeyUser);
if (!isValidToken) {
return Task.FromResult (AuthenticateResult.Fail ($"Invalid token {token}."));
}
var claims = new [] { new Claim ("token", token) };
var identity = new ClaimsIdentity (claims, nameof (ApiKeyAuthenticationHandler));
var ticket = new AuthenticationTicket (new ClaimsPrincipal (identity), Scheme.Name);
return Task.FromResult (AuthenticateResult.Success (ticket));
}
}
Focus on the handler class. Apart from the sample code I've provided, simply utilise the base class properties like Response to set your custom http status code or whatever you may need!
Here's the derived class if you need it.
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.authenticationhandler-1?view=aspnetcore-3.1
Following is the code I am using:
namespace MySite.Api
{
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using System;
using System.Net.Http.Headers;
using Microsoft.Extensions.Logging;
/// <summary>
/// API query execution helper
/// </summary>
public class ApiQuery : IApiQuery
{
/// <summary>
/// configuration reference
/// </summary>
private IConfiguration config;
private HmacAuthenticationUtils hmacUtils;
private readonly ILogger logger;
private static readonly HttpClient httpClient = new HttpClient();
private static readonly HttpClient httpClientHMAC = new HttpClient();
/// <summary>
/// Initializes a new instance of the <see cref="ApiQuery"/> class.
/// </summary>
/// <param name="inConfig">injected configuration</param>
public ApiQuery(IConfiguration inConfig, HmacAuthenticationUtils hmacUtils, ILoggerFactory loggerFactory)
{
this.config = inConfig;
this.hmacUtils = hmacUtils;
this.logger = loggerFactory.CreateLogger("perfLogger");
}
/// <summary>
/// HTTP verb post
/// </summary>
/// <param name="requestUrl">API url</param>
/// <param name="requestData">request data</param>
/// <returns>HTTP response message</returns>
public virtual async Task<string> Post(string requestUrl, object requestData, HttpClient client = null)
{
return await PostBypassCache(requestUrl, requestData, client);
}
/// <summary>
/// HTTP verb post, specifically to bypass cache
/// </summary>
/// <param name="requestUrl">API url</param>
/// <param name="requestData">request data</param>
/// <returns>HTTP response message</returns>
public async Task<string> PostBypassCache(string requestUrl, object requestData, HttpClient client = null)
{
DateTime perfStart = DateTime.Now;
string customerJson = string.Empty;
if (requestData is string)
{
customerJson = requestData.ToString();
}
else
{
customerJson = JsonConvert.SerializeObject(requestData);
}
////just some template output to test which I'm getting back.
string resultJson = "{ 'status':'No Content'}";
if (client == null)
{
client = httpClient;
}
var response = await httpClient.PostAsync(requestUrl, new StringContent(customerJson, Encoding.UTF8, "application/json"));
if (response.StatusCode == HttpStatusCode.OK)
{
resultJson = await response.Content.ReadAsStringAsync();
}
logger.LogInformation("response time: " + (DateTime.Now - perfStart).TotalMilliseconds + "ms. Resource:" + requestUrl);
return resultJson;
}
/// <summary>
/// HTTP verb post
/// </summary>
/// <param name="requestUrl">API url</param>
/// <param name="requestData">request data</param>
/// <param name="headerset">header data</param>
/// <returns>string data</returns>
public async Task<string> PostHmacAuth(string requestUrl, string requestData)
{
var httpRequest = new HttpRequestMessage(HttpMethod.Post, requestUrl);
httpRequest.Content = new StringContent(requestData, Encoding.UTF8, "application/json");
var signature = await Utils.GenerateAuthenticationString(httpRequest);
httpClientHMAC.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(HmacAuthenticationUtils.HmacHeader, signature);
return await PostBypassCache(requestUrl, requestData, httpClientHMAC);
}
}
}
In the Startup.cs, I inject it
services.AddTransient<IApiQuery, ApiQuery>();
I have recently made these changes as previously the code was actually instantiating the httpClient in each of the method, i.e.,
var client = new HttpClient();
At some places it was like:
using(var client = new HttpClient()){}
I Think due to such code, the the appPool is showing error due to which my IIS hangs, and the problem only get solved by restarting the appPool. I conclude this to be problem as I went through a lot of other articles. What I was not able to conclude is on if it should be a good idea to inject the ApiQuery Service as singleton itself.
Will it be better to inject it as ?
As I am injecting IApiQuery to every business service right now as a transient service, will it be good idea ? Any thoughts
HttpClient should be singleton-scoped. There's a finite number of connections available on your machine, and since HttpClient holds on to connections it creates, having multiple instances floating around can quickly exhaust your connection pool.
Beginning with ASP.NET Core 2.1, there exists IHttpClientFactory which provides a simple and reusable way of injecting properly scoped HttpClient instances. However, since you're using 1.1, that's not available to you. The recommended path would be to upgrade your project to 2.1. The 1.X line of ASP.NET Core is frankly trash. It wasn't ready for production use, despite being an official release.
If you insist on sticking with 1.1, then you'll need to implement your own method of reusing HttpClient instances. The most straightforward way is to use "accessor" classes, which you can then utilize to inject different HttpClients into different objects. For example:
public class ApiHttpClientAccessor : IDisposable
{
public ApiHttpClientAccessor()
{
HttpClient = new HttpClient
{
BaseAddress = new Uri("https://foo.com")
};
}
public HttpClient HttpClient { get; }
private bool _disposed;
public virtual void Dispose(bool disposing)
{
if (disposing && !_disposed)
{
HttpClient.Dispose();
}
_disposed = true;
}
public bool Dispose() =>
Dispose(true);
}
Then, you can register this accessor class as a singleton, meaning it will only be created once (so the contained HttpClient will also only be created once). Then, set up your class to accept this accessor in its constructor:
public class ApiQuery : IApiQuery
{
private readonly HttpClient _client;
public ApiQuery(ApiHttpClientAccessor httpClientAccessor)
{
_client = (httpClientAccessor ?? throw new ArgumentNullException(nameof(httpClientAccessor))).HttpClient;
}
...
}
And in Startup.cs:
services.AddSingleton<ApiHttpClientAccessor>();
services.AddTransient<IApiQuery, ApiQuery>();
First the error:
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and
then later trying to use the same context instance elsewhere in your
application. This may occur if you are calling Dispose() on the
context, or wrapping the context in a using statement. If you are
using dependency injection, you should let the dependency injection
container take care of disposing context instances.
Object name: 'MemberContext'.
I have 3 projects, Domain, API and WebSPA app.
Domain has 2 modules, DomainModule and MediatorModule
public class DomainModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(typeof(MemberContext).Assembly)
.AsImplementedInterfaces()
.InstancePerLifetimeScope(); // via assembly scan
builder.RegisterType<MemberContext>().AsSelf()
.InstancePerLifetimeScope(); // or individually
}
}
public class MediatorModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
// enables contravariant Resolve() for interfaces with single contravariant ("in") arg
builder
.RegisterSource(new ContravariantRegistrationSource());
// mediator itself
builder
.RegisterType<Mediator>()
.As<IMediator>()
.InstancePerLifetimeScope();
// request handlers
builder
.Register<SingleInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t =>
{
object o;
return c.TryResolve(t, out o) ? o : null;
};
})
.InstancePerLifetimeScope();
// notification handlers
builder
.Register<MultiInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => (IEnumerable<object>) c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
})
.InstancePerLifetimeScope();
}
}
In API project I have also 2 modules, ApplicationModule and again MediatorModule same as the one above.
public class ApplicationModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(typeof(Startup).Assembly)
.AsImplementedInterfaces()
.InstancePerLifetimeScope(); // via assembly scan
builder.RegisterType<MemberContext>().AsSelf().InstancePerLifetimeScope(); // or individually
}
}
No, when I debug I can see that member context gets newed up on each request, yet on second request, it throws above error. To make sure I am not going crazy, I modified constructor of dbcontext to create an id for context so i can verify they are different. What am I doing wrong?
public MemberContext(DbContextOptions<MemberContext> options) : base(options)
{
MemberContextId = Guid.NewGuid();
Console.WriteLine("member context created: " + MemberContextId);
}
Here is the startup in API
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 IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
// .AllowCredentials()
);
});
services.AddMvc()
.AddControllersAsServices();//Injecting Controllers themselves thru DI
//For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services
AddSwaggerGen(services);
//var connection = Configuration["ConnectionString"];
//services.AddDbContext<MemberContext>(options => options.UseSqlServer(connection),ServiceLifetime.Scoped);
services.AddEntityFrameworkSqlServer()
.AddDbContext<MemberContext>(options =>
{
options.UseSqlServer(Configuration["ConnectionString"]
//,sqlServerOptionsAction: sqlOptions =>
//{
// sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
// sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
//}
);
},
ServiceLifetime.Scoped //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request)
);
var container = new ContainerBuilder();
container.Populate(services);
container.RegisterAssemblyModules(typeof(VIN.Members.Domain.Entities.Member).Assembly,
typeof(Startup).Assembly);
return new AutofacServiceProvider(container.Build());
}
// 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();
}
//NOTE: must be before UseMVC !!!
app.UseCors("CorsPolicy");
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
}
private void AddSwaggerGen(IServiceCollection services)
{
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "VIN Members HTTP API",
Version = "v1",
Description = "Members Service HTTP API",
TermsOfService = "Terms Of Service"
});
});
}
}
UPDATE:
What I am trying to do is delete a record. On client side code looks like this
onDelete(item: IMember) {
//TODO: replace this with dialog service component
if (window.confirm('Are sure you want to delete this member?')) {
//put your delete method logic here
this.service.deleteMember(item).subscribe(x => {
this.getMembers();
});
}
}
this delete request gets mapped to a controller that passes it to mediator
Controller
// DELETE api/members/5
[HttpDelete("{id}")]
public void Delete(Guid id)
{
var command = new DeleteMember.Command(id);
_mediator.Send(command).ConfigureAwait(false);
}
and finally handler
public class DeleteMember
{
public class Command : IRequest
{
public Command(Guid memberId)
{
Guard.NotNull(memberId, nameof(memberId));
MemberId = memberId;
}
public Guid MemberId { get; }
}
public class Handler : AsyncRequestHandler<Command>
{
private MemberContext _context;
public Handler(MemberContext context)
{
_context = context;
Console.WriteLine("Delete member context: " + context.MemberContextId);
}
protected override async Task HandleCore(Command cmd)
{
try
{
var member = await _context.FindAsync<Member>(cmd.MemberId);//.ConfigureAwait(false);
// if (member != null)
//// {
_context.Remove(member);
await _context.SaveChangesAsync().ConfigureAwait(false);
// }
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
}
As you can see there is no code that disposes that context. Scratching my head.
See this commented out check for member if null. That was throwing error as well, I commented it out just to see what will happen, and now it throws as SaveChangesAsync.
As request completes, context gets disposed. Since command handler uses SaveChangesAsync(), context is disposed before save completes. Culprit is controller method :). It should be async as well.
[HttpDelete("{id}")]
public async Task Delete(Guid id)
{
var command = new DeleteMember.Command(id);
await _mediator.Send(command).ConfigureAwait(false);
}
Your DbContext is scoped, meaning that Dependency Injection will return the same DbContext object every time one is asked for within the same HTTP request (in the case of ASP.NET).
That means that you should not be calling Dispose on your DbContext (otherwise that same object can't be used a second time). That seems to be what is happening to you, intentionally or not.
That does mean you should not be using using with it. Are you using using anywhere in your code against your DbContext?
I don't think you showed the line where the Exception is being thrown.
Update:
Try overriding Dispose in your MemberContext class. Something like this:
public override void Dispose() {
base.Dispose();
}
But just set a breakpoint there. When it breaks (if it does) check the stack trace and see what called it.
This can also be caused by having async void instead of async Task within WebAPI in my experience.
I have all the Nuget Bits for SignalR , I am trying to use my own clientIDs as well as the dependency Injection container that comes with SignalR for all my other repositories and such. Now the strange thing is this jQuery to connect to the hub fails on:
debugger;
// Proxy created on the fly
var chat = $.connection.chat;
Basically, the chat object becomes undefined as if SignalR cannot be resolved. This started happening once I tried to overide the default resolver for SignalR with the code below.
What am I missing here?
Another issue I am having is I am not sure if my UserClientIDfactory which implements IConnectionIdFactory
is working either.
Here is the MVC3 code in my Global.asax
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
RegisterServices(kernel);
return kernel;
}
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<UserIdClientIdFactory>()
.To<UserIdClientIdFactory>()
.InRequestScope();
//Rest of the other stuff to inject
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
//ninject calls to create the kernal etc
IKernel kernel = CreateKernel();
//TO DO using signal IR resolver
var resolver = new NinjectDependencyResolver(kernel);
SignalR.Hosting.AspNet.AspNetHost.SetResolver(resolver);
}
Finally, here is the code for my custom clientIDfactory
public class UserIdClientIdFactory : IConnectionIdFactory
{
#region IConnectionIdFactory Members
string IConnectionIdFactory.CreateConnectionId(SignalR.Hosting.IRequest request)
{
// get and return the UserId here, in my app it is stored
// in a custom IIdentity object, but you get the idea
return HttpContext.Current.User.Identity.Name != null ?
//TO DO change to get profileID from Appfabric or the database and log user infor
HttpContext.Current.User.Identity.Name.ToString() :
Guid.NewGuid().ToString();
}
#endregion
}
As I read your question you ask how to do proper dependency injection in ASP.NET MVC and SignalR using the same DI container (and hence only need to declare bindings in one place). If this is correct understood, I once wrote a blog post regarding this: http://lcdev.dk/2012/02/14/using-signalr-ninject-with-asp-net-mvc3-and-the-ninject-mvc3-nuget-package/
In the blog post I assume that you are using ASP.NET MVC3 as well as the Ninject.MVC3 and the SignalR.Ninject Nuget packages.
However, if this is not the case I do have a comment to your code. To me it seems like that the kernel used to make your bindings (in RegisterServices) is not the kernel you actually register with SignalR. And if this is the case, then of course SignalR won't know about your intended bindings and might throw an exception as result of your use of an un-instantiated object reference -> which then might explain why you no longer can connect to your SignalR hub.
ok thanks for the your post man, made me do some more digging , I read the rest of the post you linked about how to use Ninject with MVC3 which lead me to realize that I had ninject but not the Nuget Bits for Ninject Mvc3 , I added that and alos modifed my global.asax using the following post
http://www.planetgeek.ch/2010/11/13/official-ninject-mvc-extension-gets-support-for-mvc3/
here is the working code in gloabal.asax I also removed the bootstrapper that NinJect mvc3 added to the application start folder since that is how it works in the above post
public class MvcApplication : NinjectHttpApplication
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//routes.IgnoreRoute("{*allaxd}", new { allaxd = #".*\.axd(/.*)?" }); //added for mango chat
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
public override void Init()
{
this.AuthenticateRequest += new EventHandler(MvcApplication_AuthenticateRequest);
this.PostAuthenticateRequest += new EventHandler(MvcApplication_PostAuthenticateRequest);
base.Init();
}
#region "Ninject stuff for dependancy Injection
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
protected override IKernel CreateKernel()
{
var kernel = new StandardKernel();
// kernel.Load(Assembly.GetExecutingAssembly());
RegisterServices(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<UserIdClientIdFactory>()
.To<UserIdClientIdFactory>()
.InRequestScope();
SignalR.Hosting.AspNet.AspNetHost.DependencyResolver.Register(typeof(IConnectionIdFactory), () => new UserIdClientIdFactory());
}
#endregion
protected override void OnApplicationStarted()
{
base.OnApplicationStarted();
//for project awesome
ModelMetadataProviders.Current = new AwesomeModelMetadataProvider();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
}