【发布时间】:2014-12-04 15:43:41
【问题描述】:
所以你知道我的背景,我从事专业程序员已经超过 12 年了。到目前为止,我最好的语言是 C#,但我已经完成了 C、C++ 和最近的 ObjectiveC。我已经完成了很多访问数据库中数据的工作,但我没有像大多数人那样完成 UI 工作(IOS 除外)。
最近我开始在 C# 中使用实体框架来完成一项工作,我必须说我希望我能早点发现它。我不会说这是自切片面包以来最好的东西,但它非常接近。使用它一段时间后,我开始思考最佳实践和使用方法,与使用 IDBConnections 和 IDBCommands 的老式方法相比。
我正在编写一种情况,即我将在绑定数据网格中列出来自数据库的用户表的内容,目的是让用户能够执行标准的 CRUD 工作。我首先创建了一个 User 类和一个具有相应实现的 IUserManager 接口。每个用户都被分配到一个部门,自然也需要一种对部门执行 CRUD 的方法,所以我添加了一个 Department 类、一个 IDepartmentManager 接口和一个实现。我将其设置为网格绑定在 IUserManager 接口上的 .GetAll() 方法的结果上。然后我开始填充内脏。
我面前没有代码了,但我基本上是使用 IDBConnection 使用 SQL 查询通过 IDBCommand 进入数据存储区。然后我调用了 command.ExecuteReader() 并在 IDataReader 对象上迭代了 .Read() 方法。使用每一列的序号,我取出数据,对其进行验证并将其滑入 User 类,并将该类添加到 Dictionary 中,然后该方法将返回该类。所有的 DB 类当然都是 IDisposable 的,因此将它们包装在 using 中可以清理混乱。
相当标准的东西,我已经做过无数次了。
那时我意识到我从数据库中提取的部门 ID 并不是我想要在网格中显示的内容。告诉某人“这个人在第 7 部门”不如说“这个人在会计部门”有用。因此,我首先尝试修改我的查询以同时获取部门 ID 和名称,并将名称存储在用户对象上以便稍后显示。然后我决定给用户一个 Department 类实例,它会在它的生命周期中被填充。就在那时,我将胆量转换为 linq。
public Dictionary<int, User> GetAll()
{
var result = new Dictionary<int, User>();
using (var datastore = new myEntities())
{
result = (from user in datastore.userInfoes
join department in datastore.userDepartmentInfoes on user.departmentID equals department.departmentID
select new User()
{
UserIndex = user.id,
FirstName = user.firstName,
LastName = user.lastName,
Department = new Department()
{
DepartmentId = user.departmentID.Value,
DepartmentName = department.departmentName,
},
Username = user.userName,
}
).ToDictionary(x => x.UserIndex, x => x);
}
return result;
}
这就是我开始思考的地方(阅读:可能过度分析)
我的实现可以正常工作。它甚至可以很好地用于小型数据集。它甚至适用于较大的数据集(比如 10,000)。即使你把我现在工作五倍的公司里的每个人都计算在内,你也只有不到一千人。
但是,如果我曾在一家拥有 1000 万员工的大型鸣喇叭公司工作,那会怎样?这将导致 departmentName 字符串可能被重复数百万次。
这也让我想到,与 IOS 的 MVC 实现不同,这种特殊情况不会查询足够多的用户来填满屏幕,然后处理分页和其他内容。一旦调用代码刷新数据绑定,它就会立即拉出所有 1000 万用户并传回集合。这会很慢。
所以这让我想到,这种方法对于较大的数据集既慢又低效。不仅如此,这个数据集可能有 200 万个“会计”实例,这将是一个主要的内存消耗。由于 User 中的 Department 类,我们也有点违背了关系数据库的目的。在数据库中,您只有一个 departmentId int 外键引用另一个表中的条目。仅当您交叉引用另一个表时才会出现该链接,即便如此,任何时候实际上都只有一个“会计”字符串。在上面的代码中,您将有大量的“会计”字符串在等待清理。
一个 MVC 场景基本上会“知道”它需要 X 个条目来填充网格的可视区域。它只会从索引 Y 开始一次查询 X,并且当用户导航时,它会根据需要查询和显示其他记录。这比查询所有 1000 万人并让他们在某个地方闲逛(无论他们是否显示)要好得多。
就像我说的,我很可能过度分析了这一点。我对 linq 工作方式的一些假设也可能不正确。但是为了学习,我想我不得不问:做这样的事情最好的方法是什么?对于小型数据集,这种事情可以吗?将整个事情作为 MCV 实现而不是拉入整个数据集以显示在网格中会更好吗?
【问题讨论】:
标签: performance linq memory