NamedScopes、Ninject绑定和async(线程)

6
我的项目是由服务和存储库组成的结构(所有存储库共享db上下文)。在我的一个服务层中,我有一个异步方法,使用存储库将数据写入数据库。Web请求将完成并且在该方法可以使用之前处置上下文。我尝试理解NamedScopes,如此answer所述。我仍然无法理解如何实现它。我将展示我的项目结构,并希望有人能在代码层面上帮助我。 绑定
    private static void RegisterServices(IKernel kernel)
    {
        //dbcontext
        kernel.Bind<EntityDatabaseContext>().ToMethod(context => new EntityDatabaseContext()).InRequestScope();

        //unit of work
        kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();

        //repositories
        kernel.Bind<IRepository<Account>>().To<Repository<Account>>().InRequestScope();

        //services
        kernel.Bind<IAuthenticationService>().To<AuthenticationService>().InRequestScope();
    }

AuthenticationService使用构造函数注入

public AuthenticationService(UnitOfWork unitOfWork, IRepository<Account> accountRepository){}

我的 AuthenticationService 中的一个方法

    //this is a background process
    public Task SomeMethodAsync(string text)
    {
        //spin it off into a new task
        return Task.Factory.StartNew(() => SomeMethod(text));
    }

SomeMethod使用了accountRepository。如果需要更多信息,请告诉我。如果NamedScopes是解决方案,如何在我的情况下实现线程安全?

基本上,正在执行后台进程,并且它正在使用由ninject由于请求范围而被处理的上下文。


1
请问您想要实现什么?您是想异步地在SomeMethodAsync方法中加载一些数据,还是想以类似于作业的方式后台处理一些信息?您设计中的一个主要问题是,在SomeMethodAsync中直接启动了一个新任务。启动新任务并不能保证任务执行。任务可能会在您的请求结束后执行,此时ninject已经处于请求范围内。如果您使用AsyncController,则应该在其上注册任务。但API不应该启动它。 - Daniel Marbach
@DanielMarbach 这就是我的问题,任务可能会在请求结束后执行,而Ninject会处理上下文。我需要告诉Ninject等待,但不确定该如何实现。 - Shawn Mclean
关于启动:我理解错了。TPL指南规定您必须启动任务。所以那个没问题。在我看来,这个问题与ninject无关。您需要使用AsyncController并使用异步控制器执行异步操作。然后它应该可以工作。 - Daniel Marbach
1
你想要实现什么?你希望在任务启动后立即完成Web请求,还是希望请求等待任务的某些结果,以便你可以从该结果返回一些信息? - Remo Gloor
@RemoGloor 我不想从任务中返回任何内容,我认为这个名称是一个后台任务。我想在执行后立即结束网络请求。 - Shawn Mclean
显示剩余2条评论
1个回答

6

您应该意识到运行后台线程可能会导致许多问题。IIS可以随时决定回收应用程序池,这将立即终止您的线程(或在某些情况下根本不执行),使您的应用程序处于不一致的状态。

http://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx

实现异步操作的最简单且最不容易出错的方式是实现一个 Windows 服务,并将这些异步操作委托给 Windows 服务,例如使用 MSMQ。
如果您仍想采用较困难的方法,请阅读有关 HostingEnvironment.RegisterObject 和 IRegisteredObject 的内容,以避免出现这些不一致的情况。
Ninject 部分非常简单。只需创建一些作业处理器类,例如 MyJobProcessor,以执行任务所需的所有依赖项。它应该实现 INotifyWhenDisposed。最简单的方法是从 DisposeNotifyingObject 派生。
public class MyJobProcessor : DisposeNotifyingObject, IRegisteredObject
{
    public void Execute() { ... }
    public void Stop(bool immediate) { ... }
}

将此处理器注入到控制器中,让任务启动它并在完成工作后处理它。
Task.Factory.StartNew(() => 
    { 
        try 
        { 
            processor.Execute(); 
        } 
        finally 
        { 
            processor.Dispose); 
        }
    });

指定它的依赖范围。
Bind<MyJobProcessor>().ToSelf().Named("MyJobProcessor").DefinesNamedScope("MyJobProcessorScope");
Bind<IUnitOfWork>().To<UnitOfWork>().WhenAnyAnchestorNamed("MyJobProcessor").InNamedScope("MyJobProcessorScope");

谢谢,关于使用Windows服务,它是否适用于记录日志(用户登录,我记录它)或发送电子邮件(模板化)等情况?这些行为会使操作需要更长时间才能返回,因此我希望它们在后台任务中运行。 - Shawn Mclean
@Lolcoder 问题是你是否承受得起丢失这些数据。例如,如果服务器由于某些错误而被重置,你能否忍受没有日志的情况?或者你可以接受电子邮件未发送的情况吗?同时,记录和发送电子邮件通常不需要依赖关系。你还应该检查你实际上获得多少性能提升。创建和启动任务也需要一些时间。 - Remo Gloor
如果请求中的任何一个或池回收同步完成,它们也会失败,对吗? - solipsicle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接