Доступ к моделям из представления в приложении MVC 2 Timesheet? - PullRequest
0 голосов
/ 25 ноября 2010

Я пытаюсь создать приложение с расписанием в MVC 2, но мне кажется, что я все еще пытаюсь понять отношения модель / представление и все такое.

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

В основном структура базы данных выглядит следующим образом:

(table) TimeSegments
TimeSegmentID
Hours
Date
ConsultantID (FK)
TaskID (FK)
ProjectID (FK)

(table) Projects
ProjectID
ProjectName

(table) Tasks
TaskID
TaskName

(table) Consultants
ConsultantID
ConsultantName

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

Теперь я передаю всю модель в представление создания (на самом деле это модель представления, основанная на ней, просто для упрощения некоторого кодирования, но это могла бы быть и вся модель).

Проблема в том, что обычно, когда я делал подобные вещи в представлении создания, я бы создал новый объект в контроллере и передал его представлению. В этом случае это был бы объект TimeSegment, поскольку это новый временной сегмент, который должен быть создан в базе данных. Тогда я мог бы просто отправить его и обновить базу данных в контроллере. Однако, если я только передам новый объект TimeSegment в представление, я не смогу заполнить раскрывающиеся списки проектами, задачами и консультантами.

И наоборот, если я передам только всю модель, как бы я связал текстовое поле с новым сегментом времени, который будет обновлен в базе данных?

Я чувствую, что для этого мне нужно отправить как новый объект TimeSegment, так и всю модель, но тогда я понятия не имею, как мне получить к нему доступ (есть только одна «Модель» для доступа из представления). Кроме того, вернитесь в контроллер после отправки, как контроллер узнает, что обновлять?

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

1 Ответ

3 голосов
/ 25 ноября 2010

Хорошо, я сделаю попытку.

MVC не сложно, но вы должны немного изменить свой образ мыслей.В MVC у вас есть Модели (ваш слой данных [s]), Представления и Контроллеры.

Прежде чем мы продолжим, я сделаю предположение с моими примерами ниже, что вы используете LINQ to SQL для доступа к данным.слой (модель), и я пометил его как dc.

Контроллеры извлекают и форматируют данные из моделей и передают их в представления для отображения.Итак, давайте начнем с вашего первого представления, которое будет представлением для создания TimeSegment.

[HttpGet]
public ActionResult CreateTimeSegment() {
    return View(new TimeSegmentView {
        Consultants = dc.Consultants.ToList(),
        Projects = dc.Projects.ToList(),
        Tasks = dc.Tasks.ToList()
    });
}

