c# - 尝试创建类型为“xxx”的控制器时发生错误。确保控制器有一个无参数的公共构造函数。忍者
问题描述
我在 .Net 框架 4.7.2 中工作。我在使用 NInject 依赖注入解析器时遇到问题。尝试访问没有无参数构造函数的 NotificationController 时出现以下错误:
"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)"
这是我的完整代码:
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 实现 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 在构造中包含 INotificationService 参数
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();
}
}
}
这里是 NotificationHubOptions,它在 NotificationHubService 中声明为构造函数参数。
using System.ComponentModel.DataAnnotations;
namespace ABC.API.Models
{
public class NotificationHubOptions
{
[Required]
public string Name { get; set; }
[Required]
public string ConnectionString { get; set; }
}
}
然后我使用 NInject 库执行此操作:
/// <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);
}
对于 NIjnect ,我已经关注了这些链接https://www.dotnettricks.com/learn/webapi/dependency-interjection-in-aspnet-web-api和https://github.com/ninject/Ninject/issues/270 。对于推送通知服务,我关注了这篇文章https://docs.microsoft.com/en-us/azure/developer/mobile-apps/notification-hubs-backend-service-xamarin-forms#authenticate-clients-using-an -api-key-可选。在推送通知文章中,他们使用了 .Net 核心,并通过 ConfigureService 注入了依赖项,如下所示:
services.AddSingleton<INotificationService, NotificationHubService>();
services.AddOptions<NotificationHubOptions>()
.Configure(Configuration.GetSection("NotificationHub").Bind)
.ValidateDataAnnotations();
我无法理解我错过了什么。还请建议如何将 ILogger 注入 NotificationHubService 构造函数。
解决方案
推荐阅读
- c# - Xamarin Forms - 使用 LabelSizeFontToFit 效果更改按钮上的文本使文本越来越小
- azure - 运行 kusto 查询以查找超过 24 小时未在 ATP 中报告的服务器不会提供正确的信息
- c++ - 是否有使用 boost::numeric_cast 的目的
(长)(即,从长转换为双)? - logging - 如何生成多行日志输出以强调例如更大动作的开始和结束
- powershell - 在 PowerShell 中过滤对象条目
- sql-server - SQL Server 动态使用子句
- python - 无法在 Anaconda 终端中打开 jupyter 笔记本
- python-3.x - 如何在 Flask SQLAlchemy 中设置多个多对多关系
- r - 为变量的每个值添加一个单词作为前缀
- java - 未能执行目标 org.codehaus.mojo:exec-maven-plugin:1.2.1:exec (default-cli) Netbeans