ASP.NET MVC + LINQ исключение - PullRequest
5 голосов
/ 21 августа 2009

У меня есть приложение ASP.NET MVC, которое отлично работает в 99,9% случаев. Однажды в голубой луне, хотя вещи идут действительно неправильно, и я задавался вопросом, мог ли кто-нибудь пролить некоторый свет на то, что могло бы пойти не так, как надо здесь.

Веб-приложение использует Linq2SQL и взрывается в контроллере после следующего набора инструкций:

const int pageSize = 5;
var allHeadings = artRepository.FindAllVisibleHeadings();
var paginatedHeadings = new PaginatedList<Article>(allHeadings, id ?? 0, pageSize);

allHeadings содержит только список IQueryable всех видимых заголовков для статей, в то время как PaginatedList позаботится о выделении соответствующего фрагмента из этого очень длинного списка. Это выглядит следующим образом:

public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize)
{
    PageIndex = pageIndex;
    PageSize = pageSize;
    TotalCount = source.Count();
    TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);

    this.AddRange(source.Skip(PageIndex * PageSize).Take(PageSize));
}

Он взрывается в строке source.Count () , поэтому при подсчете всех видимых статей в БД. Забавно то, что когда я перезагружаю страницу несколько раз, я получаю 2 разных вида исключений:

ПЕРВЫЙ: последовательность содержит более одного элемента

at System.Data.Linq.SqlClient.SqlProvider.Execute(Expression query, QueryInfo queryInfo, IObjectReaderFactory factory, Object[] parentArgs, Object[] userArgs, ICompiledSubQuery[] subQueries, Object lastResult)
at System.Data.Linq.SqlClient.SqlProvider.ExecuteAll(Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, Object[] userArguments, ICompiledSubQuery[] subQueries)
at System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query)
at System.Data.Linq.DataQuery`1.System.Linq.IQueryProvider.Execute[S](Expression expression)
at System.Linq.Queryable.Count[TSource](IQueryable`1 source)
at KoscierzynaInfo.Helpers.PaginatedList`1..ctor(IQueryable`1 source, Int32 pageIndex, Int32 pageSize) in C:\Users\mr\Documents\Visual Studio 2008\Projects\KoscierzynaInfo\KoscierzynaInfo\Helpers\PaginatedList.cs:line 20
at KoscierzynaInfo.Controllers.HomeController.Index(Nullable`1 id) in C:\Users\mr\Documents\Visual Studio 2008\Projects\KoscierzynaInfo\KoscierzynaInfo\Controllers\HomeController.cs:line 63
at lambda_method(ExecutionScope , ControllerBase , Object[] )
at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassa.<InvokeActionMethodWithFilters>b__7()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassa.<>c__DisplayClassc.<InvokeActionMethodWithFilters>b__9()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
at System.Web.Mvc.Controller.ExecuteCore()
at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext)
at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext)
at System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

И ВТОРОЙ ТИП: индекс находился за пределами массива

at System.Data.SqlClient.SqlDataReader.ReadColumnHeader(Int32 i)
at System.Data.SqlClient.SqlDataReader.IsDBNull(Int32 i)
at Read_Article(ObjectMaterializer`1 )
at System.Data.Linq.SqlClient.ObjectReaderCompiler.ObjectReader`2.MoveNext()
at System.Collections.Generic.List`1.InsertRange(Int32 index, IEnumerable`1 collection)
at KoscierzynaInfo.Helpers.PaginatedList`1..ctor(IQueryable`1 source, Int32 pageIndex, Int32 pageSize) in C:\Users\mr\Documents\Visual Studio 2008\Projects\KoscierzynaInfo\KoscierzynaInfo\Helpers\PaginatedList.cs:line 20
at KoscierzynaInfo.Controllers.HomeController.Index(Nullable`1 id) in C:\Users\mr\Documents\Visual Studio 2008\Projects\KoscierzynaInfo\KoscierzynaInfo\Controllers\HomeController.cs:line 63
at lambda_method(ExecutionScope , ControllerBase , Object[] )
at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassa.<InvokeActionMethodWithFilters>b__7()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassa.<>c__DisplayClassc.<InvokeActionMethodWithFilters>b__9()
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
at System.Web.Mvc.Controller.ExecuteCore()
at System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext)
at System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext)
at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext)
at System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext)
at System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Единственный способ решить эту проблему - перезапустить IIS или перезапустить пул. Эта проблема случалась с кем-то из вас раньше? Откуда это взялось?! И есть ли какое-нибудь средство от этого?

Я переместил приложение из IIS7 + SQL Server 2005 на другой сервер с IIS6 + SQLServer 2008, надеясь, что это решит проблему, но, к сожалению, сегодня это повторилось, что наводит меня на мысль, что эта проблема на самом деле не системная / db зависимый.

