Reputation: 337
I am working in .Net framework 4.7.2. I am facing a problem while using NInject dependency injection resolver. I am getting the following error when trying to access NotificationController, which has no parameterless constructor:
"Message": "An error has occurred.",
"ExceptionMessage": "An error occurred when trying to create a controller of type 'NotificationController'. Make sure that the controller has a parameterless public constructor.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": " at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\r\n at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__15.MoveNext()",
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "Type 'ABC.API.Controllers.NotificationController' does not have a default constructor",
"ExceptionType": "System.ArgumentException",
"StackTrace": " at System.Linq.Expressions.Expression.New(Type type)\r\n at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"
Here is my complete code:
INotificationService:
using System.Threading;
using System.Threading.Tasks;
using ABC.API.Models;
namespace ABC.API.Services
{
public interface INotificationService
{
Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token);
Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token);
Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token);
}
}
NotificationHubService implementing INotificationService:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.NotificationHubs;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using ABC.API.Models;
namespace ABC.API.Services
{
public class NotificationHubService : INotificationService
{
readonly NotificationHubClient _hub;
readonly Dictionary<string, NotificationPlatform> _installationPlatform;
readonly ILogger<NotificationHubService> _logger;
public NotificationHubService(IOptions<NotificationHubOptions> options, ILogger<NotificationHubService> logger)
{
_logger = logger;
_hub = NotificationHubClient.CreateClientFromConnectionString(
options.Value.ConnectionString,
options.Value.Name);
_installationPlatform = new Dictionary<string, NotificationPlatform>
{
{ nameof(NotificationPlatform.Apns).ToLower(), NotificationPlatform.Apns },
{ nameof(NotificationPlatform.Fcm).ToLower(), NotificationPlatform.Fcm }
};
}
public async Task<bool> CreateOrUpdateInstallationAsync(DeviceInstallation deviceInstallation, CancellationToken token)
{
if (string.IsNullOrWhiteSpace(deviceInstallation?.InstallationId) ||
string.IsNullOrWhiteSpace(deviceInstallation?.Platform) ||
string.IsNullOrWhiteSpace(deviceInstallation?.PushChannel))
return false;
var installation = new Installation()
{
InstallationId = deviceInstallation.InstallationId,
PushChannel = deviceInstallation.PushChannel,
Tags = deviceInstallation.Tags
};
if (_installationPlatform.TryGetValue(deviceInstallation.Platform, out var platform))
installation.Platform = platform;
else
return false;
try
{
await _hub.CreateOrUpdateInstallationAsync(installation, token);
}
catch
{
return false;
}
return true;
}
public async Task<bool> DeleteInstallationByIdAsync(string installationId, CancellationToken token)
{
if (string.IsNullOrWhiteSpace(installationId))
return false;
try
{
await _hub.DeleteInstallationAsync(installationId, token);
}
catch
{
return false;
}
return true;
}
public async Task<bool> RequestNotificationAsync(NotificationRequest notificationRequest, CancellationToken token)
{
if ((notificationRequest.Silent &&
string.IsNullOrWhiteSpace(notificationRequest?.Action)) ||
(!notificationRequest.Silent &&
(string.IsNullOrWhiteSpace(notificationRequest?.Text)) ||
string.IsNullOrWhiteSpace(notificationRequest?.Action)))
return false;
var androidPushTemplate = notificationRequest.Silent ?
PushTemplates.Silent.Android :
PushTemplates.Generic.Android;
var iOSPushTemplate = notificationRequest.Silent ?
PushTemplates.Silent.iOS :
PushTemplates.Generic.iOS;
var androidPayload = PrepareNotificationPayload(
androidPushTemplate,
notificationRequest.Text,
notificationRequest.Action);
var iOSPayload = PrepareNotificationPayload(
iOSPushTemplate,
notificationRequest.Text,
notificationRequest.Action);
try
{
if (notificationRequest.Tags.Length == 0)
{
// This will broadcast to all users registered in the notification hub
await SendPlatformNotificationsAsync(androidPayload, iOSPayload, token);
}
else if (notificationRequest.Tags.Length <= 20)
{
await SendPlatformNotificationsAsync(androidPayload, iOSPayload, notificationRequest.Tags, token);
}
else
{
var notificationTasks = notificationRequest.Tags
.Select((value, index) => (value, index))
.GroupBy(g => g.index / 20, i => i.value)
.Select(tags => SendPlatformNotificationsAsync(androidPayload, iOSPayload, tags, token));
await Task.WhenAll(notificationTasks);
}
return true;
}
catch (Exception e)
{
_logger.LogError(e, "Unexpected error sending notification");
return false;
}
}
string PrepareNotificationPayload(string template, string text, string action) => template
.Replace("$(alertMessage)", text)
.Replace("$(alertAction)", action);
Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, CancellationToken token)
{
var sendTasks = new Task[]
{
_hub.SendFcmNativeNotificationAsync(androidPayload, token),
_hub.SendAppleNativeNotificationAsync(iOSPayload, token)
};
return Task.WhenAll(sendTasks);
}
Task SendPlatformNotificationsAsync(string androidPayload, string iOSPayload, IEnumerable<string> tags, CancellationToken token)
{
var sendTasks = new Task[]
{
_hub.SendFcmNativeNotificationAsync(androidPayload, tags, token),
_hub.SendAppleNativeNotificationAsync(iOSPayload, tags, token)
};
return Task.WhenAll(sendTasks);
}
}
}
NotificationController which contains INotificationService parameter in constructure
using ABC.API.Models;
using ABC.API.Services;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
namespace ABC.API.Controllers
{
public class NotificationController : ApiController
{
readonly INotificationService _notificationService;
public NotificationController(INotificationService notificationService)
{
_notificationService = notificationService;
}
[HttpPut]
[Route("installations")]
//[ProducesResponseType((int)HttpStatusCode.OK)]
//[ProducesResponseType((int)HttpStatusCode.BadRequest)]
//[ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
public async Task<IHttpActionResult> UpdateInstallation(
[Required] DeviceInstallation deviceInstallation)
{
var success = await _notificationService
.CreateOrUpdateInstallationAsync(deviceInstallation, HttpContext.Current.Request.TimedOutToken);
if (!success)
return NotFound();
return Ok();
}
[HttpDelete()]
[Route("installations/{installationId}")]
//[ProducesResponseType((int)HttpStatusCode.OK)]
//[ProducesResponseType((int)HttpStatusCode.BadRequest)]
//[ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
public async Task<IHttpActionResult> DeleteInstallation(
[Required][FromBody] string installationId)
{
// Probably want to ensure deletion even if the connection is broken
var success = await _notificationService
.DeleteInstallationByIdAsync(installationId, CancellationToken.None);
if (!success)
return NotFound();
return Ok();
}
[HttpPost]
[Route("requests")]
//[ProducesResponseType((int)HttpStatusCode.OK)]
//[ProducesResponseType((int)HttpStatusCode.BadRequest)]
//[ProducesResponseType((int)HttpStatusCode.UnprocessableEntity)]
public async Task<IHttpActionResult> RequestPush(
[Required] NotificationRequest notificationRequest)
{
if ((notificationRequest.Silent &&
string.IsNullOrWhiteSpace(notificationRequest?.Action)) ||
(!notificationRequest.Silent &&
string.IsNullOrWhiteSpace(notificationRequest?.Text)))
return BadRequest();
var success = await _notificationService
.RequestNotificationAsync(notificationRequest, HttpContext.Current.Request.TimedOutToken);
if (!success)
return NotFound();
return Ok();
}
}
}
And here is NotificationHubOptions which is declared as constructor parameter in NotificationHubService.
using System.ComponentModel.DataAnnotations;
namespace ABC.API.Models
{
public class NotificationHubOptions
{
[Required]
public string Name { get; set; }
[Required]
public string ConnectionString { get; set; }
}
}
And then I am doing this using NInject library:
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
NotificationHubOptions options = new NotificationHubOptions();
options.ConnectionString = ConfigurationManager.AppSettings["DefaultFullSharedAccessSignature"].ToString();
options.Name = ConfigurationManager.AppSettings["NotificationHubName"].ToString();
kernel.Bind<INotificationService>().To<NotificationHubService>().InSingletonScope()
.WithConstructorArgument<NotificationHubOptions>(options);
}
I had followed these links https://www.dotnettricks.com/learn/webapi/dependency-interjection-in-aspnet-web-api and https://github.com/ninject/Ninject/issues/270 for NIjnect. For Push Notification Service, I followed this article https://learn.microsoft.com/en-us/azure/developer/mobile-apps/notification-hubs-backend-service-xamarin-forms#authenticate-clients-using-an-api-key-optional. In Push notification article, they had used .Net core and they had injected the dependency through ConfigureService as below:
services.AddSingleton<INotificationService, NotificationHubService>();
services.AddOptions<NotificationHubOptions>()
.Configure(Configuration.GetSection("NotificationHub").Bind)
.ValidateDataAnnotations();
I could not understand what I am missing. Please also suggest how to inject ILogger to NotificationHubService constructor.
Upvotes: 0
Views: 665