.NET 4.0 Linq to SQL — обновление объекта не работает

#asp.net-mvc #linq-to-sql #repository

#asp.net-mvc #linq-to-sql #репозиторий

Вопрос:

Я создаю простое приложение MVC Movie, используя шаблон репозитория и библиотеку классов для моих классов Linq to SQL. Кажется, я не могу заставить свои объекты ОБНОВЛЯТЬСЯ обратно в базу данных.. Я что-то упускаю, теперь уверен, что это такое:

     public class MovieRepository : BaseRepository, IMovieRepository
    {

        /// <summary>
        /// Updates the specified movie.
        /// </summary>
        public void Update()
        {
            GetDataContext.SubmitChanges();
        }

        /// <summary>
        /// Fetches the by id.
        /// </summary>
        /// <param name="id">The id.</param>
        public Movie FetchById(int id)
        {
            Movie movie = (from n in GetDataContext.Movies
                           where n.ID == id
                           select n).First();

            return movie;
        }
}
  

BaseRepository.cs

 public abstract class BaseRepository
{
    private static VideoStoreDBDataContext _videoStoreDbDataContext;

    protected static VideoStoreDBDataContext GetDataContext
    {
        get
        {
            if (_videoStoreDbDataContext == null)
            {
                _videoStoreDbDataContext = new VideoStoreDBDataContext();
            }

            return _videoStoreDbDataContext;
        }
    }
}
  

HomeController

 public ActionResult EditMovie(int Id)
{
    Movie movie = _movieRepository.FetchById(Id);

    if (movie == null)
        return RedirectToAction("Error", "Home");

    return View(movie);
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditMovie(Movie movie)
{
    if (!ModelState.IsValid)
        return View(movie);

// NOTE: movie object does infact contain changes made using the VIEW.

    _movieRepository.Update();

    return RedirectToAction("Index");
}
  

Вид

 <% using (Html.BeginForm()) {%>

   <fieldset>
        <legend>Details</legend>
        <p>
            <label for="Title">Title:</label><br/>
                <%= Html.TextBox("Title", Model.Title) %>
                <%= Html.ValidationMessage("Title", "*") %>
        </p>

        <p>
            <input type="submit" value="Update Movie" />
        </p>
    </fieldset>

<% } %>

<div>
    <%=Html.ActionLink("Back to List", "Index") %>
</div>
  

Ответ №1:

В вашем методе EditMovie объект movie , который вы получаете в качестве аргумента, на самом деле не является объектом, привязанным к базе данных. Он создается для вас средой выполнения MVC, и вы DataContext не знаете об этом. Поэтому, когда вы вызываете Update() , DataContext не видит никаких изменений для записи в базу данных.

Вместо этого вам следует найти этот объект в базе данных, затем скопировать в него все поля из аргумента метода и затем вызвать Update() . Вот так:

 [AcceptVerbs(HttpVerbs.Post)]
public ActionResult EditMovie(Movie movie)
{
    if (!ModelState.IsValid)
        return View(movie);

    var existingMovie = _movieRepository.FetchById( movie.Id );
    existingMovie.Title = movie.Title;
    _movieRepository.Update();

    return RedirectToAction("Index");
}
  

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

     <legend>Details</legend>
    <p>
        <label for="Title">Title:</label><br/>
            <%= Html.TextBox("Title", Model.Title) %>
            <%= Html.ValidationMessage("Title", "*") %>
            <%= Html.HiddenFor( m => m.Id ) %>   //<------
    </p>
  

РЕДАКТИРОВАТЬ: как указал Mystere Man, вам не нужно добавлять это скрытое поле, если ваш URL содержит идентификатор.

Комментарии:

1. На самом деле, он мог бы просто добавить параметр id в метод post, чтобы получить идентификатор из URL, ему не нужно делать это скрытым.

2. @Mystere: если параметр находится в URL, то ему на самом деле не нужно добавлять параметр в метод. Параметры URL будут автоматически сопоставлены с объектом модели. Однако, поскольку в сообщении не указано, какая конфигурация маршрутизации используется, и, учитывая явно низкий опыт работы с OP, я предпочитаю перестраховаться здесь.

Ответ №2:

Вы забываете, что http — это система без состояния. Каждая страница, которая обслуживается, является отдельным запросом, и каждый набор объектов уничтожается в конце каждого запроса.

Таким образом, объекты, возвращаемые вашим get, не существуют в вашем post, потому что это полностью отдельный запрос. На самом деле, привязка модели по умолчанию создает новый экземпляр вашего объекта Movie, а не изменяет содержимое того, который вы ранее вернули.

Таким образом, обновление не будет работать, потому что L2S не знает, что ваш вновь созданный объект Movie должен быть обновлен.