Ответы [ 6 ]

5 голосов
/ 27 сентября 2012

Полагаю, классы DataContext, которые вы можете использовать, привязаны к жизненному объему на уровне запроса или на уровне синглтона. Если это так, я могу предложить вам оставить область действия на уровне метода.

Странное исключение LINQ (см. Комментарии к ответу)

2 голосов
/ 26 сентября 2012

Поскольку вы используете IQueryable<T> в качестве источника, это означает, что данные загружаются в параметр source только при вызове Count() для него.

Вы можете получить первую ошибку при вызове Single() для коллекции с более чем одним элементом, подобным этому:

var data = Enumerable.Range(1, 10).Single();

Учитывая вышесказанное, я мог бы сказать, что где-то в вашем коде вы вызываете Single() и , в большинстве случаев коллекция имеет только один элемент, но когда она больше, вы получаете ошибку.

Для второй ошибки: глядя на вершину трассировки стека, вы видите

SqlDataReader.ReadColumnHeader (Int32 i)

, который является методом, который бросает ArgumentOutOfRangeException. Это может указывать на то, что ваша модель базы данных не синхронизирована со схемой вашей базы данных или с результатами ваших запросов, т.е. вы ожидаете прочитать N столбцов, но вместо этого у вас есть NK столбцов.

1 голос
/ 21 августа 2009

Единственный раз, когда я сталкиваюсь с ошибкой 1, это когда я пытаюсь установить один объект, когда возвращено более одного объекта.

if (source == null)
{
  source = new List<T>().AsQueryable();
}

Вы пытались включить что-то подобное в пейджер?

0 голосов
/ 02 октября 2012

Важно то, что даже если ваше исключение происходит при выполнении Count (), код, который не работает, находится не в этом методе, а в выражении LINQ, которое создает ваш IQueryable. Это все чудо с LINQ, оператор выбора LINQ выполняется только тогда, когда его результат перечисляется .

Перечислимое (запрашиваемое наследуемое перечислимое) перечисляется в следующих случаях (список не исчерпывающий): Count, Foreach, ToList, ToArray, ToDictionary, First и т. Д. Поэтому, когда нам нужен элемент from или количество элементов в результат.

Что еще более важно, до тех пор, пока вы сохраняете результат запроса LINQ без преобразования его в список или массив, оператор выбора будет выполняться при каждом его перечислении .

Как говорится, вот две важные вещи:

  1. Код ошибки в методе FindAllVisibleHeadings, потому что именно здесь строится ваш запрос LINQ. Не видя код, трудно понять, где именно, но, учитывая полученное вами сообщение об ошибке, вы должны искать оператор Single или SingleOrDefault.

  2. Существует очевидный запах кода в том факте, что вы перечисляете свой IQueryable несколько раз (Count и Skip / Take).

Основным решением для этого будет преобразование вашего IQueryable в список перед его использованием.

public PaginatedList(IQueryable<T> source, int pageIndex, int pageSize)
{
    var sourceList = source.ToList();
    PageIndex = pageIndex;
    PageSize = pageSize;
    TotalCount = sourceList.Count();
    TotalPages = (int)Math.Ceiling(TotalCount / (double)PageSize);

    this.AddRange(sourceList.Skip(PageIndex * PageSize).Take(PageSize));
}

Но выполнение этого самостоятельно, вероятно, не решит вашу проблему. Вы должны думать, что между вашими данными может случиться что угодно Ваш запрос LINQ в методе FindAllVisibleHeadings и момент его использования.

Вероятно, было бы неплохо заставить FindAllVisibleHeadings возвращать List вместо IQueryable. Но нет способа узнать, пока мы не увидим код внутри него.

0 голосов
/ 02 октября 2012

Ваше второе исключение не в Count (). Это происходит в AddRange (). И это наиболее вероятно, когда PageSize превышает оставшееся количество элементов после пропуска. Как примечание, даже если вы сократили код и проверяете это, счет может быть неточным к моменту запуска AddRange.

Я бы предположил, что причина, по которой вы должны циклически выполнять пул приложений, заключается в том, что вы повторно используете DataContext, как и предполагал Марк. Я бы рекомендовал сократить повторное использование DataContext по крайней мере до запроса. Тогда одна ошибка не повредит все приложение.

0 голосов
/ 01 октября 2012

Похоже, вы также будете перечислять один и тот же источник дважды подряд. Count запустит select * для базы данных и вернет счетчик, затем Take выполнит другой запрос к базе данных. Было бы лучше получить результаты обратно в коллекцию IEnumerable в памяти и запустить сначала .Length на нем и использовать Take on IEnumerable, чтобы получить результаты, которые вы хотите использовать для подкачки. Проверяя, равна ли длина нулю, единице или более, вы можете изменить ее, если используете Take (), SingleorDefault () или FirstorDefault ()

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...