Dependency injection .Net核心:自定义范围为;“范围”;依赖注入w.out。控制器

Dependency injection .Net核心:自定义范围为;“范围”;依赖注入w.out。控制器,dependency-injection,scope,.net-core,message-queue,amqp,Dependency Injection,Scope,.net Core,Message Queue,Amqp,我有一个应用程序,它不通过控制器接收普通HTTP请求,而是侦听和接收消息(AMQP协议),以启动其逻辑流 我的应用程序一次可以接收和处理多条消息。我有一个对象,它将在整个过程中收集多个不同服务/类中的信息/数据,以便我最终使用它。 但是我需要将每个接收到的消息的数据分开,因为“作用域”注入会将注入的实例与其他HTTP请求分开 因此,我的用例与我在普通API中使用作用域注入对象的方式非常相似,但我在侦听器中接收到一条消息,而不是一个新的HTTP请求 是否有任何方法可以为接收到的每条消息创建一个自定

我有一个应用程序,它不通过控制器接收普通HTTP请求,而是侦听和接收消息(AMQP协议),以启动其逻辑流

我的应用程序一次可以接收和处理多条消息。我有一个对象,它将在整个过程中收集多个不同服务/类中的信息/数据,以便我最终使用它。 但是我需要将每个接收到的消息的数据分开,因为“作用域”注入会将注入的实例与其他HTTP请求分开

因此,我的用例与我在普通API中使用作用域注入对象的方式非常相似,但我在侦听器中接收到一条消息,而不是一个新的HTTP请求

是否有任何方法可以为接收到的每条消息创建一个自定义作用域,或者通过某种配置,或者让代码在Listener.MessageReceived(message message)方法中首先创建一个新的作用域

想象一下这样的流动:

public class Listener {
    ServiceClassA serviceClassA //injected in constructor
    CustomLogger customLogger // (HAS TO BE SAME OBJECT INJECTED INTO ServiceClassA, ServiceClassB and Listener)

    public void ReceiveMessage(Message message) {
        using (var scope = CreateNewScope()) {
            try {
                serviceClassA.DoStuff();
            } catch(Exception e) {
                Console.Write(customLogger.GetLogs())
            }
        }
    }
}


public class ServiceClassA {
    ServiceClassB serviceClassB //injected in constructor
    CustomLogger customLogger //(HAS TO BE SAME OBJECT INJECTED INTO ServiceClassA, ServiceClassB and Listener)

    public void DoStuff() {
        customLogger = ResolveCustomLogger(); // how do I make sure I can get/resolve the same object as in Listener (without having to pass parameters)
        var data = // does stuff
        customLogger.Log(data);

        serviceClassB.DoStuff();
    }
}


public class ServiceClassB {
    CustomLogger customLogger //(HAS TO BE SAME OBJECT INJECTED INTO ServiceClassA, ServiceClassB and Listener)

    public void DoStuff() {
        customLogger = ResolveCustomLogger(); // how do I make sure I can get/resolve the same object as in Listener (without having to pass parameters)
        var data = // does other stuff
        customLogger.Log(data);
    }
}
我的CustomLogger可能不仅在下面的1或2个服务层中使用,可能有很多层,我可能只想在底部使用CustomLogger,但我希望在以后的顶层可以访问它,以检索存储在其中的数据


非常感谢。

您可以在类中插入一个ServiceScopyFactory,该类对来自队列的消息做出反应,然后它可以为收到的每一条消息创建一个作用域,从中请求MessageHandler依赖项

下面的代码示例正是这样做的(它还处理队列上的会话,但这对创建作用域没有影响)

