Part of ASP.NET MVC application data save not being applied - asp.net

I am writing an MVC application, and I wanted to do some extra formatting so the phone numbers all are stored the same. To accomplish this I made a simple external function to strip all non-numeric characters and return the formatted string:
public static string FormatPhone(string phone)
{
string[] temp = { "", "", "" };
phone = Regex.Replace(phone, "[^0-9]","");
temp[0] = phone.Substring(0, 3);
temp[1] = phone.Substring(3, 3);
temp[2] = phone.Substring(6);
return string.Format("({0}) {1}-{2}", temp[0], temp[1], temp[2]);
}
There is also regex in place in the model to make sure the entered phone number is a valid one:
[Required(ErrorMessage = "Phone Number is required.")]
[DisplayName("Phone Number:")]
[StringLength(16)]
[RegularExpression("^\\(?([0-9]{3})\\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$",
ErrorMessage = "Please enter a valid phone number.")]
public object phone { get; set; }
This is what I did in the controller:
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var customer = customerDB.Customers.Single(c => c.id == id);
try
{
customer.phone = HelperFunctions.FormatPhone(customer.phone);
UpdateModel(customer,"customer");
customerDB.SaveChanges();
return RedirectToAction("Index");
}
catch
{
var viewModel = new CustomerManagerViewModel
{
customer = customerDB.Customers.Single(c => c.id == id)
};
return View(viewModel);
}
}
When I step through this, the string updates then resets back to the format it was before being ran through the function. Also, any of the other fields update with no problem.
Any ideas? Thanks in advance.

Your UpdateModel call is overwriting the customer field. Try swapping these two lines of code:
try
{
UpdateModel(customer,"customer"); <--
customer.phone = HelperFunctions.FormatPhone(customer.phone); <--
customerDB.SaveChanges();
return RedirectToAction("Index");
}

Related

Old ASP.NET code works on one computer, not on another?

