使用Lambda或Linq为MVC5中的剃刀视图创建带有选定项的SelectList模型



我使用EF代码和MVC5 c#为我的web应用程序。我已经创建了一个db表,其中包含几十个设置的主定义,这些设置用于在整个应用程序中指定输入和输出参数。

我还创建了一个以用户ID为键的表。创建的每个用户都有默认设置,可以随时使用用户偏好(razor)查看页面更改。

现在我正试图为这个视图页面的控制器动作编码。大多数用户偏好都需要从下拉列表中进行选择,但这正是我感到困惑的地方。

// UOM Master Table
public class UOMMaster
{
    public int Id { get; set; }
    public string MeasureId { get; set; }         // Text used as labels
    public double SortOrder { get; set; }         // User preferences sort order
    public string UOMId { get; set; }             // UOM abbreviation
    public bool IsUserPreference { get; set; }    // true if is a stored user preference
}

上面的主表数据可能看起来像:

1,   "AbsolutePress",   1,   "psi",    true
2,   "AbsolutePress",   1,   "kPaa",   true
3,   "FluidFlow",       2,   "GPM",    true
4,   "FluidFlow",       2,   "m^3/Hr", true
5,   "FluidFlow",       2,   "l/s",    true
etc, etc...

自定义标识,包含:

public class ApplicationUser : IdentityUser
{
    // Default Units of Measure
    public virtual ICollection<AspNetUserUOMDefault> UOMDefaults { get; set; }
}

首选用户默认值存储在以Identity的UserId为键的表中:

public class AspNetUserUOMDefault
{
    [Key]
    public virtual ApplicationUser UserProfile { get; set; }
    public int Id { get; set; }
    public string MeasureId { get; set; }
    public string PreferredUomId { get; set; }
}

使用上面的表格,我需要创建一个剃刀视图,显示下拉列表来显示用户的首选设置:

                  +--------------+---+
AbsolutePress:    | psi          | V |     <-- Dropdown list containing 
                  +--------------+---+         all options available for
                  | psi              |         AbsolutePress as defined in
                  | kPaa             |         master table, with user's
                  +------------------+         preferred setting selected.
FluidFlow:        +--------------+---+
                  | m^3/Hr       | V |     <-- Dropdown list containing
                  +--------------+---+         all options available for
                  | GPM              |         FluidFlow as defined in
                  | m^3/Hr           |         master table, with user's
                  | l/s              |         preferred setting selected.
                  +------------------+
etc, etc...

GET控制器动作让我感到困惑。我只是不知道如何形成返回到剃刀视图的模型,但它需要由SortOrder(升序)排序,并包含SelectList的IList/iccollection(也许??)包含以下内容(我不确定如何做到这一点):

UOMPreferences
    MeasureId
    optionsList
        UomId
        UomId (Selected)
        UomId
UOMPreferences
    MeasureId
    optionsList
        UomId (Selected)
        UomId
   etc, etc..

View模板需要按照SortOrder顺序使用下拉列表显示Model的内容。我设想这是动态的,这样就可以将首选项添加到数据库表中,而不需要对Controller Actions或razor View模板进行任何更改。

在视图中将有一个foreach(我不确定如何做到这一点):

@foreach (var preference in Model.UOMPreferences)
{
<div class="form-group">
    @Html.LabelFor(preference.MeasureId, new { @class = "col-md-3 control-label" })
    <div class="col-md-4">
        @Html.DropDownListFor(
            preference.MeasureId,
            new SelectList(preference.optionsList, "Value", "Text"), 
            new { @class = "form-control" })
    </div>
</div>
}

有没有人有经验和时间为我提供关于如何实现这一目标的代码片段建议?也许我最大的困惑是在控制器动作本身(GET和POST)。我真的不关心模型是用Lambda还是Linq创建的,但我只是无法想象如何做到这一点-我确信它可以做到。我的数据库模型可能会得到改进,尽管我认为它们会起作用。

根据您的附加意见,我建议采用不同的结构。

首先,数据库表
AbsolutePressOptions: ID(PK), Name, IsDefault(bit)
FluidFlowOptions: ID(PK), Name(varchar), IsDefault(bit)
//more tables for other options types
UserOptions: UserID(PK), AbsolutePressPreference(FK), FluidFlowPreference(FK), etc

由于每个选项类型的值不太可能改变,您还可以考虑使用枚举而不是创建数据库表,例如

public enum AbsolutePressOption
{
  psi,
  kpa
}

用户选项类(基于使用enum)

public class UserOptions
{
  public UserOptions()
  {
    // Set defaults
    AbsolutePressPreference = AbsolutePressOption.psi;
  }
  public AbsolutePressOption AbsolutePressPreference { get; set; }
  public FluidFlowOption FluidFlowPreference { get; set; }
}
用户

public class User
{
  public User()
  {
    // Assign default options when user initialised
    Options = new UserOptions();
  }
  .... // properties of user (ID, Name etc.)
  public UserOptions Options { get; set; }
}

如果用户采用默认值,则不需要将任何内容保存到数据库中。要改变它们的选项,你的控制器可以像

这样
[HttpGet]
public ActionResult UserOptions(int ID)
{
  User user = UserRepository.Get(ID); // get user and user options from database
  ViewBag.AbsolutePressOptions = new SelectList(....
  return View(user);
}

注意,要从枚举中为MVC-4创建一个SelectList,请参见这里。另外,最好创建一个包含SelectLists属性的ViewModel。

视图
@model YourAssembly.User
// Some display properties of the user (Name etc.)
....
@using (Html.BeginForm())
{
  @Html.HiddenFor(m => m.ID) // so the user ID posts back
  @Html.LabelFor(m => m.Options.AbsolutePressPreference)
  @Html.DropDownFor(m => m.Options.AbsolutePressPreference, (SelectList)ViewBag.AbsolutePressOptions)
  // More selects for other options
  <input type="submit" value="Save Options" />
}

post方法

[HttpGet]
public ActionResult UserOptions(User user)
{
  if (!ModelState.IsValid)
  {
    // Build select lists again
    return View(user)
  }
  UserRepository.SaveOptions(user) // Save options to database
  // Redirect to the user details page?
  return RedirectToAction("Details", new { ID = user.ID })
}

最后,如果用户首选项在大多数页面上使用,我会考虑创建一个CustomPrincipal,将用户首选项写入FormsAuthenticationTicket cookie,以便在每个请求中都可用。

在视图中应该是这样的

@foreach (string preference in Model.UOMPreferences.OrderBy(p=>p.SortOrder)
                                                .Select(p=>p.MeasureId )
                                                .Distinct())
{
    var preferences = Model.UOMPreferences.Where(p=>p.MeasureId = preference);
....
     @Html.DropDown(preference, new SelectList(preferences, "Id", "UOMId"))
....
}

最新更新