公共类SessionHandler:ISessionHandler
{
公共只读字符串SessionId;
专用只读ILogger记录器;
专用readonly IServiceScopeFactory服务ScopeFactory;
只读会话状态会话状态;
公共会话处理程序(
ILogger记录器,
IServiceScopeFactory服务ScopeFactory,
字符串会话ID)
{
记录器=记录器;
ServiceScopeFactory=ServiceScopeFactory;
SessionId=SessionId
SessionState=新SessionState();
}
公共异步任务HandleMessage(IMessageSession会话、消息消息、CancellationToken CancellationToken)
{
Logger.LogInformation($“已接收{Message.Body.Length}字节的消息”);
//反序列化消息
bool反序列化成功=TryDeserializeMessageBody(message.Body,out var incomingMessage);
如果(!反序列化成功)
抛出新的NotImplementedException();//是否移动到死信队列?
//发送消息
bool handlingSuccess=使用ScopedHandler等待HandleMessage(incomingMessage,cancellationToken);
如果(!handlingSuccess)
抛出新的NotImplementedException();//是否移动到死信队列?
}
/// 
///实例化一个消息处理程序,其服务范围将持续到消息处理完成为止。
/// 
私有异步任务HandleMessageWithScopedHandler(IncomingMessage IncomingMessage,CancellationToken CancellationToken)
{
尝试
{
使用IServiceScope messageHandlerScope=ServiceScopeFactory.CreateScope();
var messageHandlerFactory=messageHandlerScope.ServiceProvider.GetRequiredService();
var messageHandler=messageHandlerFactory.Create(SessionState);
Wait messageHandler.HandleMessage(incomingMessage,cancellationToken);
返回true;
}
捕获(异常)
{
Logger.LogError(异常,$“处理消息时发生异常:{exception.message}”);
返回false;
}
}
私有布尔TryDeserializeMessageBody(字节[]body,输出输入消息?输入消息)
{
incomingMessage=null;
尝试
{
incomingMessage=incomingMessage.Deserialize(正文);
返回true;
}
捕获(MessageDeserializationException异常)
{
Logger.LogError(异常、异常、消息);
}
返回false;
}
}
现在,每当实例化MessageHandlerFactory时(对于从队列接收的每条消息都会发生这种情况),工厂请求的任何作用域依赖项都将一直存在,直到MessageHandler.HandleMessage()任务完成


我创建了一个消息处理程序工厂,以便SessionHandler可以将非DI服务参数传递给MessageHandler的构造函数(本例中为SessionState对象),以及DI服务。工厂请求(作用域)依赖项并将其传递给MessageHandler。如果您不使用会话,那么您可能不需要工厂,您可以直接从作用域获取MessageHandler。

您读过吗?@Steven我见过使用using子句创建作用域的示例,但在我看来,创建同一对象的另一个实例需要我传递这个“作用域对象”对于该方法,以创建具有相同范围的所需对象。这并不比首先传递我的对象更好,我宁愿避免。我将用一个例子来扩展我的原始帖子。这里的技巧是不要将
serviceClassA
注入
Listener
,而是从
范围
中解析它,为了让
CustomLogger
注册为
Singleton
@Steven,我如何确保我在serviceClassA中解析的CustomLogger与调用serviceClassA方法的侦听器方法中解析的对象相同(不传递scope对象)?将
CustomLogger
注册为Singleton。
public class SessionHandler : ISessionHandler
{
    public readonly string SessionId;
    private readonly ILogger<SessionHandler> Logger;
    private readonly IServiceScopeFactory ServiceScopeFactory;

    readonly SessionState SessionState;

    public SessionHandler(
        ILogger<SessionHandler> logger,
        IServiceScopeFactory serviceScopeFactory,
        string sessionId)
    {
        Logger = logger;
        ServiceScopeFactory = serviceScopeFactory;
        SessionId = sessionId
        SessionState = new SessionState();
    }

    public async Task HandleMessage(IMessageSession session, Message message, CancellationToken cancellationToken)
    {
        Logger.LogInformation($"Message of {message.Body.Length} bytes received.");


        // Deserialize message
        bool deserializationSuccess = TryDeserializeMessageBody(message.Body, out var incomingMessage);

        if (!deserializationSuccess)
            throw new NotImplementedException(); // Move to deadletter queue?


        // Dispatch message
        bool handlingSuccess = await HandleMessageWithScopedHandler(incomingMessage, cancellationToken);

        if (!handlingSuccess)
            throw new NotImplementedException(); // Move to deadletter queue?
    }

    /// <summary>
    /// Instantiate a message handler with a service scope that lasts until the message handling is done.
    /// </summary>
    private async Task<bool> HandleMessageWithScopedHandler(IncomingMessage incomingMessage, CancellationToken cancellationToken)
    {
        try
        {
            using IServiceScope messageHandlerScope = ServiceScopeFactory.CreateScope();
            var messageHandlerFactory = messageHandlerScope.ServiceProvider.GetRequiredService<IMessageHandlerFactory>();
            var messageHandler = messageHandlerFactory.Create(SessionState);

            await messageHandler.HandleMessage(incomingMessage, cancellationToken);

            return true;
        }
        catch (Exception exception)
        {
            Logger.LogError(exception, $"An exception occurred when handling a message: {exception.Message}.");
            return false;
        }
    }

    private bool TryDeserializeMessageBody(byte[] body, out IncomingMessage? incomingMessage)
    {
        incomingMessage = null;

        try
        {
            incomingMessage = IncomingMessage.Deserialize(body);
            return true;
        }
        catch (MessageDeserializationException exception)
        {
            Logger.LogError(exception, exception.Message);    
        }

        return false;
    }
}