我有一个在 MVC 应用程序中使用的包装类,该类旨在循环访问集合中的项目,并在返回该项目进行显示之前检查当前用户是否有权访问该项目。在大多数情况下,它就像一个魅力。然而,对于一种特定的对象类型,它运行得像一只绝对的狗,我不明白为什么。
接口类是这样写的:
private readonly IEnumerable<T> _ViewData;
public IEnumerator<T> GetEnumerator()
{
foreach (T item in _viewData)
{
if (item.UserCanEdit || item.UserCanView)
yield return item;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
现在我的第一个问题是检查我对这里发生的事情的理解。我可能是错的,但我的理解是,当我尝试对类型对象集合运行 foreach 循环时,它永远不需要使用 System.Collections.IEnumerable.GetEnumerator() - 当然该代码永远不会被命中?
问题在于,当我尝试在日志文件条目上使用此包装器时,日志文件条目是通过 LogEntryViewDaya 类中的实体框架传递的。当代码到达 GetEnumerator 内的 foreach 循环时,它会停止整整 6-8 秒,即使枚举中只有 20 个项目!
这是 LogEntryView 的代码
public class LogEntryViewData:BaseViewData
{
private LogEntry _entry;
public LogEntryViewData(LogEntry entry, ICustomerSecurityContext securityContext) : base(securityContext)
{
_entry = entry;
}
public string Application { get { return _entry.Application; } }
public string CurrentUser { get { return _entry.CurrentUser; } }
public string CustomerName { get { return _entry.CustomerName; } }
public DateTime Date { get { return _entry.Date; } }
public string Exception { get { return _entry.Exception; } }
public string HostName { get { return _entry.HostName; } }
public long Id { get { return _entry.Id; } }
public string Level { get { return _entry.Level; } }
public string Message { get { return _entry.Message; } }
public int? ProcessId { get { return _entry.ProcessId; } }
public int? ServiceId { get { return _entry.ServiceId; } }
public string ServiceName { get { return _entry.ServiceName; } }
public int? TaskId { get { return _entry.TaskId; } }
public int? TaskName { get { return _entry.TaskName; } }
public string Thread { get { return _entry.Thread; } }
}
据我所知,实例化这些类没有明显的性能 - 在构造函数中放置一个断点并通过 F5 似乎很顺利。
那么为什么这些特定对象的集合的迭代速度如此之慢呢?我不知道:非常感谢建议。
I've got a wrapper class that's used in an MVC application that is designed to loop through the items in a collection and check that the current user has authorisation to access that item before returning it for display. For the most part it works like a charm. However for one particular object type it runs like an absolute dog and I can't work out why.
The Interface classes are written like this:
private readonly IEnumerable<T> _ViewData;
public IEnumerator<T> GetEnumerator()
{
foreach (T item in _viewData)
{
if (item.UserCanEdit || item.UserCanView)
yield return item;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
Now my first question is check my understanding of what's going on here. I'm probably wrong, but my understanding is that when I try and run a foreach loop against a collection of type objects it never needs to use System.Collections.IEnumerable.GetEnumerator() - certainly that code never gets hit?
The problem lies when I try and use this wrapper on log file entries, which are delivered via entity framework in a LogEntryViewDaya class. When the code hits the foreach loop inside GetEnumerator it stop for a full 6-8 seconds, even though there are only 20 items in the enumeration!
Here's the code for LogEntryView
public class LogEntryViewData:BaseViewData
{
private LogEntry _entry;
public LogEntryViewData(LogEntry entry, ICustomerSecurityContext securityContext) : base(securityContext)
{
_entry = entry;
}
public string Application { get { return _entry.Application; } }
public string CurrentUser { get { return _entry.CurrentUser; } }
public string CustomerName { get { return _entry.CustomerName; } }
public DateTime Date { get { return _entry.Date; } }
public string Exception { get { return _entry.Exception; } }
public string HostName { get { return _entry.HostName; } }
public long Id { get { return _entry.Id; } }
public string Level { get { return _entry.Level; } }
public string Message { get { return _entry.Message; } }
public int? ProcessId { get { return _entry.ProcessId; } }
public int? ServiceId { get { return _entry.ServiceId; } }
public string ServiceName { get { return _entry.ServiceName; } }
public int? TaskId { get { return _entry.TaskId; } }
public int? TaskName { get { return _entry.TaskName; } }
public string Thread { get { return _entry.Thread; } }
}
As far as I can tell there's no appreciable performance in instantiating these classes - putting a breakpoint in the constructor and F5ing through seems slick as anything.
So why is a collection of these particular objects so slow to iterate through? I have no idea: suggestions gratefully appreciated.
发布评论
评论(1)
您还没有向我们展示班级是如何填充的。我的猜测是,数据库查询的时间正在流逝 - 特别是,根据所获取的内容,可能有一个查询来获取骨架实体,然后对每个实体进行一个查询
UserCanEdit
和UserCanView
。这似乎不太可能(我希望该条目在初始查询中填充这些属性),但只是有可能。基本上,观察它如何与数据库交互。
我建议你尝试编写一个使用相同类的控制台应用程序,这样你就可以比在网络应用程序中更轻松地使用它、添加计时日志等。
编辑:除了评论等中的其他内容之外,您是否有任何理由不将其作为 LINQ 查询的一部分来执行?例如:
我很欣赏它可能不会那么简单,因为
UserCanEdit
将取决于当前用户等 - 但它可能应该作为数据库完成查询而不是客户端。You haven't shown us how the class is being populated. My guess is that the time is going on database queries - in particular, depending on what's being fetched, it's just possible that there's one query to get the skeletal entities, and then one query for each of
UserCanEdit
andUserCanView
. That seems unlikely (I'd have expected the entry to have those properties populated on the initial query) but just possible.Basically, watch how it's interacting with your database.
I suggest you try to write a console app which uses the same class, so you can mess around with it, add timing logs etc more easily than you can from a webapp.
EDIT: Aside from everything else in the comments etc, is there any reason you're not doing this as part of a LINQ query? For example:
I appreciate it probably won't be quite as simple as that, as
UserCanEdit
will depend on the current user etc - but it probably should be done as a database query rather than client-side.