Ленивая / отложенная загрузка ссылок не вовремя? - PullRequest
1 голос
/ 05 сентября 2011

Кто-нибудь испытал следующее?Проверка объектов с полями, которые ссылаются на другие сущности, выдает ошибку, в которой говорится, что поле отсутствует и что при отладке программы и проверке сущностей заполненные поля .

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

У нас есть эта (упрощенная) модельгде

class Survey {
  ...
  public bool Enabled {get; set;}
  [Required]
  public virtual OrganisationalUnit OU {get; set;}
  ...
}

Если бы мы просто сделали Context.Surveys.Single(id) или Context.Surveys.Where(s => s.Id == id), изменив поле Enabled (или любое другое поле) и выполнив Context.SaveChanges(), это было бы в 9 из 10 развыдать ошибку проверки, что поле OU является обязательным и отсутствует;

После добавления .Include(s => s.OU) эта проблема была решена, и я подумал, что это конец.Хотя вчера я снова столкнулся с подобной проблемой со следующим кодом:

public class SurveyQuestionMultipleChoiceMultiSelect : SurveyQuestionMultipleChoice
{
    public override IEnumerable<ValidationResult> validateValue(string _, IEnumerable<string> values)
    {
        int ivalue;
        foreach( string value in values) {

            bool success = int.TryParse(value, out ivalue);

            if (!success || !Questions.Any(q => q.Id == ivalue))
                yield return new ValidationResult(String.Format(GUI.error_multiplechoice_answer_not_element_of, ivalue));
        }
    }
}

Это вернет ValidationErrors для значений [4,5], тогда как Questions при проверке через отладчик действительно содержит вопросы с Id s4 и 5. Если бы я на мгновение приостановил отладчик в if -состоянии, проверка прошла бы правильно после этого.

Странно то, что я (сознательно) не испытывал этих ошибок раньше ичто я не обновлял библиотеки или программное обеспечение базы данных.

Эта ситуация меня немного пугает, так как кажется, что я не могу полагаться на Lazy Loading, которая всегда работает.Или, может быть, я что-то не так делаю?

Это похоже на то, что EF 4.1 загрузка отфильтрованных дочерних коллекций не работает для многих ко многим , но я не могу объяснить, как это применимо здесь.

Update1 : появится следующее исключение, если выполнить шаги, описанные в первом примере:

System.Data.Entity.Validation.DbEntityValidationException was unhandled by user code
  Message=Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
  Source=EntityFramework
  StackTrace:
       at System.Data.Entity.Internal.InternalContext.SaveChanges()
       at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
       at System.Data.Entity.DbContext.SaveChanges()
       at Caracal.Application.Controllers.SurveyController.BulkEnable(SurveyBulkAction data) in C:\Users\Alessandro\Caracal\DigEvalProject\trunk\Caracal\application\Controllers\SurveyController.cs:line 353
       at lambda_method(Closure , 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__DisplayClass15.<InvokeActionMethodWithFilters>b__12()
       at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
  InnerException: 
    <really-empty>

Код для достижения этого (не написанный мной лично,но другой член команды):

        bool option = data.option == "true";

        // Check if all surveys can be set to the enabled state
        foreach (int id in data.surveys)
        {
            Survey survey = Context.Surveys.SingleOrDefault(s => s.Id == id);
            if (survey == null || !survey.CanAdministrate(Context))
                return JsonResponse.Error(GUI.survey_enable_change_bulk_failed);

            surveys.Add(survey);
        }

        // Enable/disable the selected surveys.
        foreach (Survey survey in surveys)
            survey.Enabled = option;

        Context.SaveChanges();

data - это объект, содержащий пост-данные от клиента.survey.CanAdministrate(Context) использует контекст для считывания всего дерева OrganisationalUnits из БД для определения ролей.

1 Ответ

3 голосов
/ 05 сентября 2011

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

public virtual DbEntityValidationResult GetValidationResult(IDictionary<object, object> items)
{
    EntityValidator entityValidator = 
        this.InternalContext.ValidationProvider.GetEntityValidator(this);
    bool lazyLoadingEnabled = this.InternalContext.LazyLoadingEnabled;
    this.InternalContext.LazyLoadingEnabled = false;
    DbEntityValidationResult result = null;
    try
    {
        ...
    }
    finally
    {
        this.InternalContext.LazyLoadingEnabled = lazyLoadingEnabled;
    }
    return result;
}

Почему это хорошо?Потому что это позволяет избежать утечки ленивых нагрузок в случае, когда вы не хотите их.Btw.если вы поместили логику проверки в свойство, которое не нужно загружать, вы сделали это неправильно.Вы несете ответственность за то, чтобы все необходимые свойства были заполнены до проверки.

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