#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 должен быть обновлен.