i have a project that use MVC3. in my project, i have a page that user can edit their account (UserComment, UserEmail, IsLocked, IsApproved). i already make the View for Edit Account. i have some trouble to make the Edit Controller.
here's my code:
[HttpPost]
public ActionResult Edit(string id, FormCollection collection)
{
id = id.Replace("+", " ");
var user = Membership.GetUser();
Guid UserGUID = new Guid(user.ProviderUserKey.ToString());
var SysUser = db.System_User.Single(u => u.User_UserId == UserGUID);
//this is for updating User Office in my System_user table
SysUser.User_Office = collection["SysUsers[0].UserOffice"];
//this is for updating User Account in aspnet_membership table
user.UserName = collection["SysUsers[0].UserName"];
Membership.UpdateUser(user);
user.Comment = collection["SysUsers[0].UserComment"];
Membership.UpdateUser(user);
user.Email = collection["SysUsers[0].UserEmail"];
Membership.UpdateUser(user);
return View();
}
when i run my controller, i get some error like :
user.UserName is read only, i cant update this one.
i get user.Comment value, but its not update.
i get error in my Edit View, it says "Object reference not set to an instance of an object."
#using (Html.BeginForm("edit/" + #Model.SysUsers[0].UserID, "cpanel/sysuser", FormMethod.Post))
can anyone help me ?
thanks,
You cannot update the UserName of a user using ASP.NET Membership provider. You need to write some custom data access logic if you wish to update a username.
I would suggest using the Membership Provider primarily for authentication and build all the other maintenance etc. methods into your User's business objects.
Related
I have two independent classes that model my tables. First when a new user is created, the user does not have a record in the certificate tables. So in the view for the certificates I have added a button to add certificates details for this new user.
This is my code for the user view: I omitted the paging/search and filter code to make it simple
public ActionResult Index()
var recipients = from s in db.User
select s;
return View(recipients.ToList());
This is the details view showing related data:
public ViewResult Details(int id)
{
var certificateDetails = db.Certificate.Where(p => p.ID == id);
return View(certificateDetails);
}
Adding a new user means also adding a new certificates details. I want when a user clicks details for the a particular user if those details aint around to be redirected to a create certificate view with both User.ID and CertificateID set. In fact CertificateID is AI but ID from User is foreign key.
I would have used Fluent API but am not good with it either so have to handle this seemingly small challenge in code.
If I understand your question correctly, you want it so that when you view Details(), if the certificate details don't exist, then redirect to a page to create them?
Just check whether or not the entity exists. If it doesn't, return a RedirectToAction() and pass whatever data you need in the route data collection.
public ViewResult Details(int id)
{
var certificateDetails = db.Certificate.FirstOrDefault(p => p.ID == id);
if (certificateDetails == null)
return RedirectToAction("Create", "Certificate", new { userId = id });
return View(certificateDetails);
}
You'll also need to create a Certificate controller with a Create() action.
I'm creating a small MVC application and am passing a User object from a controller to an ActionResult method in another controller. One of the attributes of the User object is a list of Property objects called Properties.
Here's the rub: when the User object is finally passed to the relevant View, it's list does not contain any properties.
Here's the setup:
User class:
public class User
{
public int Id {get;set;}
public List<Property> Properties {get;set;}
}
AccountController
public ActionResult LogOn(int userId, string cryptedHash)
{
//code to logOn (this works, promise)
User user = dbContext.getUser(userId);
//debugging shows the user contains the list of properties at this point
return RedirectToAction("UserHome", "Home", user);
}
HomeController
public ActionResult UserHome(User user)
{
ViewBag.Messaage = "Hello, " + user.Forename + "!";
return View(user); //debugging shows that user.Properties is now empty(!)
}
UserHome.cshtml View
#model emAPI.Model_Objects.User
#{
ViewBag.Title = "UserHome";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>UserHome</h2>
<div>
#Model.Forename, these are your properties:
<ul>
#foreach (var property in #Model.Properties)
{
<li>property.Name</li>
}
</ul>
</div>
The view loads without any problem - #Model.Forename is fine, but as far as HomeController is concerned user.Properties was empty when it received it, although I know it wasn't when AccountController sent it.
Any help or advice anyone has to offer would be gratefully received.
You cannot pass entire complex objects when redirecting. Only simple scalar arguments.
The standard way to achieve that is to authenticate the user by emitting a forms authentication cookie which will allow you to store the user id across all subsequent actions. Then if in a controller action you need user details such as forename or whatever you simply query your data store to retrieve the user from wherever it is stored using the id. Just take a look at the way the Account controller is implemented when you create a new ASP.NET MVC 3 application.
So:
public ActionResult LogOn(int userId, string cryptedHash)
{
//code to logOn (this works, promise)
User user = dbContext.getUser(userId);
//debugging shows the user contains the list of properties at this point
// if you have verified the credentials simply emit the forms
// authentication cookie and redirect:
FormsAuthentication.SetAuthCookie(userId.ToString(), false);
return RedirectToAction("UserHome", "Home");
}
and in the target action simply fetch the user id from the User.Identity.Name property:
[Authorize]
public ActionResult UserHome(User user)
{
string userId = User.Identity.Name;
User user = dbContext.getUser(int.Parse(userId));
ViewBag.Messaage = "Hello, " + user.Forename + "!";
return View(user);
}
Ah and please, don't use ViewBag. Use view models instead. If all that your view cares about is welcoming the user by displaying his forename simply build a view model containing the forename property and then pass this view model to the view. The view doesn't care about your User domain model and it shouldn't.
RedirectToAction method returns an HTTP 302 response to the browser, which causes the browser to make a GET request to the specified action. You should not think about passing a complex object in that to the next action method.
In this case, may be you can keep your user object in the Session variable and access it in the remaining places.
public ActionResult LogOn(int userId, string cryptedHash)
{
User user = dbContext.getUser(userId);
if(user!=null)
{
Session["LoggedInUser"]=user;
return RedirectToAction("UserHome", "Home");
}
}
public ActionResult UserHome()
{
var loggedInUser= Session["LoggedInUser"] as User;
if(loggedInUser!=null)
{
ViewBag.Messaage = "Hello, " + user.Forename + "!";
return View(user);
}
return("NotLoggedIn");
}
So, I am implementing ASP Membership and Role management in my application. I also have a second User table with all non-membership related information. I set the E-mail as the username in Membership and as the foreign key in my User table.
I am customizing the registration page to include a dropdown so a manager can be selected when the account is created. The list of managers is generated by finding all Membership users with the role "Manager" then creating a collection of Users where the foreign keys match the results.
List<string> managerNames = new List<string>(Roles.GetUsersInRole("Manager"));
var managers = from m in _db.Users where managerNames.Contains(m.Email) select m;
ViewBag.managers = managers;
Now I have to use that collection of users to populate a dropdown in my view that has the Name attribute set to "ManagerID" (to match my RegistrationModel), the value of each option set to the primary key of the User, and the displayed text in the dropdown showing the DisplayName of the User model.
I can go through the tedious task of looping through my "managers" collection and populating a separate SelectListItem, then passing the SelectListItem into a #Html.DropDown("ManagerID", newSelectListItem), but that seems excessive. Is there a more direct (or acceptable) way to do this?
EDIT
I added this to my controller
var selectList = new List<SelectListItem>();
foreach (var manager in managers)
{
selectList.Add(new SelectListItem(){
Value = manager.UserID.ToString(),
Text = manager.DisplayName,
Selected = false
});
}
ViewBag.managers = selectList;
and this to my view
#Html.DropDownList("ManagerID", (List<SelectListItem>)ViewBag.managers)
and it works. Is this still the best approach?
Is this still the best approach?
No. The best approach is to use view models and forget about the existence of ViewBag/ViewData. So start by designing a view model which will meet the requirements of your view (display a ddl of managers):
public class MyViewModel
{
[Required]
public int? SelectedManagerId { get; set; }
public IEnumerable<SelectListItem> Managers { get; set; }
}
and then have your controller action populate this view model and pass it to the view:
public ActionResult Foo()
{
var managers = ... query your repository to get them
var model = new MyViewModel
{
Managers = managers.Select(x => new SelectListItem
{
Value = x.UserID.ToString(),
Text = x.DisplayName
})
};
return View(model);
}
and finally in your strongly typed view:
#model MyViewModel
...
#Html.DropDownListFor(
x => x.SelectedManagerId,
Model.Managers,
"-- Select a manager --"
)
So everytime you employ ViewBag/ViewData in an ASP.NET MVC applications an alarm should ring telling you that there is a better way.
I hope i'm not bithering with my many questions...
i'm creating users in the ASPNETDB.MDF database with the asp.net configuration.
i have a client MVC, and i want when a client is created, edited, or deleted, it must dispaly the user_name and last_user_name of the user that performed the action. These two properties r in the client model. i want my controller to only deal with the ID(PK) of the users, and not their names. I only know how to make the controller work with the user names. How do i ensure that any functionality is done with the id but the display is the user_name? Here is my create method in my clientcontroller
[Authorize]
public ActionResult Create()
{
var model = new title
{
create_dt = DateTime.Now,
last_maint_dt = DateTime.Now,
row_version = 1,
status = "ACTIVE",
user_id = System.Web.HttpContext.Current.User.Identity.Name,
last_user_id = System.Web.HttpContext.Current.User.Identity.Name
};
return View(model);
}
If you are using the default ASP.NET Membership Provider you can find the user GUID for an account by accessing System.Web.Security.Membership. Then you can use the GetUser() method and pass it the username as an argument.
//Get Current User ID
var user = System.Web.Security.Membership.GetUser(User.Identity.Name);
if (user != null)
{
string id = user.ProviderUserKey.ToString();
}
And if you have the GUID and want the Username
var user = System.Web.Security.Membership.GetUser(id);
if (user != null)
{
string username = user.UserName;
}
I've been trying to add a new record.
public ActionResult Create()
{
var dc = new ServicesDataContext();
ViewData["CustomerID"] = TempData["CustomerID"];
var a = dc.services.Select(arg => arg.ServiceID).ToList();
ViewData["ServiceID"] = new SelectList(a);
var model = new Maping();
return View(model);
}
//
// POST: /Customerservice/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude ="CustomerServiceMappingID")] Maping serviceToCreate)
{
if (!ModelState.IsValid)
return View();
var dc = new ServicesDataContext();
dc.Mapings.InsertOnSubmit(serviceToCreate);
dc.SubmitChanges();
return RedirectToAction("Index", "Home");
}
Now the situation is that the tempdata has the correct value but by the time i submit changes the customerID turns out to be null. So, kindly help me in solving this.
AFAI understand you copy the customer id from TempData to ViewData. However, the contents of the ViewData will be not preserved after the request ends. In your view you should put the customer id into an input (e.g. a hidden field if it should be not displayed) to get it back in your post action.
If you use a strongly typed model, you should not use the ViewData at all, but rather you should set the customer id on the model instance. Then in the view you could use a Html.HiddenFor(m => m.CustomerId) to "preserve" this id.
I hope I did not misunderstand the question, unfortunately it is not really visible in your code snippets where you would have a customer id in the post action that is null.