【问题标题】:Would this perform better with LINQ?使用 LINQ 会更好吗?
【发布时间】:2011-11-10 16:07:05
【问题描述】:

下面的方法选择管理员权限并从缓存的 DataTable 中返回 bool,使用 Linq 会更好吗?

您可能会问为什么不对其进行测试。好吧,由于缺乏知识,我不能用Linq写它

 DataRow[] result = PrivilegeMap.Select("privilegeActionId=" + (int)actionId);
            bool moderatorHasIt = Convert.ToBoolean(result[0]["moderatorHasIt"]);
            bool adminHasIt = Convert.ToBoolean(result[0]["adminHasIt"]);                                        
            if (myRole == User.Role.Admin)
            {
                return Convert.ToBoolean(adminHasIt);
            }
            if (myRole == User.Role.Moderator)
            {
                return Convert.ToBoolean(moderatorHasIt);
            }
            else
            {
                return false;
            }

【问题讨论】:

  • 我删除了我的答案,因为我记得 DataTable 维护基于 Select 查询的索引。

标签: c# performance linq datatable


【解决方案1】:

可能是。

假设如下:

  1. 基础表的起始大小、读取频率和更新频率之间的平衡使得将整个内容全部加载到内存中并反复查看是没有意义的。

  2. 每个 id 只有一个匹配行。

  3. 行中有很多其他有趣的字段,我们在这里不关心。

如果我们用 Linq2SQL Table<Privileges> 替换 PrivilegeMap,等效的 LINQ 代码将类似于:

var result = PrivilegeMap.Where(p => p.PrivilegeActionId == actionID).Select(p => new{p.ModeratorHasIt, p.AdminHasIt}).First()
if (myRole == User.Role.Admin)
{
  return result.AdminHasIt;
}
if (myRole == User.Role.Moderator)
{
  return result.ModeratorHasIt;
}
else
{
  return false;
}

(就此而言,var result = PrivilegeMap.Where(p => p.PrivilegeActionId == actionID).First(p => new{p.ModeratorHasIt, p.AdminHasIt}) 也可以写成var result = (from p in PrivilegeMap where p.PrivilegeActionId == actionID select new{p.ModeratorHasIt, p.AdminHasIt}).First(),这只是相同 LINQ 操作的不同语法)。

假设actionID2 您的代码将按照以下方式转换为 SQL:

SELECT * FROM Privileges WHERE privilegeActionId=2

上面的 Linq 会变成:

SELECT TOP 1 adminHasIt, moderatorHasIt FROM Privileges WHERE privilegeActionId

您可以看到,如果这是一个包含很多列的表和/或如果有多个匹配行,那么这可能会更有效率。

(如果 PrivilegeMap 是可枚举但不可查询的,它将变成一个加载和扫描整个事物的操作,因此根本没有效率)。

但另一方面,生成该 SQL 的代码可能更复杂,并且需要在设置特权实体对象方面进行一些工作。如果这是一次性操作,就开发人员或运行时效率而言肯定不值得,但否则它可以同时受益。

但是,请注意,在这两种情况下,我们都在进行不必要的查询。我实际上会用:

if (myRole == User.Role.Admin)
{
  return PrivilegeMap.Where(p => p.PrivilegeActionId == actionID).Select(p => p.AdminHasIt).First();
}
if (myRole == User.Role.Moderator)
{
  return PrivilegeMap.Where(p => p.PrivilegeActionId == actionID).Select(p => p.ModeratorHasIt);
}
else
{
  return false;
}

要么只查询 adminHasIt、只查询 moderatorHasIt,要么根本不查询,因为无论数据库状态如何,myRole 的任何其他值都保证为 false

同样,您可以通过以下方式获得更简单的改进:

if(myRole != User.Role.Admin && myRole != User.Role.Moderator)
  return false;
DataRow[] result = PrivilegeMap.Select("privilegeActionId=" + (int)actionId);
if (myRole == User.Role.Admin)
{
  return Convert.ToBoolean(result[0]["adminHasIt"]);
}
if (myRole == User.Role.Moderator)
{
  return Convert.ToBoolean(result[0]["moderatorHasIt"]);
}

这里我们首先完全避免数据库查询,如果我们不可能使用它,只转换我们关心的字段,而不是随后将 bool 转换为 bool。这种对实际使用数据的本地思考要容易得多,虽然大多数这样的节省很小,但有些很大(这可能是),它们是一种习惯问题,而不是复杂的权衡。

【讨论】:

  • 不客气,尽管我现在才看到您的问题说该表已被缓存。在这种情况下,您会更快地使用@StriplingWarrior 提出的建议,即使用字典。也就是说,您当然可以查询同一个表(无论是缓存的 DataTable 还是 Linq2Objects 的 List),这会再次改变平衡。在后一种情况下,linq 和您的方法之间的性能更接近。
【解决方案2】:

使用 LINQ 可能会更快,因为数据表不必解析您的查询字符串,但您将通过更改数据表示来获得最大的优势,以避免首先查询数据表。

创建一个IDictionary<int, bool>,代表您感兴趣的权限,由actionId 键入。然后当你需要查找时,你可以直接返回dict[actionId

无论如何,我怀疑这可能是过早优化的情况:您是否测试过您的程序并发现这段代码占了您处理时间的很大一部分?

【讨论】:

  • 谢谢,不,它只会被少数用户调用。我只是想知道 linq 是否会产生重大影响
  • @nLL:那你的时间最好用在别处。 :-)
  • 数据库将不得不解析 something 是否来自 Linq、Datatable 或手动编码 SQL。
  • @JonHanna:假设 DataTable 在内存中(OP 说它是一个 cached 数据表),您可以通过编程方式遍历行而不访问数据库。在这种情况下,LINQ to Objects 不需要像 run 那样解析查询。
  • 对,我错过了上面的“缓存”这个词,所以虽然我在回答中提到了这种可能性,然后我写得好像不是这样。
【解决方案3】:

您列出的代码不完整,因为未定义 myRole 变量。

考虑到这一点,我建议将它写在 switch 语句中,然后继续直到它被确定为问题。另外,它会更容易阅读(在我看来)。

【讨论】:

    猜你喜欢
    • 2014-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-25
    • 2013-04-15
    • 1970-01-01
    相关资源
    最近更新 更多