Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/277.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 结合任务并行库使用实体框架_C#_Sql Server_Multithreading_Entity Framework_Task Parallel Library - Fatal编程技术网

C# 结合任务并行库使用实体框架

C# 结合任务并行库使用实体框架,c#,sql-server,multithreading,entity-framework,task-parallel-library,C#,Sql Server,Multithreading,Entity Framework,Task Parallel Library,我有一个应用程序,我们正在使用.NET4.0和EF6.0开发。这个程序的前提很简单。查看文件系统上的特定文件夹。将新文件放入此文件夹时,请在SQL Server数据库中查找有关此文件的信息(使用EF),然后根据找到的信息,将文件移动到文件系统上的另一个文件夹。文件移动完成后,返回数据库并更新有关此文件的信息(注册文件移动) 这些是大型媒体文件,因此可能需要一段时间才能移动到目标位置。此外,我们可能会启动此服务,因为源文件夹中已有数百个这样的媒体文件,需要发送到目标位置 为了加快速度,我开始使用任

我有一个应用程序,我们正在使用.NET4.0和EF6.0开发。这个程序的前提很简单。查看文件系统上的特定文件夹。将新文件放入此文件夹时,请在SQL Server数据库中查找有关此文件的信息(使用EF),然后根据找到的信息,将文件移动到文件系统上的另一个文件夹。文件移动完成后,返回数据库并更新有关此文件的信息(注册文件移动)

这些是大型媒体文件,因此可能需要一段时间才能移动到目标位置。此外,我们可能会启动此服务,因为源文件夹中已有数百个这样的媒体文件,需要发送到目标位置

为了加快速度,我开始使用任务并行库(async/await不可用,因为这是.NET4.0)。对于源文件夹中的每个文件,我在DB中查找有关它的信息,确定它需要移动到哪个目标文件夹,然后启动一个新任务,开始移动该文件

LookupFileinfoinDB(filename)
{
  // use EF DB Context to look up file in DB
}

