System.OutOfMemoryException: 类型为 'System.OutOfMemoryException' 的异常在 angularjs 中被抛出

21

我已经花了几周的时间来解决这个问题,但是我仍然无法解决它。

我正在使用angularjs中的http调用Web API服务。

           $http({
               method: 'GET',
               url: rootUrl + '/api/Project/ProjectList',
               headers: {
                   'Content-Type': "application/json; charset=utf-8"
               }
           }).success(function (response) {
               $scope.ProjectList = response;
           }).error(function (response, errorCode) {
               if (errorCode == 444) {
               }
           })

我已在服务器和客户端代码中设置了断点。

当我调用该服务时,服务器端方法会迅速响应。

我的服务器端方法(使用MVC WEB API和实体框架)。

    [ActionName("ProjectList")]
    [HttpGet]   
    public IList<Project> ProjectList(Project projectModel)
    {
        return objIProjectService.ListOfProject();
    }

我检查了代码,服务返回了8条记录(从数据库中取得的8行数据),问题在于 objIProjectService.ListOfProject(); 这一行。

一切都很好。但我的 http.success 和 http.error 回调函数响应非常慢。

请看下面的图片,展示我调用 http 方法时的性能表现。

enter image description here

最终,http error function 函数在5或10分钟后才响应,并显示以下错误信息:

System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown

这是问题所在。请告诉我如何解决?

实际上,我已经采取了一些措施来尝试解决这个问题。

  • 我清理了临时文件夹 - 没有起作用
  • 我重启了 Visual Studio 并清理了解决方案,重新启动了我的系统并修复了 Visual Studio - 也没有起作用
  • 但是如果我删除数据库中的某些行(我使用的是 sql server 2008 r2),那么它就会工作。

例如,如果我的数据库表少于7行,则它可以快速工作而没有错误。但是如果我的表有超过8行,那么它就会非常缓慢并且抛出错误?为什么呢? 如果你也遇到这个问题,请分享你的解决方案。


1
你需要发布 objIProjectService.ListOfProject(); 函数的实现、数据库表结构以及正常运行和出现错误时的样本数据。 - Brendan Green
该代码为:return context.products.ToList() - Ramesh Rajendran
尝试使用 JSON 作为返回类型,序列化对象并将其发送回去。 - Munzer
那么关于返回的模型和表中的数据呢?这是否与序列化成你要返回的模型具有循环引用有关? - Brendan Green
5个回答

12

我认为问题在于序列化器正在访问项目类中的所有相关属性,因此不是直接返回 Entity Framework 类,而是创建一个新类来表示您希望通过api发送的数据(进一步研究DTO类以获取更多信息)

您可以使用Select Linq方法获取填充了来自EF调用数据的新dto类列表。

var projects = objIProjectService.ListOfProject();

return projects.Select(p => new ProjectDTO() {
   ID = p.Id
   //... other properties of DTO class
}).ToList();

更好的方法是,如果你把这个选择方法放进你的 EF 查询里面(例如 context.projects.Select(/* 在这里选择信息 */).ToList()),你可以确保 EF 只会将你需要的字段带回来。

构建 API 时一定要检查 JSON/XML 响应,确保序列化数据包含了你期望它产生的内容。使用实体框架时,这个响应可能会变得非常大,因为它会遍历所有关联表并提取所有链接的信息,然后尝试进行序列化。

作为个人偏好,我总是喜欢返回一个 IHttpActionResult,它允许你管理发送回客户端的内容,特别是在出现问题时。控制器有许多方法可供使用,比如 OK()、BadRequest()、InternalServerError()...


感谢您的解释。 - Ramesh Rajendran

3
打开SQL Server Profiler并观察EF生成的SQL查询和结果。然后尝试在SQL窗口中执行这些原始查询。也许在此之后您会理解。

0

这里的问题是信息序列化所花费的时间太长了。您正在访问项目类的所有相关类,这可能会在处理内存方面造成重大损害。

您可以通过以下两种方式解决此问题:

一种方法是返回您的承诺,然后获取数据:

 return $http({
               method: 'GET',
               url: rootUrl + '/api/Project/ProjectList',
               headers: {
                   'Content-Type': "application/json; charset=utf-8"
               }
           }).then(function (response) {

第二个选项是在代码中使用实体框架或linq查询。
使用选择查询来降低服务器端和客户端的负担,具体如下:
    var projectsQuery = from p in ProjectModel as pm where pm.Id = p.Id select p.SomeValue
return projectsQuery 

这样做可以降低数据序列化的负担,可能避免OutOfMemoryException。

这里没有必要打开Sql Profiler,因为服务器端的数据在合理的时间内到达。


0

HTTP规范并没有为POST请求设置特定的大小限制。 GET查询的问题在于参数嵌入在URL中,而URL的大小受到限制(这个限制取决于浏览器和服务器)。 不要使用GET查询来处理大数据,应该使用POST请求。

如果你正在处理大数据且使用MVC框架,那么你需要使用这个方法。

<appSettings>
  <add key="aspnet:MaxJsonDeserializerMembers" value="150000" />
</appSettings>

或者

<system.webServer>
<security>
    <requestFiltering>
        <requestLimits maxAllowedContentLength="1000000" />
    </requestFiltering>
</security>

来自:MSDN

希望这些能解决你的问题。


0

进一步跟随Chris Warnes的建议并描述可能的原因。

让我猜猜...你使用启用了延迟加载的实体框架。因此,在序列化期间,您拥有的每个导航属性都会懒惰地递归地加载数据。例如:Project 包含 Worker,后者包含对同一 Project 的引用,该引用位于某些导航属性(如 WorkerProjects)中。

序列化程序试图理解它,但没有任何提示就无法做到这一点。检查正在发生的最简单方法是禁用实体框架的延迟加载,并在 ToList() 之后、return 之前的断点处获取它。

顺便说一句,将此类结构序列化为 JSON - 有点 - 不可能。您需要具有外部机制(例如cycle.js使用XPath来编码此类引用)来处理重复引用和循环。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接