在 MVC 5 的 ASP.NET Identity 3 管理自定义用户属性
Manage Custom User Properties at ASP.NET Identity 3 in MVC 5
我目前正在练习 ASP.NET MVC 5 中的 Identity 3.0 - 遵循了一些教程并创建了一个 MVC 项目,到目前为止我已经管理了以下内容:
- 在注册页面添加了自定义属性
- 已将用户 ID 从字符串转换为整数
- 迁移和修改连接字符串以使用 SQL 服务器
- 在“管理帐户”页面显示自定义属性
接下来我想让用户能够在“管理帐户”页面上编辑 his/her 自己的自定义属性,但我现在很困惑 - 谁能告诉我如何从这里开始?
更新:通过参考下面提供的 Archil 示例,我更新了我的代码,如下所示以管理自定义属性:
ManageController.cs
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Net;
using System.Web;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using IdentityMVC.Models;
namespace IdentityMVC.Controllers
{
[Authorize]
public class ManageController : Controller
{
ApplicationDbContext db = new ApplicationDbContext();
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
public ManageController()
{
}
public ManageController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
{
UserManager = userManager;
SignInManager = signInManager;
}
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
//
// GET: /Manage/Index
public async Task<ActionResult> Index(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set."
: message == ManageMessageId.Error ? "An error has occurred."
: message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added."
: message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
: "";
var userId = User.Identity.GetUserId<int>();
var user = UserManager.FindById(userId);
var model = new IndexViewModel
{
HasPassword = HasPassword(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
Logins = await UserManager.GetLoginsAsync(userId),
Email = await UserManager.GetEmailAsync(userId),
Surname = user.Surname,
GivenName = user.GivenName,
Honors = user.Honors,
Address1 = user.Address1,
Address2 = user.Address2,
City = user.City,
State = user.State,
Postal = user.Postal,
Country = user.Country,
PSANo = user.PSANo,
Id = user.Id,
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(User.Identity.GetUserId())
};
return View(model);
}
//
// GET: /User/Edit/1
public async Task<ActionResult> Edit()
{
var myUserId = User.Identity.GetUserId();
int myUserIdint = Convert.ToInt32(myUserId);
var myUser = db.Users.FirstOrDefault(x => x.Id == myUserIdint);
if (myUserId == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var user = await UserManager.FindByIdAsync(myUserIdint);
if (user == null)
{
return HttpNotFound();
}
var userRoles = await UserManager.GetRolesAsync(user.Id);
return View(new EditUserViewModel()
{
Id = user.Id,
Surname = user.Surname,
GivenName = user.GivenName,
Honors = user.Honors,
Address1 = user.Address1,
Address2 = user.Address2,
City = user.City,
State = user.State,
Postal = user.Postal,
Country = user.Country,
PSANo = user.PSANo,
});
}
//
// POST: /User/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "Surname,GivenName,Honors,Address1,Address2,City,State,Postal,Country,PSANo")] EditUserViewModel editUser)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
if (user == null)
{
return HttpNotFound();
}
user.Surname = editUser.Surname;
user.GivenName = editUser.GivenName;
user.Honors = editUser.Honors;
user.Address1 = editUser.Address1;
user.Address2 = editUser.Address2;
user.City = editUser.City;
user.State = editUser.State;
user.Postal = editUser.Postal;
user.Country = editUser.Country;
user.PSANo = editUser.PSANo;
var userRoles = await UserManager.GetRolesAsync(user.Id);
var result = await UserManager.AddToRolesAsync(user.Id);
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
return View();
}
result = await UserManager.RemoveFromRolesAsync(user.Id, userRoles.Except(userRoles).ToArray<string>());
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
return View();
}
return RedirectToAction("Index");
}
ModelState.AddModelError("", "Something failed.");
return View();
}
//
// POST: /Manage/RemoveLogin
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RemoveLogin(string loginProvider, string providerKey)
{
ManageMessageId? message;
var result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId<int>(), new UserLoginInfo(loginProvider, providerKey));
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
message = ManageMessageId.RemoveLoginSuccess;
}
else
{
message = ManageMessageId.Error;
}
return RedirectToAction("ManageLogins", new { Message = message });
}
//
// GET: /Manage/AddPhoneNumber
public ActionResult AddPhoneNumber()
{
return View();
}
//
// GET: /Manage/ChangePassword
public ActionResult ChangePassword()
{
return View();
}
//
// POST: /Manage/ChangePassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ChangePassword(ChangePasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId<int>(), model.OldPassword, model.NewPassword);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess });
}
AddErrors(result);
return View(model);
}
//
// GET: /Manage/SetPassword
public ActionResult SetPassword()
{
return View();
}
//
// POST: /Manage/SetPassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SetPassword(SetPasswordViewModel model)
{
if (ModelState.IsValid)
{
var result = await UserManager.AddPasswordAsync(User.Identity.GetUserId<int>(), model.NewPassword);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess });
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Manage/ManageLogins
public async Task<ActionResult> ManageLogins(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
: message == ManageMessageId.Error ? "An error has occurred."
: "";
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
if (user == null)
{
return View("Error");
}
var userLogins = await UserManager.GetLoginsAsync(User.Identity.GetUserId<int>());
var otherLogins = AuthenticationManager.GetExternalAuthenticationTypes().Where(auth => userLogins.All(ul => auth.AuthenticationType != ul.LoginProvider)).ToList();
ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1;
return View(new ManageLoginsViewModel
{
CurrentLogins = userLogins,
OtherLogins = otherLogins
});
}
//
// POST: /Manage/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
return new AccountController.ChallengeResult(provider, Url.Action("LinkLoginCallback", "Manage"), User.Identity.GetUserId());
}
//
// GET: /Manage/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null)
{
return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
var result = await UserManager.AddLoginAsync(User.Identity.GetUserId<int>(), loginInfo.Login);
return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
protected override void Dispose(bool disposing)
{
if (disposing && _userManager != null)
{
_userManager.Dispose();
_userManager = null;
}
base.Dispose(disposing);
}
#region Helpers
// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
private bool HasPassword()
{
var user = UserManager.FindById(User.Identity.GetUserId<int>());
if (user != null)
{
return user.PasswordHash != null;
}
return false;
}
private bool HasPhoneNumber()
{
var user = UserManager.FindById(User.Identity.GetUserId<int>());
if (user != null)
{
return user.PhoneNumber != null;
}
return false;
}
public enum ManageMessageId
{
AddPhoneSuccess,
ChangePasswordSuccess,
SetTwoFactorSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
RemovePhoneSuccess,
Error
}
#endregion
}
}
ManageViewModels.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
namespace IdentityMVC.Models
{
public class IndexViewModel
{
public bool HasPassword { get; set; }
public IList<UserLoginInfo> Logins { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
public int Id { get; set; }
public string Email { get; set; }
public string Surname { get; set; }
public string GivenName { get; set; }
public string Honors { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Postal { get; set; }
public string Country { get; set; }
public string PSANo { get; set; }
}
public class EditUserViewModel
{
public int Id;
[Required]
[Display(Name = "Surname / Family Name*")]
public string Surname { get; set; }
[Required]
[Display(Name = "Given Name")]
public string GivenName { get; set; }
[Display(Name = "Honors")]
public string Honors { get; set; }
[Required]
[Display(Name = "Address Line 1")]
public string Address1 { get; set; }
[Display(Name = "Address Line 2")]
public string Address2 { get; set; }
[Required]
[Display(Name = "City")]
public string City { get; set; }
[Required]
[Display(Name = "State")]
public string State { get; set; }
[Required]
[Display(Name = "Postal")]
public string Postal { get; set; }
[Required]
[Display(Name = "Country")]
public string Country { get; set; }
[Display(Name = "PSA Membership No. (if applicable)")]
public string PSANo { get; set; }
}
public class ManageLoginsViewModel
{
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationDescription> OtherLogins { get; set; }
}
public class FactorViewModel
{
public string Purpose { get; set; }
}
public class SetPasswordViewModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class ChangePasswordViewModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class AddPhoneNumberViewModel
{
[Required]
[Phone]
[Display(Name = "Phone Number")]
public string Number { get; set; }
}
public class VerifyPhoneNumberViewModel
{
[Required]
[Display(Name = "Code")]
public string Code { get; set; }
[Required]
[Phone]
[Display(Name = "Phone Number")]
public string PhoneNumber { get; set; }
}
public class ConfigureTwoFactorViewModel
{
public string SelectedProvider { get; set; }
public ICollection<System.Web.Mvc.SelectListItem> Providers { get; set; }
}
}
View\Manage\Edit.cshtml
@model IdentityMVC.Models.EditUserViewModel
@{
ViewBag.Title = "Edit";
}
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Edit My Profile</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.Id)
<div class="form-group">
@Html.LabelFor(model => model.Surname, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Surname, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Surname, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.GivenName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.GivenName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.GivenName, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Honors, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Honors, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Honors, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Address1, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Address1, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Address1, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Address2, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Address2, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Address2, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.City, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.City, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.City, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.State, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.State, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.State, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Postal, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Postal, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Postal, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Country, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Country, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Country, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.PSANo, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.PSANo, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.PSANo, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to Profile", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
美好的一天,我已经做到了,它对我有用:
我的编辑用户模型:
public class ManageEditUserViewModel
{
public string Id { get; set; }
[Required]
[Display(Name = "FirstName")]
public string FirstName { get; set; }
[Required]
[Display(Name = "LastName")]
public string LastName { get; set; }
public string DisplayName { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "UserName")]
public string UserName { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "Email")]
[EmailAddress]
public string Email { get; set; }
[Display(Name = "BirthDate")]
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime BirthDate { get; set; }
[NotMapped]
public int Day { get; set; }
[NotMapped]
public int Month { get; set; }
[NotMapped]
public int Year { get; set; }
public int Gender { get; set; }
}
这是我的控制器(GET 和 Post):
//
// GET: /User/Edit/1
public async Task<ActionResult> Edit()
{
var myUserId = User.Identity.GetUserId();
var myUser = db.Users.FirstOrDefault(x => x.Id == myUserId);
if (myUserId == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var user = await UserManager.FindByIdAsync(myUserId);
if (user == null)
{
return HttpNotFound();
}
var userRoles = await UserManager.GetRolesAsync(user.Id);
return View(new ManageEditUserViewModel()
{
Id = user.Id,
UserName = user.UserName,
Email = user.Email,
FirstName = user.FirstName,
LastName = user.LastName,
DisplayName = user.FirstName + " " + user.LastName,
Gender = user.Gender,
BirthDate = user.BirthDate,
Year = user.BirthDate.Year,
Month = user.BirthDate.Month,
Day = user.BirthDate.Day,
});
}
//
// POST: /User/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "FirstName,LastName,DisplayName,Gender,UserName,Email,Id,Year,Month,Day")] ManageEditUserViewModel editUser)
{
DateTime dob = new DateTime(editUser.Year, editUser.Month, editUser.Day);
if (ModelState.IsValid)
{
var user = await UserManager.FindByIdAsync(editUser.Id);
if (user == null)
{
return HttpNotFound();
}
user.UserName = editUser.UserName;
user.Email = editUser.Email;
user.FirstName = editUser.FirstName;
user.LastName = editUser.LastName;
user.DisplayName = editUser.FirstName + "<span> </span>" + editUser.LastName;
user.Gender = editUser.Gender;
user.BirthDate = dob;
var userRoles = await UserManager.GetRolesAsync(user.Id);
var result = await UserManager.AddToRolesAsync(user.Id);
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
return View();
}
result = await UserManager.RemoveFromRolesAsync(user.Id, userRoles.Except(userRoles).ToArray<string>());
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
return View();
}
var name = user.DisplayName;
var message = editsuccess.Content;
context.Clients.All.addNewMessageToPage(name, message);
return RedirectToAction("MyProfile");
}
ModelState.AddModelError("", "Something failed.");
return View();
}
最后查看:
@model MyApp.Models.ManageEditUserViewModel
@{
ViewBag.Title = "EditUser";
}
<div class="page-header">
<h3 class="text-center text-uppercase">EditUser</h3>
</div>
<div class="row">
<div class="col-sm-3">
</div>
<div class="col-sm-6">
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
@Html.ValidationSummary(true)
@Html.HiddenFor(model => model.Id)
@Html.HiddenFor(model => model.UserName)
@Html.HiddenFor(model => model.Email)
<div class="form-group">
@Html.LabelFor(model => model.FirstName, new { @class = "control-label col-md-2" })
<div class="col-md-6">
@Html.TextBoxFor(m => m.FirstName, new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.FirstName)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.LastName, new { @class = "control-label col-md-2" })
<div class="col-md-6">
@Html.TextBoxFor(m => m.LastName, new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.LastName)
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-md-6">
<strong>@Html.LabelFor(model => model.BirthDate) </strong>
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-md-6">
@Html.DropDownListFor(model => model.Day, Enumerable.Range(1, 31).Select(i => new SelectListItem { Value = i.ToString(), Text = i.ToString() }), "Day")
@Html.DropDownListFor(model => model.Month, Enumerable.Range(1, 12).Select(i => new SelectListItem { Value = i.ToString(), Text = System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat.GetMonthName(i) }), "Month")
@Html.DropDownListFor(model => model.Year, Enumerable.Range(1900, 114).Select(i => new SelectListItem { Value = i.ToString(), Text = i.ToString() }), "Year")
@Html.ValidationMessageFor(model => model.BirthDate, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-md-6">
<label>
@Html.RadioButtonFor(x => x.Gender, 1) Male
</label>
<label>
@Html.RadioButtonFor(x => x.Gender, 2) Female
</label>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-7">
<input type="submit" value="Save Changes" class="btn btn-default" /> <span> </span> @Html.ActionLink("Back", "Index", null, new { @class = "btn btn-default" })
</div>
</div>
</div>
}
</div>
<div class="col-sm-3">
</div>
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
它工作正常你也可以定义你的属性,回家它会帮助你。
我目前正在练习 ASP.NET MVC 5 中的 Identity 3.0 - 遵循了一些教程并创建了一个 MVC 项目,到目前为止我已经管理了以下内容:
- 在注册页面添加了自定义属性
- 已将用户 ID 从字符串转换为整数
- 迁移和修改连接字符串以使用 SQL 服务器
- 在“管理帐户”页面显示自定义属性
接下来我想让用户能够在“管理帐户”页面上编辑 his/her 自己的自定义属性,但我现在很困惑 - 谁能告诉我如何从这里开始?
更新:通过参考下面提供的 Archil 示例,我更新了我的代码,如下所示以管理自定义属性:
ManageController.cs
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Net;
using System.Web;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using IdentityMVC.Models;
namespace IdentityMVC.Controllers
{
[Authorize]
public class ManageController : Controller
{
ApplicationDbContext db = new ApplicationDbContext();
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
public ManageController()
{
}
public ManageController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
{
UserManager = userManager;
SignInManager = signInManager;
}
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
//
// GET: /Manage/Index
public async Task<ActionResult> Index(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
: message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set."
: message == ManageMessageId.Error ? "An error has occurred."
: message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added."
: message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
: "";
var userId = User.Identity.GetUserId<int>();
var user = UserManager.FindById(userId);
var model = new IndexViewModel
{
HasPassword = HasPassword(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
Logins = await UserManager.GetLoginsAsync(userId),
Email = await UserManager.GetEmailAsync(userId),
Surname = user.Surname,
GivenName = user.GivenName,
Honors = user.Honors,
Address1 = user.Address1,
Address2 = user.Address2,
City = user.City,
State = user.State,
Postal = user.Postal,
Country = user.Country,
PSANo = user.PSANo,
Id = user.Id,
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(User.Identity.GetUserId())
};
return View(model);
}
//
// GET: /User/Edit/1
public async Task<ActionResult> Edit()
{
var myUserId = User.Identity.GetUserId();
int myUserIdint = Convert.ToInt32(myUserId);
var myUser = db.Users.FirstOrDefault(x => x.Id == myUserIdint);
if (myUserId == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var user = await UserManager.FindByIdAsync(myUserIdint);
if (user == null)
{
return HttpNotFound();
}
var userRoles = await UserManager.GetRolesAsync(user.Id);
return View(new EditUserViewModel()
{
Id = user.Id,
Surname = user.Surname,
GivenName = user.GivenName,
Honors = user.Honors,
Address1 = user.Address1,
Address2 = user.Address2,
City = user.City,
State = user.State,
Postal = user.Postal,
Country = user.Country,
PSANo = user.PSANo,
});
}
//
// POST: /User/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "Surname,GivenName,Honors,Address1,Address2,City,State,Postal,Country,PSANo")] EditUserViewModel editUser)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
if (user == null)
{
return HttpNotFound();
}
user.Surname = editUser.Surname;
user.GivenName = editUser.GivenName;
user.Honors = editUser.Honors;
user.Address1 = editUser.Address1;
user.Address2 = editUser.Address2;
user.City = editUser.City;
user.State = editUser.State;
user.Postal = editUser.Postal;
user.Country = editUser.Country;
user.PSANo = editUser.PSANo;
var userRoles = await UserManager.GetRolesAsync(user.Id);
var result = await UserManager.AddToRolesAsync(user.Id);
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
return View();
}
result = await UserManager.RemoveFromRolesAsync(user.Id, userRoles.Except(userRoles).ToArray<string>());
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
return View();
}
return RedirectToAction("Index");
}
ModelState.AddModelError("", "Something failed.");
return View();
}
//
// POST: /Manage/RemoveLogin
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RemoveLogin(string loginProvider, string providerKey)
{
ManageMessageId? message;
var result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId<int>(), new UserLoginInfo(loginProvider, providerKey));
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
message = ManageMessageId.RemoveLoginSuccess;
}
else
{
message = ManageMessageId.Error;
}
return RedirectToAction("ManageLogins", new { Message = message });
}
//
// GET: /Manage/AddPhoneNumber
public ActionResult AddPhoneNumber()
{
return View();
}
//
// GET: /Manage/ChangePassword
public ActionResult ChangePassword()
{
return View();
}
//
// POST: /Manage/ChangePassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ChangePassword(ChangePasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId<int>(), model.OldPassword, model.NewPassword);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess });
}
AddErrors(result);
return View(model);
}
//
// GET: /Manage/SetPassword
public ActionResult SetPassword()
{
return View();
}
//
// POST: /Manage/SetPassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SetPassword(SetPasswordViewModel model)
{
if (ModelState.IsValid)
{
var result = await UserManager.AddPasswordAsync(User.Identity.GetUserId<int>(), model.NewPassword);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess });
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
//
// GET: /Manage/ManageLogins
public async Task<ActionResult> ManageLogins(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
: message == ManageMessageId.Error ? "An error has occurred."
: "";
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId<int>());
if (user == null)
{
return View("Error");
}
var userLogins = await UserManager.GetLoginsAsync(User.Identity.GetUserId<int>());
var otherLogins = AuthenticationManager.GetExternalAuthenticationTypes().Where(auth => userLogins.All(ul => auth.AuthenticationType != ul.LoginProvider)).ToList();
ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1;
return View(new ManageLoginsViewModel
{
CurrentLogins = userLogins,
OtherLogins = otherLogins
});
}
//
// POST: /Manage/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
return new AccountController.ChallengeResult(provider, Url.Action("LinkLoginCallback", "Manage"), User.Identity.GetUserId());
}
//
// GET: /Manage/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null)
{
return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
var result = await UserManager.AddLoginAsync(User.Identity.GetUserId<int>(), loginInfo.Login);
return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
protected override void Dispose(bool disposing)
{
if (disposing && _userManager != null)
{
_userManager.Dispose();
_userManager = null;
}
base.Dispose(disposing);
}
#region Helpers
// Used for XSRF protection when adding external logins
private const string XsrfKey = "XsrfId";
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
private bool HasPassword()
{
var user = UserManager.FindById(User.Identity.GetUserId<int>());
if (user != null)
{
return user.PasswordHash != null;
}
return false;
}
private bool HasPhoneNumber()
{
var user = UserManager.FindById(User.Identity.GetUserId<int>());
if (user != null)
{
return user.PhoneNumber != null;
}
return false;
}
public enum ManageMessageId
{
AddPhoneSuccess,
ChangePasswordSuccess,
SetTwoFactorSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
RemovePhoneSuccess,
Error
}
#endregion
}
}
ManageViewModels.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
namespace IdentityMVC.Models
{
public class IndexViewModel
{
public bool HasPassword { get; set; }
public IList<UserLoginInfo> Logins { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
public int Id { get; set; }
public string Email { get; set; }
public string Surname { get; set; }
public string GivenName { get; set; }
public string Honors { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Postal { get; set; }
public string Country { get; set; }
public string PSANo { get; set; }
}
public class EditUserViewModel
{
public int Id;
[Required]
[Display(Name = "Surname / Family Name*")]
public string Surname { get; set; }
[Required]
[Display(Name = "Given Name")]
public string GivenName { get; set; }
[Display(Name = "Honors")]
public string Honors { get; set; }
[Required]
[Display(Name = "Address Line 1")]
public string Address1 { get; set; }
[Display(Name = "Address Line 2")]
public string Address2 { get; set; }
[Required]
[Display(Name = "City")]
public string City { get; set; }
[Required]
[Display(Name = "State")]
public string State { get; set; }
[Required]
[Display(Name = "Postal")]
public string Postal { get; set; }
[Required]
[Display(Name = "Country")]
public string Country { get; set; }
[Display(Name = "PSA Membership No. (if applicable)")]
public string PSANo { get; set; }
}
public class ManageLoginsViewModel
{
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationDescription> OtherLogins { get; set; }
}
public class FactorViewModel
{
public string Purpose { get; set; }
}
public class SetPasswordViewModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class ChangePasswordViewModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class AddPhoneNumberViewModel
{
[Required]
[Phone]
[Display(Name = "Phone Number")]
public string Number { get; set; }
}
public class VerifyPhoneNumberViewModel
{
[Required]
[Display(Name = "Code")]
public string Code { get; set; }
[Required]
[Phone]
[Display(Name = "Phone Number")]
public string PhoneNumber { get; set; }
}
public class ConfigureTwoFactorViewModel
{
public string SelectedProvider { get; set; }
public ICollection<System.Web.Mvc.SelectListItem> Providers { get; set; }
}
}
View\Manage\Edit.cshtml
@model IdentityMVC.Models.EditUserViewModel
@{
ViewBag.Title = "Edit";
}
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Edit My Profile</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.Id)
<div class="form-group">
@Html.LabelFor(model => model.Surname, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Surname, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Surname, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.GivenName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.GivenName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.GivenName, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Honors, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Honors, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Honors, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Address1, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Address1, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Address1, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Address2, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Address2, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Address2, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.City, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.City, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.City, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.State, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.State, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.State, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Postal, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Postal, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Postal, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Country, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Country, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Country, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.PSANo, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.PSANo, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.PSANo, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to Profile", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
美好的一天,我已经做到了,它对我有用: 我的编辑用户模型:
public class ManageEditUserViewModel
{
public string Id { get; set; }
[Required]
[Display(Name = "FirstName")]
public string FirstName { get; set; }
[Required]
[Display(Name = "LastName")]
public string LastName { get; set; }
public string DisplayName { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "UserName")]
public string UserName { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "Email")]
[EmailAddress]
public string Email { get; set; }
[Display(Name = "BirthDate")]
[DataType(DataType.Date), DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
public DateTime BirthDate { get; set; }
[NotMapped]
public int Day { get; set; }
[NotMapped]
public int Month { get; set; }
[NotMapped]
public int Year { get; set; }
public int Gender { get; set; }
}
这是我的控制器(GET 和 Post):
//
// GET: /User/Edit/1
public async Task<ActionResult> Edit()
{
var myUserId = User.Identity.GetUserId();
var myUser = db.Users.FirstOrDefault(x => x.Id == myUserId);
if (myUserId == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var user = await UserManager.FindByIdAsync(myUserId);
if (user == null)
{
return HttpNotFound();
}
var userRoles = await UserManager.GetRolesAsync(user.Id);
return View(new ManageEditUserViewModel()
{
Id = user.Id,
UserName = user.UserName,
Email = user.Email,
FirstName = user.FirstName,
LastName = user.LastName,
DisplayName = user.FirstName + " " + user.LastName,
Gender = user.Gender,
BirthDate = user.BirthDate,
Year = user.BirthDate.Year,
Month = user.BirthDate.Month,
Day = user.BirthDate.Day,
});
}
//
// POST: /User/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include = "FirstName,LastName,DisplayName,Gender,UserName,Email,Id,Year,Month,Day")] ManageEditUserViewModel editUser)
{
DateTime dob = new DateTime(editUser.Year, editUser.Month, editUser.Day);
if (ModelState.IsValid)
{
var user = await UserManager.FindByIdAsync(editUser.Id);
if (user == null)
{
return HttpNotFound();
}
user.UserName = editUser.UserName;
user.Email = editUser.Email;
user.FirstName = editUser.FirstName;
user.LastName = editUser.LastName;
user.DisplayName = editUser.FirstName + "<span> </span>" + editUser.LastName;
user.Gender = editUser.Gender;
user.BirthDate = dob;
var userRoles = await UserManager.GetRolesAsync(user.Id);
var result = await UserManager.AddToRolesAsync(user.Id);
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
return View();
}
result = await UserManager.RemoveFromRolesAsync(user.Id, userRoles.Except(userRoles).ToArray<string>());
if (!result.Succeeded)
{
ModelState.AddModelError("", result.Errors.First());
return View();
}
var name = user.DisplayName;
var message = editsuccess.Content;
context.Clients.All.addNewMessageToPage(name, message);
return RedirectToAction("MyProfile");
}
ModelState.AddModelError("", "Something failed.");
return View();
}
最后查看:
@model MyApp.Models.ManageEditUserViewModel
@{
ViewBag.Title = "EditUser";
}
<div class="page-header">
<h3 class="text-center text-uppercase">EditUser</h3>
</div>
<div class="row">
<div class="col-sm-3">
</div>
<div class="col-sm-6">
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
@Html.ValidationSummary(true)
@Html.HiddenFor(model => model.Id)
@Html.HiddenFor(model => model.UserName)
@Html.HiddenFor(model => model.Email)
<div class="form-group">
@Html.LabelFor(model => model.FirstName, new { @class = "control-label col-md-2" })
<div class="col-md-6">
@Html.TextBoxFor(m => m.FirstName, new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.FirstName)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.LastName, new { @class = "control-label col-md-2" })
<div class="col-md-6">
@Html.TextBoxFor(m => m.LastName, new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.LastName)
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-md-6">
<strong>@Html.LabelFor(model => model.BirthDate) </strong>
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-md-6">
@Html.DropDownListFor(model => model.Day, Enumerable.Range(1, 31).Select(i => new SelectListItem { Value = i.ToString(), Text = i.ToString() }), "Day")
@Html.DropDownListFor(model => model.Month, Enumerable.Range(1, 12).Select(i => new SelectListItem { Value = i.ToString(), Text = System.Globalization.CultureInfo.InvariantCulture.DateTimeFormat.GetMonthName(i) }), "Month")
@Html.DropDownListFor(model => model.Year, Enumerable.Range(1900, 114).Select(i => new SelectListItem { Value = i.ToString(), Text = i.ToString() }), "Year")
@Html.ValidationMessageFor(model => model.BirthDate, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-md-6">
<label>
@Html.RadioButtonFor(x => x.Gender, 1) Male
</label>
<label>
@Html.RadioButtonFor(x => x.Gender, 2) Female
</label>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-7">
<input type="submit" value="Save Changes" class="btn btn-default" /> <span> </span> @Html.ActionLink("Back", "Index", null, new { @class = "btn btn-default" })
</div>
</div>
</div>
}
</div>
<div class="col-sm-3">
</div>
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
它工作正常你也可以定义你的属性,回家它会帮助你。