Частичное обновление записей с помощью Linq to SQL и MVC

Допустим, у меня есть таблица БД со столбцами A и B, и я использовал конструктор Visual Studio для создания объектов Linq для этой таблицы. Все поля помечены как NOT NULL.

Теперь я пытаюсь отредактировать эту запись, используя типичное редактирование формы MVC и привязку модели, но поле B не должно быть редактируемым, поэтому я не включаю его в форму.

Когда обработчик сообщений привязывает объект, он не заполняет поле B, оставляя его пустым. Теперь, когда я сохраняю объект, я получаю сообщение об ошибке БД, говорящее, что поле B не может быть NULL.

Код для сохранения выглядит примерно так:

m_DataContext.MyObjects.Attach(myObject);
m_DataContext.Refresh(RefreshMode.KeepCurrentValues, myObject);
m_DataContext.SubmitChanges();

Как заставить это работать? Нужно ли мне включать поле B в качестве скрытого поля в форму - я действительно не хочу этого делать, так как оно может одновременно обновляться другими пользователями, и я не хочу топтать его.


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


person Brad Robinson    schedule 08.09.2009    source источник


Ответы (4)


Спуститься в SQL

Этот подход отказывается от LINQ в пользу прямого SQL:

public override void SaveMyObject(MyObject o)
{
    // Submit
    m_DataContext.ExecuteCommand("UPDATE MyObjects SET A={0} WHERE ID={1}", o.ID, o.A);
}

Мне нравится этот подход лучше всего из-за его простоты. Как бы мне ни нравился LINQ, я просто не могу оправдать его беспорядок этой проблемой.

person Brad Robinson    schedule 09.09.2009

Использование пользовательского связывателя модели

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

public class MyObjectBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        MyObject a = ((MyObjectController)controllerContext.Controller).Repository.GetMyObjectForUpdate(bindingContext.ValueProvider["ID"].AttemptedValue.ToString());
        return a;
    }
}

Затем репозиторий создает объект и связывает его с контекстом данных:

public Object GetMyObjectForUpdate(string id)
{
    MyObject o=new MyObject();
    o.ID=id;
    m_DataContext.Articles.Attach(o);
    m_DataContext.Refresh(RefreshMode.KeepCurrentValues);
    return o;
}

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

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditMyObject([ModelBinder(typeof(MyObjectBinder))] MyObject o)
{
    if (!ModelState.IsValid)
        return View("EditMyObject", a);

    Repository.SaveMyObject(a);
    return RedirectToAction("Index");
}

и, наконец, SaveMyObject просто вызывает datacontext.SubmitChanges().

Чтобы это работало, мне также нужно было установить для атрибутов проверки обновлений во всех столбцах значение «Никогда» (в файле dbml).

В целом, этот подход работает, но запутан.

person Brad Robinson    schedule 09.09.2009

Используйте два объекта сущностей

В этом подходе используются два объекта сущностей, один для привязки модели и один LINQ:

public override void SaveMyObject(MyObject o)
{
    // Create a second object for use with linq and attach to the data context
    MyObject o2 = new MyObject();
    o2.ID = o.ID;
    m_DataContext.Articles.Attach(o2);
    m_DataContext.Refresh(RefreshMode.KeepCurrentValues);

    // Apply fields edited by the form
    o2.A = o.A;

    // Submit
    m_DataContext.SubmitChanges();
}

Этот подход не требует какой-либо специальной обработки в контроллере (т. е. привязки пользовательской модели), но по-прежнему требует, чтобы для свойства Update Check было установлено значение Never в файле dbml.

person Brad Robinson    schedule 09.09.2009

Вы можете добавить поле метки времени и проверить его на странице с полем в БД (также скрывая поле метки времени). Если пользователь обновил запись, возвращается ошибка параллелизма, и страница обновляется или остается неизменной после изменений пользователей.

person Russell    schedule 08.09.2009
comment
У меня сложилось впечатление, что он хочет разрешить одновременное редактирование в разных полях. - person JoshJordan; 08.09.2009
comment
Да, я хочу разрешить одновременное редактирование, и мне интересно, как это сделать правильно с точки зрения Linq. Если бы я использовал прямой SQL, это было бы легко, конечно, это можно как-то сделать с Linq. - person Brad Robinson; 08.09.2009
comment
Хорошо, я не уверен, как это сделать в Linq. - person Russell; 09.09.2009