问题描述:

I am creating a system where when a user is created a device can be assigned to that user. I am doing this with a dropdown and while the dropdown is populated with the list of devices, it does not update the user with that device. Multiple users can use the same device and i want to avoid hard coding as the information is already on the database.

I am doing this through ef code first any other suggestions will be taken into account. Im really just focused on getting this to work so i am open to suggestions. I also want to get this working with edit, however im assuming the solution will fix both create and edit, if i use similar code.

I have been using a viewmodel to display the devices on the index page. The current users on the db show their allocated devices so i know that works, but i had to manually assign the devices in the table data.

PatientController.cs (users are also known as patients)

namespace FaceToFaceWebsite.Controllers

{

public class PatientController : Controller

{

private F2FData db = new F2FData();

public ActionResult Index()

{

var viewModel = db.Users

.Select(u => new UserDeviceViewModel()

{

User = u,

Device = u.Device

}).ToList();

return View(viewModel);

}

public ActionResult Create()

{

ViewBag.DeviceID = new SelectList(db.Devices, "DeviceID", "Name");

return View();

}

[HttpPost]

[ValidateAntiForgeryToken]

public ActionResult Create([Bind(Include = "UserID,CodeName,UseBriefInstructions,DeviceID,Name")] User user, Device device)

{

if (ModelState.IsValid)

{

db.Users.Add(user);

db.Devices.Attach(device);

db.SaveChanges();

return RedirectToAction("Index");

}

return View(user);

}

User.cs (in another project, but in the same solution)

namespace FaceToFace.Model

{

public class User

{

public int UserID { get; set; }

public string CodeName { get; set; }

public bool UseBriefInstructions { get; set; }

public ICollection<RegimeItem> RegimeItems { get; set; }

public Device Device { get; set; }

public virtual ICollection<Grading> UserGradings { get; set; }

public User()

{

this.RegimeItems = new List<RegimeItem>();

Device = new Device();

}

}

public class RegimeItem

{

public int RegimeItemID { get; set; }

public Exercise RegimeExercise { get; set; }

}

}

Device.cs (in another project, but in the same solution)

namespace FaceToFace.Model

{

public class Device

{

public int DeviceID { get; set; }

public string Name { get; set; }

}

}

UserDeviceViewModel.cs

namespace FaceToFaceWebsite.Models

{

public class UserDeviceViewModel

{

public Device Device { get; set; }

public User User { get; set; }

public IList<SelectListItem> Name { get; set; }

}

}

View/Patient/Create.cshtml