Это действие создаст объект TimeSegmentView и передаст его View как Model.Имейте в виду, что это действие украшено [HttpGet].TimeSegmentView` - это контейнерный класс для объектов, которые необходимо передать в представление для создания пользовательского интерфейса, и он выглядит следующим образом:

public class TimeSegmentView {
    public IList<Consultant> Consultants { get; set; }
    public IList<Project> Projects { get; set; }
    public IList<Task> Tasks { get; set; }
    public TimeSegment TimeSegment { get; set; }
}

ПРИМЕЧАНИЕ: Я не использую TimeSegmentсвойство еще ниже, оно еще ниже ...

В представлении убедитесь, что оно наследуется от TimeSegmentView.Предполагая, что вы следуете структуре проекта MVC по умолчанию, и я позволю себе добавить папку Views в папку Models, ваша полная ссылка будет выглядеть следующим образом:

<%@ Page Inherits="System.Web.Mvc.ViewPage<PROJECTNAME.Models.Views.TimeSegmentView>" %>

Теперь вы 'Вы ввели представление для этого объекта, и теперь вы можете взаимодействовать с его свойствами.Таким образом, вы можете построить форму, такую ​​как:

<form action="/" method="post">
    <p>
        <label>Hours</label>
        <input name="TimeSegment.Hours" />
    </p>
    <p>
        <label>Date</label>
        <input name="TimeSegment.Date" />
    </p>
    <p>
        <label>Consultant</label>
        <select name="TimeSegment.ConsultantID">
            <% foreach (Consultant C in Model.Consultants) { %>
            <option value="<%=C.ConsultantID%>"><%=C.ConsultantName%></option>
            <% }; %>
        </select>
    </p>
    <p>
        <label>Project</label>
        <select name="TimeSegment.ProjectID">
            <% foreach (Project P in Model.Projects) { %>
            <option value="<%=P.ProjectID%>"><%=P.ProjectName%></option>
            <% }; %>
        </select>
    </p>
    <p>
        <label>Task</label>
        <select name="TimeSegment.TaskID">
            <% foreach (Task T in Model.Tasks) { %>
            <option value="<%=T.TaskID%>"><%=T.TaskName%></option>
            <% }; %>
        </select>
    </p>
</form>

Как вы можете видеть, она создала 3 выбранных поля и только что выполнила циклы в каждом из них, чтобы построить свои значения на основе модели.

Теперь, принимая эту форму, нам нужно получить данные и добавить их в нашу базу данных с помощью:

[HttpPost]
public RedirectToRouteResult CreateTimeSegment(
    [Bind(Prefix = "TimeSegment", Include = "Hours,Date,ConsultantID,ProjectID,TaskID")] TimeSegment TimeSegment) {
    dc.TimeSegments.InsertOnSubmit(TimeSegment);
    dc.SubmitChanges();

    return RedirectToAction("EditTimeSegment", new {
        TimeSegmentID = TimeSegment.TimeSegmentID
    });
}

Хорошо, сначала обратите внимание, что я назвал действие тем же, но у этого есть [HttpPost] украшение.Я говорю действию, что отправляю ему объект TimeSegment и хочу, чтобы он связывал свойства в предложении Include (это в основном для безопасности и проверки).Затем я беру переданный объект TimeSegment, добавляю его в контекст данных, отправляю изменения и перенаправляю.В этом случае я перенаправляю на другое действие для редактирования только что созданного объекта, передавая новый TimeSegmentID.Вы можете перенаправить на что угодно, это мне показалось уместным ...

[HttpGet]
public ActionResult EditTimeSegment(
    int TimeSegmentID) {
    return View(new TimeSegmentView {
        Consultants = dc.Consultants.ToList(),
        Projects = dc.Projects.ToList(),
        Tasks = dc.Tasks.ToList(),
        TimeSegment = dc.TimeSegments.Single(t => t.TimeSegmentID == TimeSegmentID)
    });
}

В действии редактирования вы делаете то же самое, что и в действии создания, создавая новый объект TimeSegmentView и передавая егона вид.Ключевое отличие здесь заключается в том, что вы сейчас заполняете свойство TimeSegment.Ваша форма будет выглядеть примерно так (сокращенно сверху):

<form action="/<%=Model.TimeSegment.TimeSegmentID%>" method="post">
    <p>
        <label>Hours</label>
        <input name="TimeSegment.Hours" value="<%=Model.TimeSegment.Hours%>" />
    </p>
</form>

И ваше действие получения на контроллере будет выглядеть так:

[HttpPost]
public RedirectToRouteResult EditTimeSegment(
    int TimeSegmentID) {
    TimeSegment TS = dc.TimeSegments.Single(t => t.TimeSegmentID == TimeSegmentID);

    TryUpdateModel<TimeSegment>(TS, "TimeSegment", new string[5] {
        "Hours", "Date", "ConsultantID", "ProjectID", "TaskID"
    });

    dc.SubmitChanges();

    return RedirectToAction("EditTimeSegment", new {
        TimeSegmentID = TimeSegment.TimeSegmentID
    });
}

Наконец, если вы хотите отобразитьсписок временных сегментов, вы можете сделать что-то вроде этого:

[HttpGet]
public ActionResult ListTimeSegments() {
    return View(new TimeSegmentsView {
        TimeSegments = dc.TimeSegments.ToList()
    });
}

и TimeSegmentsView выглядит так:

public class TimeSegmentsView {
    public IList<TimeSegment> TimeSegments { get; set; }
}

И в View вы хотели бы сделать это:

<table>
    <% foreach (TimeSegment TS in Model.TimeSegments) { %>
    <tr>
        <td><%=TS.Hours%></td>
        <td><%=TS.Date%></td>
        <td><%=TS.Project.ProjectName%></td>
        <td><%=TS.Consultant.ConsultantName%></td>
        <td><%=TS.Task.TaskName%></td>
    </tr>
    <% }; %>
</table>

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

Однако я бы посоветовал вам изменить наименование свойств ваших таблиц.Например, когда вы пишете выражения, как в таблице выше, вам нужно будет сделать TS.Project.ProjectName, и это излишне.Вы уже получаете доступ к Project свойству TS через их отношения, так что вы знаете, что собираетесь работать только с Project.Это тогда делает ProjectName бессмысленным сгустком текста, заново описывающим объект, с которым вы работаете.Вместо этого просто используйте Name и измените выражение на TS.Project.Name.

В любом случае, просто предложение, делайте то, что вам больше нравится.Я теряю сознание, так что спокойной ночи и счастливого Дня Благодарения!

ОБНОВЛЕНИЕ

Процесс с коллекциями по сути такой же, как и со стороны контроллера.Сложнее запустить клиентскую часть и JavaScript, поэтому я предполагаю, что у вас уже есть что-то установленное.

Итак, вот как будет работать контроллер. Вы передаете массив TimeSegment, и механизм связывания достаточно умен, чтобы вычислить его через Префикс элементов формы.

<form action="/<%=Model.TimeSegment.TimeSegmentID%>" method="post">
    <p>
        <label>Hours</label>
        <input name="TimeSegment[0].Hours" />
        <!-- Notice the array in the prefix -->
    </p>
    <p>
        <label>Hours</label>
        <input name="TimeSegment[1].Hours" />
        <!-- Notice the array in the prefix -->
    </p>
</form>

и контроллер:

[HttpPost]
public RedirectToRouteResult CreateTimeSegments(
    [Bind(Prefix = "TimeSegment", Include = "Hours,Date,ConsultantID,ProjectID,TaskID")] TimeSegment[] TimeSegments) {
    dc.TimeSegments.InsertAllOnSubmit(TimeSegments);
    dc.SubmitChanges();

    return RedirectToAction("ListTimeSegments");
}

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

ОБНОВЛЕНИЕ 2

Я полагаю, что вы можете сделать IList<TimeSegment> вместо TimeSegment[] без проблем, но, насколько это лучше, это для обсуждения. На мой взгляд, браузер по-прежнему отправляет на сервер массив virtual , поэтому действие, получающее массив, выглядит естественным, но вам решать, что вы хотите использовать.

Итак, общее действие списка будет выглядеть так:

[HttpPost]
public RedirectToRouteResult CreateTimeSegments(
    [Bind(Prefix = "TimeSegment", Include = "Hours,Date,ConsultantID,ProjectID,TaskID")] IList<TimeSegment> TimeSegments) {
    dc.TimeSegments.InsertAllOnSubmit(TimeSegments);
    dc.SubmitChanges();

    return RedirectToAction("ListTimeSegments");
}

Имейте в виду, что я раньше не использовал это (имеется в виду IList), поэтому я не могу гарантировать, что это сработает, просто размышляя ...

ОБНОВЛЕНИЕ 3

То, что вы хотите сделать с Consultant, очень похоже на то, что я делаю с Cookies. У меня есть BaseView класс, который является типом, используемым Site.Master, и тогда все другие представления расширяются от него. В BaseView у меня есть свойство Cookie, которое всегда заполняется каждым действием контроллера. Затем я использую это свойство, чтобы получить идентификатор авторизованного пользователя.

Итак, в коде это выглядит так (на примерах из одного из моих приложений):

public class BaseView {
    // Don't confuse with an HttpCookie, this is an object in my database...
    public Cookie Cookie { get; set;}
}

public class EmployeeView : BaseView {
    public Employee Employee { get; set; }
}

И с этим, скажем, я хочу добавить заметку сотруднику, моя форма будет выглядеть так, где я прохожу в скрытом поле, где ваше ConsultantID входит в игру.

<form>
    <input type="hidden" name="Note.AuthorId" value="<%=Model.Cookie.EmployeeId%>" />
    <!-- other fields -->
</form>

Надеюсь, это поможет.

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