// start a new task to begin the file move
var moveFileTask = Task<bool>.Factory.StartNew(
                () =>
                    {
                        var success = false;

                        try
                        {
                         // the code to actually moves the file goes here…
                         .......
                         }
                      }
问题是,我使用相同的DB上下文在主任务中以及稍后在嵌套任务上执行的RegisterFileMoveInDB()方法中查找文件信息。在移动多个文件时,我遇到了各种奇怪的异常(主要是关于SQL server Data reader等)。在线搜索答案发现,像我在这里做的几个任务之间共享DB上下文是一个很大的禁忌,因为EF不是线程安全的


我不希望为每个文件移动创建一个新的DB上下文,因为可能有几十个甚至数百个文件同时移动。什么是好的替代方法?当嵌套任务完成并在主任务中完成文件移动注册时,是否有方法“通知”主任务?还是我以错误的方式处理这个问题,并且有更好的方法来解决这个问题?

您最好的选择是为每个线程定义
DbContext
Parallel.ForEach
具有对此有用的重载(带有
Func initLocal
的重载:

Parallel.ForEach( 
    fileNames, // the filenames IEnumerable<string> to be processed
    () => new YourDbContext(), // Func<TLocal> localInit
    ( fileName, parallelLoopState, dbContext ) => // body
    {
        // your logic goes here
        // LookUpFileInfoInDB( dbContext, fileName )
        // MoveFile( ... )
        // RegisterFileMoveInDB( dbContext, ... )

        // pass dbContext along to the next iteration
        return dbContext;
    }
    ( dbContext ) => // Action<TLocal> localFinally
    {
        dbContext.SaveChanges(); // single SaveChanges call for each thread
        dbContext.Dispose();
    } );
Parallel.ForEach(
filename,//要处理的文件名IEnumerable
()=>新建YourDbContext(),//Func localini
(文件名,parallelLoopState,dbContext)=>//正文
{
//你的逻辑是这样的
//LookUpFileInfoInDB(dbContext,文件名)
//移动文件(…)
//RegisterFileMoveInDB(dbContext,…)
//将dbContext传递给下一个迭代
返回dbContext;
}
(dbContext)=>//操作localFinally
{
dbContext.SaveChanges();//为每个线程调用单个SaveChanges
dbContext.Dispose();
} );

您可以调用
SaveChanges()
在主体表达式/RegisterFileMoveInDB中,如果您希望尽快更新数据库。我建议将文件系统操作与数据库事务绑定,以便在数据库更新失败时回滚文件系统操作。

您的最佳选择是为每个线程确定
DbContext
的范围。
Parallel.ForEach
具有对此有用的重载(带有
Func initLocal的重载

Parallel.ForEach( 
    fileNames, // the filenames IEnumerable<string> to be processed
    () => new YourDbContext(), // Func<TLocal> localInit
    ( fileName, parallelLoopState, dbContext ) => // body
    {
        // your logic goes here
        // LookUpFileInfoInDB( dbContext, fileName )
        // MoveFile( ... )
        // RegisterFileMoveInDB( dbContext, ... )

        // pass dbContext along to the next iteration
        return dbContext;
    }
    ( dbContext ) => // Action<TLocal> localFinally
    {
        dbContext.SaveChanges(); // single SaveChanges call for each thread
        dbContext.Dispose();
    } );
Parallel.ForEach(
filename,//要处理的文件名IEnumerable
()=>新建YourDbContext(),//Func localini
(文件名,parallelLoopState,dbContext)=>//正文
{
//你的逻辑是这样的
//LookUpFileInfoInDB(dbContext,文件名)
//移动文件(…)
//RegisterFileMoveInDB(dbContext,…)
//将dbContext传递给下一个迭代
返回dbContext;
}
(dbContext)=>//操作localFinally
{
dbContext.SaveChanges();//为每个线程调用单个SaveChanges
dbContext.Dispose();
} );

如果希望尽快更新数据库,可以在主体表达式/RegisterFileMoveInDB中调用
SaveChanges()
。我建议将文件系统操作与数据库事务绑定,以便在数据库更新失败时回滚文件系统操作。

根据@Moho问题:

  • 内置
    IO异步
    操作中的线程取自 NET运行时CLR的线程池,因此它是一种非常有效的机制 你自己创建线程,你用旧的方式创建线程 效率低下,尤其是对于IO操作

  • 当您调用
    async
    时,您不必立即等待。将等待推迟到必要时


  • 问候。

    根据@Moho问题:

  • 内置
    IO异步
    操作中的线程取自 NET运行时CLR的线程池,因此它是一种非常有效的机制 你自己创建线程,你用旧的方式创建线程 效率低下,尤其是对于IO操作

  • 当您调用
    async
    时,您不必立即等待。将等待推迟到必要时

  • 致以最诚挚的问候。

    您还可以将实例的作为的参数传递。这样,continuations将按顺序运行,而不是同时运行

    TaskScheduler exclusiveScheduler
        = new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler;
    
    //...
    
    filemoveTask.ContinueWith(t => 
    {
        if (t.Result)
        {
            RegisterFileMoveinDB();
        }
    }, exclusiveScheduler);
    
    您还可以将实例的作为的参数传递。这样,continuations将按顺序运行,而不是同时运行

    TaskScheduler exclusiveScheduler
        = new ConcurrentExclusiveSchedulerPair().ExclusiveScheduler;
    
    //...
    
    filemoveTask.ContinueWith(t => 
    {
        if (t.Result)
        {
            RegisterFileMoveinDB();
        }
    }, exclusiveScheduler);
    

    我只想在RegisterFileMoveinDB和LookupFileinfoinDB中定义单独的DbContext对象。您使用的是外部资源(文件系统、数据库)-因此
    async wait
    比“浪费”更适合您的情况用于IO操作的线程。您可以在.NET 4.0中使用
    async wait
    。@Fabio-线程是如何被浪费的?当您调用可等待的
    xyzAsync(…)
    方法时,您认为会发生什么?@Moho,执行IO操作的线程不做任何事情-只等待