использование Breezejs для захвата исключения внешнего ключа в SaveChanges()

Я использую breezejs в проекте ASP.NET MVC 4 SPA и durandal с первой настройкой базы данных EF 5.0, и все работает нормально, за исключением определенного сценария.

Простите меня, если я кажусь немного нубом в отношении javascript и материалов SPA. Я учусь на ходу.

вот мои 2 модели от EF

Project.cs

public partial class Project
{
        public Project()
        {
            this.Timesheets = new HashSet<Timesheet>();
        }

        public int ProjectId { get; set; }
        public Nullable<int> ClientId { get; set; }
        public string ProjectName { get; set; }
        public string ProjectDescription { get; set; }
        public Nullable<decimal> ProjectRate { get; set; }

        public virtual Client Client { get; set; }
        public virtual ICollection<Timesheet> Timesheets { get; set; }
}

Клиент.cs

public partial class Client
{
    public Client()
    {
        this.Projects = new HashSet<Project>();
        this.Timesheets = new HashSet<Timesheet>();
    }

    public int ClientId { get; set; }
    public string ClientNo { get; set; }
    public string ClientName { get; set; }
    public string CompanyName { get; set; }
    public string AddressLine1 { get; set; }
    public string AddressLine2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
    public string County { get; set; }
    public Nullable<decimal> ClientRate { get; set; }
    public string EmailAddress { get; set; }

    public virtual ICollection<Project> Projects { get; set; }
    public virtual ICollection<Timesheet> Timesheets { get; set; }
}

отрывок из моей модели представления клиента

function deleteClient(model) {
    app.showMessage('Are you sure you want to DELETE this record?', 'Delete a Record', ['Yes', 'No'])
   .then(function (dialogresult) {
       if (dialogresult == "Yes") {
           models.remove(model);
           model.entityAspect.setDeleted();
           return saveRecord(model);
       }
   });
};

function saveRecord(model) {
    return repository.saveEntity(model)
    .fail(handleFailed);

    function handleFailed(error) {
        var err = "Error retrieving Clients : " + error.message;
        error(err);
        logger.error(err, null, null, true);
        return;
    }
};

выдержка из репозитория.js (контекст данных)

function saveEntity(masterEntity) {
    return manager.saveChanges().fail(saveFailed);

    function saveFailed(error) {
            logger.error("Error saving : " + error.message, null, null, true);
    }
}

вот выдержка из моего ProjectBillingController.cs

   [HttpPost]
    public SaveResult SaveChanges(JObject saveBundle)
    {
        return _contextProvider.SaveChanges(saveBundle);
    }

Вот мой сценарий,

У меня есть проект строки "P1", в котором есть ссылка на клиента "ABC". Если я попытаюсь удалить клиент «ABC», ветер вызовет исключение внутри SaveChanges в моем контроллере WEBAPI «ProjectBillingController.cs».

Я знаю, что в мире SQL это должно быть ожидаемой ошибкой.

Но мои вопросы...

  • Могу ли я зафиксировать эту ошибку ограничения на стороне клиента до того, как она попадет на сервер «ProjectBillingController.cs»?
  • Единственный способ, которым я вижу решение этой проблемы, - это выполнить другой запрос, чтобы увидеть, есть ли какие-либо строки в объекте Project с этим клиентом, и если да, то остановить удаление, в противном случае продолжить удаление. Есть ли другой способ сделать это?
  • Должен ли я написать эту «логику проверки» внутри моей модели представления или содержимого данных (репозиторий)

person Ed Mendez    schedule 15.03.2013    source источник
comment
Прошу прощения, кажется, я решил эту проблему. Моя проблема заключалась в том, что у меня была опечатка в моем методе logger.error(). Это должен был быть logger.logError(). и из-за этого ошибка не возвращалась клиенту, она выдавала расплывчатое сообщение. Когда я запустил его через отладку, я увидел исключение в ограничении внешнего ключа и подумал, что оно не возвращается клиенту. Однажды я изменил имя. ошибка возвращалась к части .failed() обещания saveChanges(), и я могу увидеть ошибку.   -  person Ed Mendez    schedule 16.03.2013