 @model FaceToFace.Model.User

@{

ViewBag.Title = "Create";

}

<h2>Create</h2>

@using (Html.BeginForm()) {

@Html.AntiForgeryToken()

@Html.ValidationSummary(true)

<fieldset>

<legend>User</legend>

<div class="editor-label">

@Html.LabelFor(model => model.CodeName)

</div>

<div class="editor-field">

@Html.EditorFor(model => model.CodeName)

@Html.ValidationMessageFor(model => model.CodeName)

</div>

<div class="editor-label">

@Html.LabelFor(model => model.Device.Name, "Device")

</div>

<div class="editor-field">

@Html.DropDownListFor(model => model.Device.DeviceID, (IEnumerable<SelectListItem>)ViewBag.DeviceID)

</div>

<div class="editor-label">

@Html.LabelFor(model => model.UseBriefInstructions)

</div>

<div class="editor-field">

@Html.EditorFor(model => model.UseBriefInstructions)

@Html.ValidationMessageFor(model => model.UseBriefInstructions)

</div>

<p>

<input type="submit" value="Create" />

</p>

</fieldset>

}

<div>

@Html.ActionLink("Back to List", "Index")

</div>

I believe the problem lies with what ive been doing in the Controller, and after many tries i cannot get the create function to assign a user to a device from the db so any help would be greatly appreciated.

UPDATE

PatientController.cs

private void PopulateDeviceChoices(UserDeviceViewModel model)

{

model.DeviceChoices = db.Devices.Select(m => new SelectListItem

{

Text = m.Name,

Value = m.DeviceID

});

}

//

// GET: /Patient/

public ActionResult Index()

{

//var viewModel = new List<FaceToFaceWebsite.Models.UserDeviceViewModel>();

var viewModel = db.Users

.Select(u => new UserDeviceViewModel()

{

User = u,

Device = u.Device

}).ToList();

return View(viewModel);

}

public ActionResult Create(UserDeviceViewModel model, User user)

{

ViewBag.DeviceID = new SelectList(db.Devices, "DeviceID", "Name");

var device = db.Devices.Find(model.DeviceId);

user.Device = device;

PopulateDeviceChoices(model);

return View(model);

}

//

// POST: /Patient/Create

[HttpPost]

[ValidateAntiForgeryToken]

public ActionResult Create(User user, Device device, UserDeviceViewModel model)

{

if (ModelState.IsValid)

{

db.Users.Add(user);

db.Devices.Attach(device);

db.SaveChanges();

return RedirectToAction("Index");

}

PopulateDeviceChoices(model);

return View(model);

}

I have made the changes as Chris' suggestions, however i dont think i have translated them correctly, as i am getting an error at Value = m.DeviceID which is telling me Cannot implicitly convert type 'int' to 'string'

I have got it working however, i am getting two sets of user input rows on the view, it also does not allow me to assign a device to a user still.

Update 2 (Stephens suggestions)

PatientController.cs

public ActionResult Create()

{

UserDeviceCreateViewModel model = new UserDeviceCreateViewModel();

ConfigureViewModel(model);

return View(model);

}

public ActionResult Create(UserDeviceCreateViewModel model, User user, Device device)

{

if (!ModelState.IsValid)

{

ConfigureViewModel(model);

return View(model);

}

db.Users.Add(user);

db.SaveChanges();

return RedirectToAction("Index");

}

网友答案:

The error you are getting is because property DeviceID is typeof int but the Value property is typeof string so it would need to be

model.DeviceChoices = db.Devices.Select(m => new SelectListItem
{
    Text = m.Name,
    Value = m.DeviceID.ToString()
});

However you have numerous other issues with you code, for example your Create() GET method has parameters which not only are unnecessary, but its properties will be null, your view is based on User but your POST method also has parameters for Device and UserDeviceViewModel, both of which will be null, and if you ever add validation attributes to any of your models, ModelState is likely to be invalid. As always, you should be using a view model to represent what you want to display/edit

public class UserCreateVM
{
  [Display(Name = "Code Name")]
  [Required(ErrorMessage = "Please enter a code name")]
  public string CodeName { get; set; }
  [Display(Name = Use Brief Instructions?")]
  public bool UseBriefInstructions { get; set; }
  [Display(Name = Device")]
  [Required(ErrorMessage = "Please select a device")]
  public int? SelectedDevice { get; set; }
  public SelectList DeviceList { get; set; }
}

Controller

public ActionResult Create()
{
  UserCreateVM model = new UserCreateVM();
  ConfigureViewModel(model);
  return View(model);
}

[HttpPost]
public ActionResult Create(UserCreateVM model)
{
  if (!ModelState.IsValid)
  {
    ConfigureViewModel(model);
    return View(model);
  }
  // Initialize new instance of the data model
  User user = new User();
  // Map properties from view model to data model
  user.CodeName = model.CodeName;
  user.UseBriefInstructions = model.UseBriefInstructions;
  user.Device = db.Devices.Find(model.SelectedDevice);
  // Save and redirect
  db.Users.Add(user);
  db.SaveChanges();
  return RedirectToAction("Index");
}

private void ConfigureViewModel(UserCreateVM model)
{
  model.DeviceList = new SelectList(db.Devices, "DeviceID", "Name");
}

View

@model UserCreateVM
@using (Html.BeginForm())
{
  ....
  @Html.LabelFor(m => m.CodeName)
  @Html.TextBoxFor(m => m.CodeName)
  @Html.ValidationMessageFor(m => m.CodeName)

  @Html.LabelFor(m => m.SelectedDevice)
  @Html.DropDownListFor(m => m.SelectedDevice, Model.DeviceList, "-Please select-")
  @Html.ValidationMessageFor(m => m.SelectedDevice)

  @Html.LabelFor(m => m.UseBriefInstructions)
  @Html.CheckboxFor(m => m.UseBriefInstructions)
  @Html.ValidationMessageFor(m => m.UseBriefInstructions)
  ....
}
网友答案:

Remove the Device property from your view model. You're not adding/editing a device here, merely associating it with a user.

Then, you need to add an IEnumerable<SelectListItem> to your view model containing possible device options:

public IEnumerable<SelectListItem> DeviceChoices { get; set; }

You'll initialize this list in your GET and POST actions, so you should probably factor out the initialization logic into it's own private method in your controller:

private void PopulateDeviceChoices(UserDeviceViewModel model)
{
    model.DeviceChoices = db.Devices.Select(m => new SelectListItem
    {
        Text = m.Name,
        Value = m.Id
    };
}

Then, in your actions, call this method before returning your view:

PopulateDeviceChoices(model);
return View(model);

There's a slight hiccup in the fact that you did not define an explicit foreign key property for Device on User. You don't have to, but it makes things much easier. If you were to add an explicit property, you could just do:

@Html.DropDownListFor(m => m.User.DeviceId, Model.DeviceChoices)

However, since you don't have this explicit property, you have to take a few extra steps. First, you need to add a property to your view model to hold the posted id:

public int DeviceId { get; set; }

Then, in your view, you'd render your drop down list via:

@Html.DropDownListFor(m => m.DeviceId, Model.DeviceChoices)

Finally, in your action, you must use this id to look up the device from the database, and then set that on your user:

var device = db.Devices.Find(model.DeviceId);
user.Device = device;
相关阅读:
Top