Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.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# 直接从数据库返回IEnumerable或之前使用ToListSync_C#_Asp.net Core - Fatal编程技术网

C# 直接从数据库返回IEnumerable或之前使用ToListSync

C# 直接从数据库返回IEnumerable或之前使用ToListSync,c#,asp.net-core,C#,Asp.net Core,当直接从数据库中为控制器提供IEnumerable时,控制器是如何工作的?哪种代码更正确、更优化?让我们假设数据库非常慢,并且还有其他操作正在进行 这个例子非常简单,所以在执行时间上可能没有足够的差异,但我正在尝试学习最佳实践 #1 公共任务查找(表达式谓词) { 返回DatabaseContext.Applications .Where(谓词) .ToArrayAsync(); } ... 公共任务查找(…) { 返回ApplicationService.Find(…); } #2 公共任务

当直接从数据库中为控制器提供IEnumerable时,控制器是如何工作的?哪种代码更正确、更优化?让我们假设数据库非常慢,并且还有其他操作正在进行

这个例子非常简单,所以在执行时间上可能没有足够的差异,但我正在尝试学习最佳实践

#1
公共任务查找(表达式谓词)
{
返回DatabaseContext.Applications
.Where(谓词)
.ToArrayAsync();
}
...
公共任务查找(…)
{
返回ApplicationService.Find(…);
}
#2
公共任务查找(表达式谓词)
{
返回DatabaseContext.Applications
.Where(谓词)
.ToListAsync();
}
...
公共异步任务查找(…)
{
var applications=wait ApplicationService.Find(…)
返回Ok(应用程序);
}
#3
public IEnumerable Find(表达式谓词)
{
返回DatabaseContext.Applications;
}
...
公共IActionResult查找(…)
{
var applications=ApplicationService.Find(…);
返回Ok(应用程序);
}
  • 您返回的任务将由mvc框架执行
  • 您正在运行(异步)一个任务,等待它,然后得到结果并将其交给mvc框架
  • 您将返回一个由mvc框架执行的枚举数
  • 我会选择选项#2,因为您确切地知道数据库查询将在何时执行。由于返回任务并正确使用async和Wait关键字,框架将尽可能多地保持线程繁忙,从而利用应用程序的吞吐量

    当直接从数据库中为控制器提供IEnumerable时,控制器是如何工作的

    (所谓
    IEnumerable
    我假定您的意思是直接从
    DbContext
    返回未执行的
    IQueryable

    它们不表示,您也不应该这样做——这是因为未执行的
    IQueryable
    不表示加载的数据——执行时只能从打开的数据库连接加载数据——这需要一个活动且有效的
    DbContext

    …因此,如果处置了
    DbContext
    ,则无法执行
    IQueryable

    如果在控制器操作中创建
    DbContext
    ,并在视图中呈现
    IQueryable
    ,或在
    ObjectResponse
    中返回它(对于Web API),则它将始终失败:

    public IActionResult GetPeople()
    {
        // WARNING: NEVER DO THIS!
        using( MyDbContext db = new MyDbContext( GetConnectionString() ) )
        {
            return this.Ok( db.People.Where( p => p.Name == "John Smith" ) );
    
            // or:
    
            return this.View( model: db.People.Where( p => p.Name == "John Smith" ) );
        }
    }
    
    请记住,
    .Ok()
    this.View()
    不会触发对视图的求值或向客户端发送对象响应,而是会导致控制器操作首先结束,然后将数据传递到ASP.NET管道中的下一步(即视图)。请记住:视图是在控制器操作完成后执行的

    如果您使用依赖项注入在控制器中拥有
    DbContext
    的就绪实例,则结果不太可预测:在操作方法返回后仍然可以评估
    IQueryable
    ,因为
    DbContext
    直到控制器被释放后才会被释放,这通常是在视图被呈现,但是您仍然不应该这样做,因为您的
    IQueryable
    仍可能被传递到某个超出控制器类生命周期的进程,这将导致失败。您还应该避免这种情况,因为视图是为了快速同步地呈现而设计的——使用外部数据库或IO调用会破坏这种设计

    (无论如何,您不应该使用实体框架实体对象作为根视图模型,但这是另一个讨论)

    如果始终在
    DbContext
    上使用
    async
    操作(例如
    ToListAsync()),则可以避免此习惯
    ToDictionaryAsync
    等-因为它们分别返回
    任务
    任务字典>
    -这需要
    等待
    ,编译器默认情况下会阻止您在视图或对象结果中执行该操作(您可以在视图中设置
    wait
    ,但这是不可取的,需要在某处设置一些设置)

    简而言之,始终要这样做:

    public async Task<IActionResult> GetPeople()
    {
        using( MyDbContext db = new MyDbContext( GetConnectionString() ) )
        {
            List<Person> list = await db.People
                .Where( p => p.Name == "John Smith" )
                .ToListAsync();
    
            // WebAPI:
            return this.Ok( list ); // returning an evaluated list, loaded into memory. (Make sure Lazy Navigation Properties are disabled too)
    
            // MVC:
            PeopleListViewModel vm = new PeopleListViewModel(); // in MVC always use a custom class for root view-models so you're not accepting nor returning Entity Framework entity types directly
            vm.List = list;
    
            return this.View( vm );
        }
    }
    
    public异步任务GetPeople()
    {
    使用(MyDbContext db=newmydbcontext(GetConnectionString()))
    {
    List=等待db.People
    .Where(p=>p.Name==“约翰·史密斯”)
    .ToListAsync();
    //WebAPI:
    返回此。确定(列表);//返回已计算的列表,并加载到内存中。(确保禁用了惰性导航属性)
    //MVC:
    PeopleListViewModel vm=new PeopleListViewModel();//在MVC中,始终为根视图模型使用自定义类,这样您就不会直接接受或返回实体框架实体类型
    vm.List=List;
    返回此.View(vm);
    }
    }
    
    正是我所希望的。我只是想控制器会锁定线程,以执行查询阻塞整个API几毫秒(或不)当我可以异步执行时..无论如何,我自己这样做似乎是最好的选择。感谢您的解释。我的意思是,i/O操作不是异步的,在等待整个数据库响应时不会让其他操作完成。@MateuszSurma ASP.NET默认情况下为每个请求/响应使用专用线程(我认为ASP.NETCore也是如此)-因此,在整个线程池耗尽之前,一个请求不会阻止另一个请求。
    public IEnumerable<Application> Find(Expression<Func<Application, bool>> predicate)
    {
        return DatabaseContext.Applications;
    }
    
    ...
    
    public IActionResult<IEnumerable<Application>> Find(...)
    {
        var applications = ApplicationService.Find(...);
        return Ok(applications);
    }
    
    public IActionResult GetPeople()
    {
        // WARNING: NEVER DO THIS!
        using( MyDbContext db = new MyDbContext( GetConnectionString() ) )
        {
            return this.Ok( db.People.Where( p => p.Name == "John Smith" ) );
    
            // or:
    
            return this.View( model: db.People.Where( p => p.Name == "John Smith" ) );
        }
    }
    
    public async Task<IActionResult> GetPeople()
    {
        using( MyDbContext db = new MyDbContext( GetConnectionString() ) )
        {
            List<Person> list = await db.People
                .Where( p => p.Name == "John Smith" )
                .ToListAsync();
    
            // WebAPI:
            return this.Ok( list ); // returning an evaluated list, loaded into memory. (Make sure Lazy Navigation Properties are disabled too)
    
            // MVC:
            PeopleListViewModel vm = new PeopleListViewModel(); // in MVC always use a custom class for root view-models so you're not accepting nor returning Entity Framework entity types directly
            vm.List = list;
    
            return this.View( vm );
        }
    }