Ответы (2)


В свойствах таблицы Project у вас должно быть отношение FK к Client, верно? В этом отношении правило удаления должно быть установлено на CASCADE (обратите внимание, что это приведет к удалению всех связанных с клиентом проектов при удалении клиента).

Итак, шаг за шагом: 1. В Visual Studio проверьте обозреватель серверов 2. Щелкните правой кнопкой мыши таблицу "Проект" 3. Выберите "Свойства таблицы"

Проверьте то, что я упомянул о правиле удаления, и измените его соответствующим образом.

Тьяго

person Tiago Reis    schedule 15.03.2013
comment
Спасибо за ваше предложение, но в событии пользователь пытается удалить клиента, с которым связаны проекты, я не хочу это разрешать. - person Ed Mendez; 16.03.2013

Я не верю, что есть способ определить на клиенте, что удаление родителя должно каскадировать удаление любых связанных с дочерними объектами. Вы не можете поймать «ошибку» до вызова сервера. Вы можете компенсировать клиенту... как описано ниже.

Я думаю, что предпочитаю предложение Тиаго о том, чтобы вы настроили свою базу данных (и/или EF) для автоматического каскадного удаления.

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

Если вы пишете логику на клиенте, делайте это в своем «контексте данных». Это не проблема ViewModel.

FWIW, я бы не назвал это «логикой проверки». Вы не проверяете данные. Логика проверки никогда не изменяет данные; он может только объявить данные действительными или недействительными.

Вы работаете с данными в соответствии с правилом бизнес-модели, согласно которому дочерние экземпляры этого родительского EntityType должны быть удалены при удалении родителя. В этом правиле нет ничего очевидного. С точки зрения схемы, «Цвет» является «родителем» типа «Продукт», поскольку он имеет отношение «один ко многим» к «Продукт». Но если бы я удалил цвет «Красный», я бы НЕ хотел, чтобы все мои красные продукты удалялись автоматически. Поскольку каскадное удаление опасно по своей природе, вы должны приложить все усилия, чтобы настроить серверные компоненты для его поддержки.

Удаление - неприятная штука. Лично я избегаю удаления в своих приложениях, если могу, и вместо этого использую некоторую форму обратимого удаления. Я признаю, что это не всегда вариант.

person Ward    schedule 15.03.2013
comment
Спасибо за ваш вклад. Я согласен, что не хотел бы, чтобы все мои красные продукты были удалены, если я удалю красный цвет. Что, если я дополню объект Client вычисляемым полем HasProjects, и если это правда, я отключу кнопку удаления в html черезknoutjs. - person Ed Mendez; 16.03.2013
comment
Вы могли бы это сделать. Будет ли это правильным поведением приложения? Что, если вы хотите иметь возможность удалить родительские и дочерние элементы? Также обратите внимание, что если вы знаете ответ на HasProjects, это потому, что вы каким-то образом запросили проекты (или их существование). Сделав это, вы все равно могли бы легко удалить родительские и дочерние элементы из клиента. Просто не забудьте перебрать дочерние элементы и setDeleted на них, когда вы setDeleted на родителях. - person Ward; 16.03.2013
comment
NB: даже удаление дочерних элементов на стороне клиента может привести к сбою на сервере... если другой пользователь добавил/удалил дочерние элементы рассматриваемого родителя, пока текущий пользователь готовится удалить родителя. Теперь мы подходим к теме проверки параллелизма в корне агрегата... это другая важная тема. Разве не было бы хорошо, если бы все было так просто, как кажется? :) - person Ward; 16.03.2013
comment
Ах... прочитав ваш ответ Тиаго, я вижу, что вы не хотите удалять родителя, если у него есть дети. Я видел это правило много раз. Так что быстрая проверка на стороне клиента, чтобы узнать, есть ли дети, звучит полезно. Множество способов сделать это помимо очевидного. Но очевидное часто лучше... пока показатели производительности не заявят об обратном. Удачного кодирования! - person Ward; 17.03.2013