So in my global.asax, I've got the following code:
Inventory.BusinessTier bt = new Inventory.BusinessTier();
string UserLogin = bt.ExtractLogin (Request.ServerVariables ["AUTH_USER"]);
Inventory.User myUser = new Inventory.User (UserLogin);
Session ["User"] = myUser;
It works just fine on one development PC, but using the same version of Visual Studio, it craps out on the third line with this error:
System.TypeInitializationException: 'The type initializer for
'Inventory.DataTier' threw an exception.'
Inner Exception
NullReferenceException: Object reference not set to an instance of an
object.
Other than a line adding impersonation in my web.config (it has to be there now), I haven't changed a single thing. Is there a way to get more info on this? I can't even trace it, because if I put a debug line in the User object constructor, it never hits it. I'm at a bit of a loss. Would appreciate any advice.
EDIT to answer questions below:
InventoryUser is a very simple user object that reads the current from the database and stores some basic user info in properties, such as UserID, Role, RoleID, and IsAdmin.
The DataTier class is a class that interacts with the database. It is used in multiple projects, so I'm quite sure it's not the problem. I tried to paste in the code anyway, but it exceeded the limit for a post.
I'm reasonably sure the problem is related to the user class. It's short, so I can paste it in here:
using System;
using System.Data;
// This is the user business object. It contains information pertaining to the current user of the application. Notably, it
// contains the department ID, which determines what inventory items the user will see when using the application. Only
// specified employees with admin access can see all items for all departments, and that is determined by a specific department ID.
namespace Inventory {
public class User {
private Guid _UserID;
private Guid _RoleID;
private Guid _UserDepartmentID;
private string _UserRole = "";
private string _UserName = "";
private bool _IsAuthorizedUser = false;
private bool _IsAdmin = false;
// Attribute declarations
public Guid UserID {
get {
return _UserID;
}
set {
_UserID = value;
}
}
public string UserRole {
get {
return _UserRole;
}
set {
_UserRole = value;
}
}
public Guid RoleID {
get {
return _RoleID;
}
set {
_RoleID = value;
}
}
public string UserName {
get {
return _UserName;
}
set {
_UserName = value;
}
}
public Guid UserDepartmentID {
get {
return _UserDepartmentID;
}
set {
_UserDepartmentID = value;
}
}
public bool IsAdmin {
get {
return _IsAdmin;
}
set {
_IsAdmin = value;
}
}
public bool IsAuthorizedUser {
get {
return _IsAuthorizedUser;
}
set {
_IsAuthorizedUser = value;
}
}
// -----------------
// - Constructor -
// -----------------
public User (string UserLogin) {
string ShortUserLogin = ExtractLogin (UserLogin);
GetUser (ShortUserLogin);
}
// ------------------
// - ExtractLogin -
// ------------------
public string ExtractLogin (string Login) {
// The domain and "\" symbol must be removed from the string, leaving only the user name.
int pos = Login.IndexOf (#"\");
return Login.Substring (pos + 1, Login.Length - pos - 1);
}
// -------------
// - GetUser -
// -------------
// This method is called to fill the user object based on the user's login. It ultimately gets authorized user data
// from the user table.
public void GetUser (string UserName) {
DataTier dt1 = new DataTier();
DataTable dt = dt1.GetUserInfo (UserName);
int RecordCount = dt.Rows.Count;
switch (RecordCount) {
case 1: // There is one user name match, as there should be. This is the likely situation.
DataRow dr = dt.Rows[0];
UserID = (Guid)dr ["UserID"];
UserRole = (string)dr ["UserRole"];
RoleID = (Guid)dr ["RoleID"];
this.UserName = UserName;
UserDepartmentID = (Guid)dr ["DepartmentID"];
IsAdmin = (bool)dr ["IsAdmin"];
IsAuthorizedUser = true;
break;
case 0: // There are no user name matches (unauthorized use).
IsAdmin = false;
IsAuthorizedUser = false;
break;
default: // There are multiple user name matches (problem!).
IsAdmin = false;
IsAuthorizedUser = false;
break;
}
}
}
}

Redirecting to the correct page after submitting a form in ASP.NET MVC

I have the same
#Html.ActionLink("SUSPEND", "Suspend", "Serials", new { id = s.serial, orderId = s.Order.orderID }, null)
on two different pages.
Let's say I can click on it from this page:
http://localhost:55058/Customers/Details/4106
and from this page:
http://localhost:55058/Orders/Details/102091
The link takes me to a form associated to two standard Controller actions, GET:
public ActionResult Suspend(string id, string orderId)
{
Serial serial = context.Serials.Single(x => x.serialID == id);
ViewBag.orderId = orderId;
return View(serial);
}
and POST:
[HttpPost]
public ActionResult Suspend(Serial serial, string orderId)
{
if (ModelState.IsValid)
{
serial.suspended = true;
serial.suspensionDate = DateTime.Now;
context.Entry(serial).State = EntityState.Modified;
context.SaveChanges();
ViewBag.orderId = orderId;
return View(context.Serials.Single(x => x.serialID == serial.serialID));
}
}
How do I Redirect() to the page where I first clicked the link, once I submitted the form? Possibly, in an elegant way...
Thanks.

entity-framework error: An object with the same key already exists in the ObjectStateManager

I got thiserror
An object with the same key already exists in the ObjectStateManager.
The ObjectStateManager cannot track multiple objects with the same
key.
only because this line
oldProject = db.Projectes.Find(project.ID);
I need it in order to get the object before the edit, and afterwards compare the values changes between before and after the edit, I do it in the third if statement (that compare one values), and in the "SFSiteEmailSend.ProjectEdited" function that only check the changes and send email about it.
By the way, I never change the "oldproject", the error still exist even if I delete all the email funcation and the third(last) if statement.
// POST: /Project/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Project project)
{
if (ModelState.IsValid)
{
// For Email Send - start
string UrlProject = Request.Url.GetLeftPart(UriPartial.Authority) + Url.Action("Detail", "Project", new { id = project.ID });
Project oldProject = new Project();
Project newProject = new Project();
newProject = project;
oldProject = db.Projectes.Find(project.ID);
SFSiteEmailSend.ProjectEdited(oldProject, newProject, UrlProject);
// For Email Send - end
if (oldProject.Finished == false && newProject.Finished == true)
{
project.DateFinished = DateTime.Now;
}
db.Entry(project).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(project);
}
Your code is full of redundancy. Strip some of it out and it should become easier to spot the error:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(Project newProject)
{
if (ModelState.IsValid)
{
string UrlProject = Request.Url.GetLeftPart(UriPartial.Authority)
+ Url.Action("Detail", "Project", new { id = newProject.ID });
//both oldProject and newProject have the same Key but only oldProject
//is attached
Project oldProject = db.Projectes.Find(newProject.ID);
SFSiteEmailSend.ProjectEdited(oldProject, newProject, UrlProject);
if (!oldProject.IsFinished && newProject.IsFinished )
newProject.DateFinished = DateTime.Now;
//now you try to attach another project with the same Key.
//It will fail when you call SaveChanges
db.Entry(newProject).State = EntityState.Modified;
//This is an option:
d.Entry(oldProject).State = EntityState.Detached;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(newProject);
}
or you could load oldProject with AsNoTracking in the first place - as you already found out
try
project.entitykey = oldproject.entitykey before savechanges
I found a solution
Instead
public ActionResult Edit(Project project)
{
Project beforeEditProject = db.Projectes.Find(project.ID);
....
}
}
Use
public ActionResult Edit(Project project)
{
Project beforeEditProject = db.Projectes.AsNoTracking().First(p => p.ID == project.ID);
.....
}

Save data instead of adding to database

I'm trying to edit an article in my asp.net mvc project. This is what I do when I create a project:
public ActionResult Create(ArticleViewModel model)
{
if (ModelState.IsValid)
{
try
{
// Get the userID who created the article
User usr = userrepo.FindByUsername(User.Identity.Name);
model.UsernameID = usr.user_id;
repository.AddArticle(model.Title, model.Description, model.ArticleBody);
}
catch (ArgumentException ae)
{
ModelState.AddModelError("", ae.Message);
}
return RedirectToAction("Index");
}
return View(model);
}
In my repository:
public void AddArticle(string Title, string Description, string ArticleBody)
{
item Item = new item()
{
item_title = Title,
item_description = Description,
article_body = ArticleBody,
item_createddate = DateTime.Now,
item_approved = false,
user_id = 1,
district_id = 2,
link = "",
type = GetType("Article")
};
try
{
AddItem(Item);
}
catch (ArgumentException ae)
{
throw ae;
}
catch (Exception)
{
throw new ArgumentException("The authentication provider returned an error. Please verify your entry and try again. " +
"If the problem persists, please contact your system administrator.");
}
Save();
// Immediately persist the User data
}
public void AddItem(item item)
{
entities.items.Add(item);
}
But now I want to edit an article, this is what I have till now:
public ActionResult Edit(int id)
{
var model = repository.GetArticleDetails(id);
return View(model.ToList());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ArticleViewModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the User
try
{
item Item = repository.GetArticleDetailsByTitle(model.Title);
Item.item_title = model.Title;
Item.item_description = model.Description;
Item.article_body = model.ArticleBody.
// HERE I NEED TO SAVE THE NEW DATA
return RedirectToAction("Index", "Home");
}
catch (ArgumentException ae)
{
ModelState.AddModelError("", ae.Message);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
As you can see I check the adjusted text and drop it in "Item". But how can I save this in my database? (the function in my repository)
I think your save() method had entityobject.SaveChanches()
So you want to call that save() method in here
try
{
item Item = repository.GetArticleDetailsByTitle(model.Title);
Item.item_title = model.Title;
Item.item_description = model.Description;
Item.article_body = model.ArticleBody.
**Save();**
return RedirectToAction("Index", "Home");
}
should be need to only Save() method, could not need to AddItem() method .
I'm afraid you'll need to get article again from database, update it and save changes. Entity framework automatically tracks changes to entites so in your repository should be:
public void EditArticle(Item article)
{
var dbArticle = entities.items.FirstOrDefault(x => x.Id == article.Id);
dbArticle.item_title = article.item_title;
//and so on
//this is what you call at the end
entities.SaveChanges();
}

C# Error: Cannot read from a Closed reader...?

I have this code below. Gets data and sets data property to the values gathered.
public struct TrblShootData
{
public List<string> Logins;
public IEnumerable<Hieracrhy> Hierarchy;
public IEnumerable<EmployeeTeam> EmpTeam;
}
public TrblShootData TroubleShootData
{
get;
private set;
}
class DataGetter
{
public void GetData(string FirstName, string LastName, string Login, string Email, bool isFirstName, bool isLastName, bool isLogin, bool isEmail)
{
List<string> logins = null;
IEnumerable<Hieracrhy> hier = null;
IEnumerable<EmployeeTeam> tmemp = null;
TrblShootData tsData = new TrblShootData();
queries q = BuildQuery(FirstName, LastName, Login, Email, isFirstName, isLastName, isLogin, isEmail);
if (q.isValidQueries)
{
DataContext1 mscDB = new DataContext1 ();
using (DataContext2 opsDB = new DataContext2 ())
{
tmemp = opsDB.ExecuteQuery<EmployeeTeam>(q.qryEmployeeTeam);
}
using (DataContext3 rptDB = new DataContext3 ())
{
hier = rptDB.ExecuteQuery<Hieracrhy>(q.qryHierarchy);
if (hier != null)
{
logins = hier.Select(s => s.SalesRepLogin).Distinct().ToList();
}
}
tsData.EmpTeam = tmemp.Select(r=>r);
tsData.Hierarchy = hier.Select(r => r);
tsData.Logins = logins.Select(r => r).ToList();
TroubleShootData = tsData;
}//if
}
}
From another class I attempt to do this:
tshtr.GetData(txtFirstName.Text, txtLastName.Text, txtLogin.Text, txtEmail.Text, chkFirstName.Checked, chkLastName.Checked, chkLogin.Checked, chkEmail.Checked);
gvEmpTeams.DataSource = tshtr.TroubleShootData.EmpTeam;
gvHierarchy.DataSource = tshtr.TroubleShootData.Hierarchy;
gvEmpTeams.DataBind();
gvHierarchy.DataBind();
But at the DataBind() I get an error saying that I cannot read from a closed reader.
I'm not seeing why it would throw this error when I've set my property as above after I've assigned the values in the usings. So I'm not seeing how this is trying to use a closed reader.
Thanks for any help!
Because of deferred execution, your query only executes when the data-binding engine enumerates its results, after you close the DataContext.
You need to call .ToList() before closing the DataContext to force it to be evaluated immediately.

Resources