Var*_*hra 1 c# linq asp.net-mvc linq-to-sql deferred-execution
目前的项目,突破了这个问题:
客户端存储库:
public class ClientRepository
{
// Members
private masterDataContext _db;
// Constructor
public ClientRepository()
{
_db = new masterDataContext();
}
public IEnumerable<ClientName> GetCorporateClientNames()
{
return _db.corporate_client_tbs.Select(o => new ClientName { id = o.id, name = o.company_name }).AsEnumerable();
}
public IEnumerable<ClientName> GetRetailClientNames()
{
return _db.retail_client_tbs.Select(o => new ClientName { id = o.id, name = o.name }).AsEnumerable();
}
// Define return type
public class ClientName
{
public int id { get; set; }
public string name { get; set; }
}
}
Run Code Online (Sandbox Code Playgroud)
现在在控制器中我有以下内容:
public ActionResult Index()
{
var _visits = _db.GetAllServiceVisits();
return View(_visits);
}
Run Code Online (Sandbox Code Playgroud)
使用当前存在的200个奇数行加载视图大约需要4秒钟.
我想在访问模型中添加一个属性"client",其中包含客户端的名称.客户端的名称将来自两个不同的表中的一个,这两个表是从"ClientName"类型的两个数组之一中获取的.
这是第一种使用LINQ的方法:
public ActionResult Index()
{
private ClientRepository _cr = new ClientRepository();
var _retailclients = _cr.GetRetailClientNames().ToArray();
var _corporateclients = _cr.GetCorporateClientNames().ToArray();
var _visits = _db.GetAllServiceVisits();
var _temp = _visits.Select(o => new ServiceVisitViewModel
{
service_visit = o,
client = (o.client_type ? _corporateclients.Where(p => p.id == o.client_id).First().name : _retailclients.Where(p => p.id == o.client_id).First().name)
}).ToArray();
return View(_temp);
}
Run Code Online (Sandbox Code Playgroud)
这是第二种方法,使用普通的'ol C#:
public ActionResult Index()
{
private ClientRepository _cr = new ClientRepository();
var _retailclients = _cr.GetRetailClientNames().ToArray();
var _corporateclients = _cr.GetCorporateClientNames().ToArray();
var _visits = _db.GetAllServiceVisits();
List<ServiceVisitViewModel> _temp = new List<ServiceVisitViewModel>();
foreach (service_visit_tb v in _visits)
{
_temp.Add(new ServiceVisitViewModel { service_visit = v, client = (v.client_type ? _corporateclients.Where(p => p.id == v.client_id).First().name : _retailclients.Where(p => p.id == v.client_id).First().name) });
}
return View(_temp);
}
Run Code Online (Sandbox Code Playgroud)
根据我的测试,第二种方法快8到10倍.
我能看到的唯一区别是.Select语句.
有人可以告诉我,如果我在第一种方法或替代方案中做错了什么,为什么第一种方法是这样的!@#$ ing慢?!
编辑: _db.GetAllServiceVisits()定义如下:
public IEnumerable<service_visit_tb> GetAllServiceVisits()
{
var _visits = _db.service_visit_tbs;
return _visits.AsEnumerable();
}
Run Code Online (Sandbox Code Playgroud)
结束编辑
第二次编辑: 我已从日志中的每个条目中删除此行:
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.1
Run Code Online (Sandbox Code Playgroud)
上下文日志如下:
// This query is to fetch all the clients of Type One (corresponding to _cr.GetRetailClientNames() )
SELECT [t0].[id], [t0].[name]
FROM [genii].[retail_client_tb] AS [t0]
// This query is to fetch all the clients of Type Two (corresponding to _cr.GetCorporateClientNames() )
SELECT [t0].[id], [t0].[company_name] AS [name]
FROM [genii].[corporate_client_tb] AS [t0]
// This is the main query (loading roughly 250 records) which fetchs all Visits
SELECT [t0].[id], [t0].[client_type], [t0].[client_id], [t0].[machine_type], [t0].[machineID], [t0].[visit_type], [t0].[scheduledon], [t0].[arrivedon], [t0].[completedon], [t0].[reported_problem], [t0].[diagnosed_problem], [t0].[action_taken], [t0].[visit_status], [t0].[engineer_id], [t0].[reference_id], [t0].[addedby], [t0].[addedon], [t0].[modifiedby], [t0].[modifiedon]
FROM [genii].[service_visit_tb] AS [t0]
// These next queries are not being manually called by me, I assume they are being
// called when the Razor view is compiled since I am calling the name value of a linked table as such:
// @item.service_visit.engineer_tb.name
SELECT [t0].[id], [t0].[type]
FROM [genii].[visit_type_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [8]
SELECT [t0].[id], [t0].[status]
FROM [genii].[visit_status_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [1]
SELECT [t0].[id], [t0].[name]
FROM [genii].[engineer_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [3]
SELECT [t0].[id], [t0].[type]
FROM [genii].[visit_type_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [11]
SELECT [t0].[id], [t0].[name]
FROM [genii].[engineer_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [2]
SELECT [t0].[id], [t0].[type]
FROM [genii].[visit_type_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [7]
SELECT [t0].[id], [t0].[type]
FROM [genii].[visit_type_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [2]
SELECT [t0].[id], [t0].[type]
FROM [genii].[visit_type_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [6]
SELECT [t0].[id], [t0].[type]
FROM [genii].[visit_type_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [3]
SELECT [t0].[id], [t0].[name]
FROM [genii].[engineer_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [5]
SELECT [t0].[id], [t0].[name]
FROM [genii].[engineer_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [4]
SELECT [t0].[id], [t0].[status]
FROM [genii].[visit_status_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [8]
SELECT [t0].[id], [t0].[status]
FROM [genii].[visit_status_tb] AS [t0]
WHERE [t0].[id] = @p0
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [2]
Run Code Online (Sandbox Code Playgroud)
该问题的附录:有没有更好的方法来提取这些数据?我总是假设(从未检查过)LINQ上下文给我的基于外键的数据访问是如此好,但是看到这些额外的查询,我不再那么肯定了!
将在今天晚些时候发布第二部分执行速度(这是孟买一个漫长的周末,但我们正在努力)
结束编辑
第三次编辑
(我正在考虑从网络服务器到客户端的响应,因为应该考虑所有计算/获取/绑定/等等.)
方法一:6.85秒(从3个表中调用,然后使用C#进入View-Model)
public IEnumerable<service_visit_tb> GetAllServiceVisits()
{
var _visits = _db.service_visit_tbs;
_db.Log = new DebuggerWriter();
return _visits.AsEnumerable();
}
public ActionResult Index()
{
var _retailclients = _cr.GetRetailClientNames().ToArray();
var _corporateclients = _cr.GetCorporateClientNames().ToArray();
var _visits = _db.GetAllServiceVisits();
List<ServiceVisitViewModel> _temp = new List<ServiceVisitViewModel>();
foreach (service_visit_tb v in _visits)
{
_temp.Add(new ServiceVisitViewModel { service_visit = v, client = (v.client_type ? _corporateclients.Where(p => p.id == v.client_id).First().name : _retailclients.Where(p => p.id == v.client_id).First().name) });
//}
return View(_temp);
}
Run Code Online (Sandbox Code Playgroud)
方法二:8.59秒(从3个表中调用,然后使用LINQ进入View-Model)
public IEnumerable<service_visit_tb> GetAllServiceVisits()
{
var _visits = _db.service_visit_tbs;
_db.Log = new DebuggerWriter();
return _visits.AsEnumerable();
}
public ActionResult Index()
{
var _retailclients = _cr.GetRetailClientNames().ToArray();
var _corporateclients = _cr.GetCorporateClientNames().ToArray();
var _visits = _db.GetAllServiceVisits();
var _temp = _visits.Select(o => new ServiceVisitViewModel
{
service_visit = o,
client = (o.client_type ? _corporateclients.Where(p => p.id == o.client_id).First().name : _retailclients.Where(p => p.id == o.client_id).First().name)
});
return View(_temp);
}
Run Code Online (Sandbox Code Playgroud)
方法三:5.76秒(单个LINQ查询中的所有内容 - 在数据库上执行)
public IEnumerable<ServiceVisitViewModel> GetAllServiceVisitsNew()
{
var _visits = _db.service_visit_tbs.Select(o => new ServiceVisitViewModel
{
service_visit = o,
client = (o.client_type ? _db.corporate_client_tbs.Where(c=> c.id == o.client_id).First().company_name : _db.retail_client_tbs.Where(c=> c.id == o.client_id).First().name)
});
_db.Log = new DebuggerWriter();
return _visits;
}
public ActionResult Index()
{
var _visits = _db.GetAllServiceVisitsNew();
return View(_visits());
}
Run Code Online (Sandbox Code Playgroud)
猜猜决定了.感谢大家的帮助.我正在将Jon标记为正确答案,因为他在数据库方面做所有事情的方法就是将培根带回家.非常感谢任何接受过麻烦回复的人和所有人.
结束编辑
该Select声明在这里产生了巨大的差异 - 因为它改变了在数据库中所做的事情.(我假设_db.GetAllServiceVisits()退货了IQueryable<T>.)
由于您正在使用,您已将所有零售和企业客户提取到内存中ToArray.您的Select通话将(我怀疑),然后是发送所有的数据回到数据库中,这样的Select和Where可以在那里进行.我怀疑如果你查看SQL分析器,它将是一个非常奇怪的查询.
如果你强迫一切都在客户端完成,它应该与你的后一种方法相同.你可以轻松地做到这一点:
var _visits = _db.GetAllServiceVisits().ToList();
Run Code Online (Sandbox Code Playgroud)
...但是,这意味着每次点击此页面时,您都会从数据库中提取所有三个表格.这对我来说听起来不是一个好计划.
但是,如果您可以在数据库中执行所有操作而不首先将所有零售和corportate客户端提取到内存中,那将会更好.
这可能就像更改存储库方法以返回IQueryable<T>而不是IEnumerable<T>删除调用一样简单AsEnumerable.