Avoid redundancy in multiple controllers in C# code - c#

I'm writing an application that inserts data into 6 different databases. Each database has its model and controller and views.
The problem is all of them have the same methods with a slight difference here and there but in general, they are all the same. This is redundancy and a lot of similar code in each controller.
Is there a way to reduce that? Would you suggest a design pattern to study to implement it here because I feel what I'm doing here is a bad programming practice?
Am I correct or just paranoid? Thanks
controller 1:
namespace Manage_account.Controllers
{
public class HRLoanController : Controller
{
private Entities db = new Entities();
string EmployeeLogin = System.Security.Principal.WindowsIdentity.GetCurrent().Name.Split('\\')[1];
[HttpGet]
public ActionResult Create()
{
ViewBag.DepID = new SelectList(db.Departments , "DepID", "Department1");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(AccountVM vM)
{
if (ModelState.IsValid)
{
#region Insert in account and lob of HRLoan database
var chk_accountName = db.Accounts.Any(x => x.AccountName == vM.Accountex.Name);
try
{
if (!chk_accountName)
{
Account account = new Account();
account.AccountName = vM.Accountex.Name;
account.IsActive = true;
db.Accounts.Add(account);
db.SaveChanges();
TempData["success"] = "account saved successfully !";
}
else
{
TempData["warning"] = "Data already exists";
}
var Find_AccountId = db.Accounts.Where(x => x.AccountName == vM.Accountex.Name).FirstOrDefault();
var theID = Find_AccountId.AccountID;
var chk_lobName = db.LOBs.Where(x => x.AccountID == theID).Any(x => x.LOB1 == vM.LOBex.Name);
if (!chk_lobName)
{
LOB oB = new LOB();
oB.LOB1 = vM.LOBex.Name;
oB.LOBValue = vM.LOBex.Name;
oB.IsActive = true;
oB.AccountID = theID;
oB.DEPID = vM.Accountex.Dep_ID;
db.LOBs.Add(oB);
db.SaveChanges();
TempData["success"] = "lob saved successfully !";
}
else
{
TempData["warning"] = "Data already exists";
}
}
catch
{
TempData["error"] = "Something went wrong :( .Please, try again later.";
}
#endregion
}
return RedirectToAction("Create");
}
public ActionResult CreateLob()
{
ViewBag.DepID = new SelectList(db.Departments, "DepID", "Department1");
ViewBag.AccountID = new SelectList(db.Accounts.OrderBy(x=>x.AccountName), "AccountID" , "AccountName");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateLob(LOBex lOBex)
{
#region insert in Lob table in HRLoan database
if (ModelState.IsValid)
{
try
{
var chk_LobName = db.LOBs.Where(x => x.AccountID == lOBex.Account_ID).Any(x => x.LOB1 == lOBex.Name);
if (!chk_LobName)
{
LOB oB = new LOB();
oB.LOB1 = lOBex.Name;
oB.LOBValue = lOBex.Name;
oB.IsActive = true;
oB.DEPID = lOBex.DEPID;
oB.AccountID = lOBex.Account_ID;
db.LOBs.Add(oB);
db.SaveChanges();
TempData["success"] = "Data saved to LOB Table !";
}
else
{
TempData["warning"] = "Data already exists !";
}
}
catch
{
TempData["danger"] = "Something went wrong :(. Please, try again later.";
}
}
return RedirectToAction("CreateLob");
#endregion
}
[HttpGet]
public ActionResult CreateUser()
{
ViewBag.RoleID = new SelectList(db.Roles.OrderBy(x => x.Role1), "RoleID", "Role1");
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateUser(AddUser addUser)
{
#region Insert new user in Users table in HRLoan database
if (ModelState.IsValid)
{
var chk_user_existence = db.Users.Any(x => x.UserID == addUser.UserID); //check if user exists in db or not
if (!chk_user_existence)
{
User user = new User();
user.UserID = addUser.UserID;
user.UserName = addUser.UserName;
user.UserEmail = addUser.UserEmail;
user.IsActive = true;
user.InsertedOn = DateTime.Now;
user.UpdatedBy = EmployeeLogin;
user.GradeID = addUser.GradeID;
user.RoleID = addUser.RoleID;
db.Users.Add(user);
db.SaveChanges();
}
else
{
TempData["warning"] = "This user already exists in Users table !";
}
}
else
{
TempData["error"] = "Something went wrong :/ . Please, try again later!";
}
return RedirectToAction("CreateUser");
#endregion
}
[HttpGet]
public ActionResult CreateBoss_Department()
{
var get_Admin = db.Users.Where(x => x.RoleID == 1).Where(x=>x.IsActive==true).ToList();
ViewBag.UserID = new SelectList(get_Admin.OrderBy(x=>x.UserName) , "UserID", "UserName");
ViewBag.Indexs = new SelectList(db.LOBs , "Indexs", "LOB1");
ViewBag.AccountID = new SelectList(db.Accounts.OrderBy(x=>x.AccountName) , "AccountID", "AccountName");
return View();
}
#region populate user's ID with their specified names
//populate user's names
public JsonResult GetUserName_toUserLogin(string usr)
{
List<User> getUser = new List<User>();
getUser = (from r in db.Users where (r.UserID == usr) select r).ToList();
List<SelectListItem> User_ID = new List<SelectListItem>();
foreach (User name in getUser)
{
User_ID.Add(new SelectListItem { Text = name.UserID, Value = name.UserName.ToString() });
}
return Json(new SelectList(User_ID, "Value", "Text"));
}
#endregion
#region Populates Lobs according to the selected account in the Create boss-department view
//populate Lobs
public JsonResult GetLOB_toAccount(int acc)
{
List<LOB> getLob = new List<LOB>();
getLob = (from r in db.LOBs where (r.AccountID == acc) select r).ToList();
List<SelectListItem> Lobs = new List<SelectListItem>();
foreach (LOB lob in getLob)
{
Lobs.Add(new SelectListItem {Text = lob.LOB1, Value = lob.Indexs.ToString() });
}
return Json(new SelectList(Lobs , "Value" , "Text"));
}
#endregion
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateBoss_Department(Boss_Dep boss_Dep)
{
#region Insert new record for admins only in Boss_Deapartment table in HRLoan database
if (ModelState.IsValid)
{
var Chk_UsrAccess = db.Boss_Department.Where(x => x.BossLogin == boss_Dep.BossLogin).Any(x => x.LOBIndexs == boss_Dep.LOBIndexs);
if (!Chk_UsrAccess)
{
Boss_Department boss_ = new Boss_Department();
boss_.BossLogin = boss_Dep.BossLogin;
boss_.LOBIndexs = boss_Dep.LOBIndexs;
boss_.IsActive = true;
boss_.ToMail = boss_Dep.To_mail;
boss_.InsertedOn = DateTime.Now;
boss_.UpdatedBy = EmployeeLogin;
db.Boss_Department.Add(boss_);
db.SaveChanges();
TempData["success"] = "Data saved successfully !";
}
else
{
TempData["warning"] = "Data already exists in Boss_Department table !";
}
}
else
{
TempData["error"] = "something went wrong :( !";
}
return RedirectToAction("CreateBoss_Department");
#endregion
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
}
}
controller 2:
namespace Manage_account.Controllers
{
public class HrperonalController : Controller
{
private personal personal = new personal();
string EmployeeLogin = System.Security.Principal.WindowsIdentity.GetCurrent().Name.Split('\\')[1];
//GET: create
[HttpGet]
public ActionResult Create()
{
ViewBag.DepID = new SelectList(personal.Departments, "DepID", "Department");
return View();
}
//POST: create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(AccountVM vM)
{
if (ModelState.IsValid)
{
#region insert account in account table in HRPersonnal db
var chk_accountName = personal.Accounts.Any(x => x.AccountName == vM.Accountex.Name);
if (!chk_accountName)
{
Accounts accounts = new Accounts();
accounts.AccountName = vM.Accountex.Name;
accounts.IsActive = true;
personal.Accounts.Add(accounts);
personal.SaveChanges();
TempData["success"] = "Account has been saved successfully :) !";
}
else
{
TempData["warning"] = "account already exists";
}
#endregion
#region insert Lob under that account in LOB table in HRPersonnal db
var Added_account = personal.Accounts.Where(x=>x.AccountName == vM.Accountex.Name).FirstOrDefault().AccountID;
var chk_lobName = personal.LOB.Where(x => x.DEPID == vM.LOBex.DEPID)
.Where(x => x.AccountID == Added_account).Any(x => x.LOB1 == vM.LOBex.Name);
if (!chk_lobName)
{
LOB lOB = new LOB();
lOB.LOB1 = vM.LOBex.Name;
lOB.LOBValue = vM.LOBex.Name;
lOB.AccountID = Added_account;
lOB.DEPID = vM.LOBex.DEPID;
lOB.IsActive = true;
personal.LOB.Add(lOB);
personal.SaveChanges();
TempData["success"] = "Lob has been saved successfully :) !";
}
else
{
TempData["warning"] = "Lob already exists on that account";
}
#endregion
}
else
{
TempData["error"] = "Something went wrong.Please, try again later :/.";
}
return RedirectToAction("Create");
}
//GET: createLob
[HttpGet]
public ActionResult CreateLob()
{
ViewBag.AccountID = new SelectList(personal.Accounts, "AccountID", "AccountName");
ViewBag.DepID = new SelectList(personal.Departments , "DepID", "Department");
return View();
}
//POST: createLob
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateLob(LOBex oBex)
{
#region insert Lob in LOB table in HRPersonnal db
if (ModelState.IsValid)
{
var chk_Lob = personal.LOB.Where(x => x.DEPID == oBex.DEPID).Where(x => x.AccountID == oBex.Account_ID).Any(x=>x.LOB1 == oBex.Name);
if (!chk_Lob)
{
LOB lOB = new LOB();
lOB.LOB1 = oBex.Name;
lOB.LOBValue = oBex.Name;
lOB.DEPID = oBex.DEPID;
lOB.AccountID = oBex.Account_ID;
lOB.IsActive = true;
personal.LOB.Add(lOB);
personal.SaveChanges();
TempData["success"] = "Lob has been saved successfully :) ! ";
}
else
{
TempData["warning"] = "Lob already exists !";
}
}
#endregion
else
{
TempData["error"] = "Something went wrong.Please, try again later :/ !";
}
return RedirectToAction("CreateLob");
}
//GET: createuser
[HttpGet]
public ActionResult CreateUser()
{
ViewBag.RoleID = new SelectList(personal.Roles , "RoleID", "Role");
ViewBag.DepID = new SelectList(personal.Departments, "DepID", "Department");
return View();
}
//POST: createuser
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateUser(PersonnalUser user)
{
if (ModelState.IsValid)
{
var chk_user = personal.Users.Any(x => x.UserID == user.usrID);
if (!chk_user)
{
Users usr = new Users();
usr.UserID = user.usrID;
usr.UserName = user.Name;
usr.RoleID = user.Roleid;
usr.IsActive = true;
usr.GradeID = user.Gradeid;
usr.InsertedBy = EmployeeLogin;
usr.InsertedOn = DateTime.Now;
usr.Email = user.Email;
usr.PositionName = user.PositionName;
usr.DepartmentName = user.DepartmentName;
personal.Users.Add(usr);
personal.SaveChanges();
TempData["success"] = "User has been saved successfully :) !";
}
else
{
TempData["warning"] = "User already exists !";
}
}
return RedirectToAction("CreateUser");
}
//GET: create boss-department
[HttpGet]
public ActionResult CreateBoss_Dep()
{
var get_this_Admin = personal.Users.Where(x => x.RoleID == 1).Where(x => x.IsActive == true).ToList();
ViewBag.UserID = new SelectList(get_this_Admin.OrderBy(x => x.UserName), "UserID", "UserName");
ViewBag.Indexs = new SelectList(personal.LOB, "Indexs", "LOB1");
ViewBag.AccountID = new SelectList(personal.Accounts, "AccountID", "AccountName");
return View();
}
#region populate user's ID with their specified names
//populate user's names
public JsonResult GetUserName_toUserLogin(string usr)
{
List<Users> getUser = new List<Users>();
getUser = (from r in personal.Users where (r.UserID == usr) select r).ToList();
List<SelectListItem> User_ID = new List<SelectListItem>();
foreach (Users name in getUser)
{
User_ID.Add(new SelectListItem { Text = name.UserID, Value = name.UserName.ToString() });
}
return Json(new SelectList(User_ID, "Value", "Text"));
}
#endregion
#region Populates Lobs according to the selected account in the Create boss-department view
//populate Lobs
public JsonResult GetLOB_toAccount(int acc)
{
List<LOB> getLob = new List<LOB>();
getLob = (from r in personal.LOB where (r.AccountID == acc) select r).ToList();
List<SelectListItem> Lobs = new List<SelectListItem>();
foreach (LOB lob in getLob)
{
Lobs.Add(new SelectListItem { Text = lob.LOB1, Value = lob.Indexs.ToString() });
}
return Json(new SelectList(Lobs, "Value", "Text"));
}
#endregion
//POST: create boss-department
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateBoss_Dep(Boss_Dep boss_Dep)
{
#region Insert new record for admins only in Boss_Deapartment table in HRLoan database
if (ModelState.IsValid)
{
var Chk_UsrAccess = personal.Boss_Department.Where(x => x.BossLogin == boss_Dep.BossLogin).Any(x => x.LOBIndexs == boss_Dep.LOBIndexs);
if (!Chk_UsrAccess)
{
Boss_Department boss_Department = new Boss_Department();
boss_Department.BossLogin = boss_Dep.BossLogin;
boss_Department.LOBIndexs = boss_Dep.LOBIndexs;
boss_Department.IsActive = true;
boss_Department.ToMail = boss_Dep.To_mail;
boss_Department.InsertedOn = DateTime.Now;
boss_Department.UpdatedBy = EmployeeLogin;
personal.Boss_Department.Add(boss_Department);
personal.SaveChanges();
TempData["success"] = "Data saved successfully !";
}
else
{
TempData["warning"] = "Data already exists in Boss_Department table !";
}
}
else
{
TempData["error"] = "something went wrong :( !";
}
return RedirectToAction("CreateBoss_Dep");
#endregion
}
}
}

Congratulations for spotting the repetitive nature of your code. That's a good start.
On the flip side, you have a long way to go, in that, there's a lot you'll need to learn.
Upon a study of your code structure, it looks like this:
(1) Controller Class
-- (2) HTTP GET METHOD / ACTION
-- (3) HTTP POST METHOD / ACTION
-- -- (4) If the model state is valid
-- -- -- (5) If the object does not already exist in the database
-- -- -- -- (6) Do the thing. In your case, create a record in the database and save it.
-- -- -- -- (7) While doing it, if there's an error, write it to TempData.
-- -- -- (8) Else if the object already exists in the database
-- -- -- -- (9) Write an error to the temp data
-- -- -- (10) End of check if the object exists in the database
-- -- (11) Else if the model state is not valid
-- -- -- (12) Write an error to TempData
-- -- (13) End of check if model state is valid or not
-- -- (14) If while doing the whole method thing, there's an error, write it to TempData
-- (15) End of method.
Fortunately, this isn't a new problem at all, or even a difficult one to crack. It's basically the meat and potatoes of all simple, CRUD applications.
The problem is shouting loud and clear, and you have an inkling that something's wrong. Here's the problem.
All is mixed up. In sophisticated language, you're mixing concerns.
So, you need to separate out your concerns.
This is a basic design principle rather than a specific design pattern.
Separation of Concerns
You have code that sits close to the UI, i.e. your controller actions, your business or model objects, i.e. classes such as User, Account, etc. your utility classes such as methods that convert stuff to JSON and back, all in one place.
Besides, you've made even your utility methods that do the JSON and back conversion from your business objects into public methods, making them callable as actions on your controller.
All these mistakes are in violation of good design principles.
You need to separate out these, so that your code reads more like two or three lines like so:
public void PostAction(ModelObject model)
{
if (!ModelState.IsValid)
{
// Some error handling strategy common to the whole
// application that sits well with the kind of interface
// this application has. Are you making a website or is this
// a web API?
}
// You're here because the model state is valid
// Get the repository object somehow:
// (a) Either through dependency injection in the controller's constructor, and
// when you get the dependency, get it in the lowest / basest possible interface
// so that it is polymorphic.
// (b) Or as a service location strategy, which means new it up here
repositoryObject.doTheThing();
}
public class MyCentralErrorHandler : HandleErrorAttribute
{
...
}
In separating them, I noticed I applied a few patterns:
Dependency Injection
Service Location
Repository
I also applied the SOLID principle of interface seggregation. That is, I got my dependency using a polymorphic compile-time type like so:
public class MyController : Controller
{
public MyController(IRepository repositoryObject) : base()
{
}
}
So, to answer the other aspect of your question about what design pattern to apply, I'd say you're still falling in the same trap that all rookies fall. It's not applying a design pattern you need to learn at this stage. But you need to code more.
Code a lot for practice until your own intuition develops to a stage where, just like you spotted that there was something messy about your code, your own intuition clearly and unequivocally tells you boundaries of what needs to go where, and you begin to work on your intuition and at the end of following its instruction, you realize, "Hey, what I did just now, it's called such and such a design pattern. Viola! I never did it consciously."
So, in a nutshell, study them all. Learn all the design patterns for now. Learn them thoroughly and give it a few years to roll in your head. And then throw them all out of your head when you're writing code. You're not supposed to be super-imposing design patterns on your code. They're just supposed to come out of you like poop does. And believe me, it'll happen. All you need is just more years of practice.
Meanwhile, write plenty of code. Plenty of code.
Another matter-of-fact answer to your question, as I already stated above is, you need to apply these things:
The design principle of separation of concerns;
The repository pattern, which is itself a manifestation of the principle of separation of concerns;
Service Location
Dependency Injection
The SOLID principle of programming to an interface rather than to a concrete implementation
A proper error / exception handling strategy. This is a lot of thinking that's due here. You need to ask yourself a lot of questions about what kind of an interface your application has and what do you intend to do at each stage. It's not something I can write here and be done with. You need a mentor or plenty of years of practice.
One more thing I must bring to your attention is that design patterns overlap. So, there are a few more obvious design patterns that actually also go by more than one name that are already applied in the solution I highlighted above. They're basically different names for the same thing. So, these are nebulous ideas that are demarkated on the basis of their function and even the context.
For e.g just the principle of programming to an interface can also be referred to as interface seggregation though the term interface seggregation has another, wider connotation in the context of system design. The same thing can also be noticed to be polymorphism at play. And I am sure there's another pattern name that can be ascribed to the same common-sensical, obvious solution.

Related

Record not adding to SQL Server despite data being passed correctly? (ASP.NET WEB API and Angular)

I am trying to add two records to two separate entities using one action result.
My data is passing just fine, and no catches are met (implying that the database is saving the necessary changes and returning the appropriate Ok).
My two entities are related, one entity is linked to the other through a Foreign Key. The entities are created in such a way that the entity dependent on the other (for its foreign key) is created after the other.
The first entity (Branch) is created and is stored just fine as expected on the SQL Server. However, the second is not, and I am not sure why?
Both entities are set to auto-increment their primary keys.
My action result (within my web API is split into two separate methods):
// Create New Branch
[HttpPost]
[Route("CreateBranch")]
public ActionResult CreateBranch([FromBody] BranchesVM newBranch)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
var branch = new Branch
{
BranchName = newBranch.branch.BranchName,
BranchContactNumber = newBranch.branch.BranchContactNumber,
BranchEmailAddress = newBranch.branch.BranchEmailAddress,
BranchImage = newBranch.branch.BranchImage,
BranchStatus = true
};
db.Branch.Add(branch);
db.SaveChanges();
var latestBranchRecordReader = db.Branch.OrderByDescending(x => x.BranchId).First();
var latestBranchRecord = latestBranchRecordReader.BranchId;
var branchAddress = new BranchAddress
{
BranchStreetName = newBranch.address.BranchStreetName,
BranchSuburb = newBranch.address.BranchSuburb,
BranchCity = newBranch.address.BranchCity,
BranchProvince = newBranch.address.BranchProvince,
BranchCountry = newBranch.address.BranchCountry,
BranchZip = newBranch.address.BranchZip,
BranchDate = DateTime.Now,
BranchLate = newBranch.address.BranchLate,
BranchLng = newBranch.address.BranchLng,
BranchId = branch.BranchId
};
CreateBranchAddress(branchAddress);
return Ok();
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
public ActionResult CreateBranchAddress(BranchAddress newBranchAddress)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
db.BranchAddress.Add(newBranchAddress);
db.SaveChanges();
return Ok();
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
Both db.SaveChanges() are hit when debugging, so I do not understand why nothing is being added to the database for the BranchAddress entity.
The data coming through newBranch of type BranchesVM is also correct (I get no POST 400 errors).
Are there any other possible reasons as to why it may not be adding?
Does it need to have 2 ActionResult? If it's possible to have just one, you could try this:
// Create New Branch
[HttpPost]
[Route("CreateBranch")]
public ActionResult CreateBranch([FromBody] BranchesVM newBranch)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
var branch = new Branch
{
BranchName = newBranch.branch.BranchName,
BranchContactNumber = newBranch.branch.BranchContactNumber,
BranchEmailAddress = newBranch.branch.BranchEmailAddress,
BranchImage = newBranch.branch.BranchImage,
BranchStatus = true
};
db.Add(branch);
db.SaveChanges();
var branchAddress = new BranchAddress
{
BranchStreetName = newBranch.address.BranchStreetName,
BranchSuburb = newBranch.address.BranchSuburb,
BranchCity = newBranch.address.BranchCity,
BranchProvince = newBranch.address.BranchProvince,
BranchCountry = newBranch.address.BranchCountry,
BranchZip = newBranch.address.BranchZip,
BranchDate = DateTime.Now,
BranchLate = newBranch.address.BranchLate,
BranchLng = newBranch.address.BranchLng,
BranchId = branch.BranchId
};
db.Add(branchAddress);
db.SaveChanges();
return Ok();
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
You might also want to wrap it using transaction to make sure both query executed successfully, otherwise it will rollback.
// Create New Branch
[HttpPost]
[Route("CreateBranch")]
public ActionResult CreateBranch([FromBody] BranchesVM newBranch)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
//START TRANSACTION
using (var transSQL = db.Database.BeginTransaction())
{
var branch = new Branch
{
BranchName = newBranch.branch.BranchName,
BranchContactNumber = newBranch.branch.BranchContactNumber,
BranchEmailAddress = newBranch.branch.BranchEmailAddress,
BranchImage = newBranch.branch.BranchImage,
BranchStatus = true
};
db.Add(branch);
db.SaveChanges();
var branchAddress = new BranchAddress
{
BranchStreetName = newBranch.address.BranchStreetName,
BranchSuburb = newBranch.address.BranchSuburb,
BranchCity = newBranch.address.BranchCity,
BranchProvince = newBranch.address.BranchProvince,
BranchCountry = newBranch.address.BranchCountry,
BranchZip = newBranch.address.BranchZip,
BranchDate = DateTime.Now,
BranchLate = newBranch.address.BranchLate,
BranchLng = newBranch.address.BranchLng,
BranchId = branch.BranchId
};
db.Add(branchAddress);
db.SaveChanges();
//SQL Commit
transSQL.commit();
return Ok();
}
}
catch (Exception e)
{
return BadRequest(e.Message);
}
}
I realized that I did not post enough information in my question to get an appropriate answer. However, I changed my catch to output an inner exception:
catch (Exception e)
{
return BadRequest(e.InnerException);
}
This allowed me to see what exactly was causing the error, that being one of the records exceeding its maximum size as set out in the table.

C# ASP.NET MVC read-only for data field not working

I am having some issues with an edit form that does not seem to work no matter what I use.
I have tried
[ReadOnly(true)] and [Editable(false)]
in the Model
#Html.EditorFor(model => model.SellingPrice, new { htmlAttributes = new { #class = "form-control", #readonly = true } })
in the View..
I have changed the controller with nothing about the field.
I have also tried changing the model to
{ get; private set; }
According to a other post on another site using the [ReadOnly(true)] in the model and nothing in the controller pointing to it, it should come up 'null' and it does not. It seems to give a value of 0 or what the field has in it. Depending on if I use it in the edit View or not.
This field in the table is a calculated field on the SQL side. Used only for viewing.
Below is the controller POST - 2 Versions that I have tried:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(ProductMaster productMaster)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
try
{
if (ModelState.IsValid)
{
ProductMaster prodFromDB = db.ProductMaster.Single(x => x.ProductMasterId == productMaster.ProductMasterId);
prodFromDB.VendorId = productMaster.VendorId;
prodFromDB.Material = productMaster.Material;
prodFromDB.VendorRef = productMaster.VendorRef;
prodFromDB.CreatedDate = productMaster.CreatedDate;
prodFromDB.CreatedBy = productMaster.CreatedBy;
prodFromDB.LastUpdated = productMaster.LastUpdated;
prodFromDB.UpdatedBy = productMaster.UpdatedBy;
prodFromDB.UnitOfMeasure = productMaster.UnitOfMeasure;
prodFromDB.StandardCost = productMaster.StandardCost;
prodFromDB.AverageCost = productMaster.AverageCost;
prodFromDB.LastCost = productMaster.LastCost;
prodFromDB.IsPrimary = productMaster.IsPrimary;
var Manager = ((IObjectContextAdapter)db).ObjectContext.ObjectStateManager;
Manager.ChangeObjectState(prodFromDB, System.Data.Entity.EntityState.Modified);
db.SaveChanges();
sb.Append("Submitted");
return Content(sb.ToString());
}
else
{
foreach (var key in this.ViewData.ModelState.Keys)
{
foreach (var err in this.ViewData.ModelState[key].Errors)
{
sb.Append(err.ErrorMessage + "<br/>");
}
}
}
}
catch (Exception ex)
{
sb.Append("Error :" + ex.Message);
}
return Content(sb.ToString());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "ProductMasterId,VendorId,Material,VendorRef,CreatedDate,CreatedBy,LastUpdated,UpdatedBy,UnitOfMeasure,StandardCost,AverageCost,LastCost,IsPrimary")] ProductMaster productMaster)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
try
{
if (ModelState.IsValid)
{
db.Entry(productMaster).State = EntityState.Modified;
db.SaveChanges();
sb.Append("Submitted");
return Content(sb.ToString());
}
else
{
foreach (var key in this.ViewData.ModelState.Keys)
{
foreach (var err in this.ViewData.ModelState[key].Errors)
{
sb.Append(err.ErrorMessage + "<br/>");
}
}
}
}
catch (Exception ex)
{
sb.Append("Error :" + ex.Message);
}
return Content(sb.ToString());
}
The error that I get is:
The column "SellingPrice" cannot be modified because it is either a computed column or is the result of a UNION operator
First thing would be to mark the Entity property with the DatabaseGeneratedOption of Computed:
public class Product
{
// ...
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public decimal SellingPrice { get; internal set; }
// ...
}
The next thing would be to avoid code that sets an entity's EntityState to modified explicitly. So long as the DbContext is tracking the entity, this should be enough:
ProductMaster prodFromDB = db.ProductMaster.Single(x => x.ProductMasterId == productMaster.ProductMasterId);
prodFromDB.VendorId = productMaster.VendorId;
prodFromDB.Material = productMaster.Material;
prodFromDB.VendorRef = productMaster.VendorRef;
prodFromDB.CreatedDate = productMaster.CreatedDate;
prodFromDB.CreatedBy = productMaster.CreatedBy;
prodFromDB.LastUpdated = productMaster.LastUpdated;
prodFromDB.UpdatedBy = productMaster.UpdatedBy;
podFromDB.UnitOfMeasure = productMaster.UnitOfMeasure;
prodFromDB.StandardCost = productMaster.StandardCost;
prodFromDB.AverageCost = productMaster.AverageCost;
prodFromDB.LastCost = productMaster.LastCost;
prodFromDB.IsPrimary = productMaster.IsPrimary;
db.SaveChanges();
The reason is that lets say I have an entity with 20 columns and I update 5 of them from the view model, and only 2 of those values actually differ from the entity's current state. If I let the DbContext track the changes and just call SaveChanges then the resulting UPDATE SQL statement will be an update specifying just the columns which values actually changed. If instead I set the EntityState to Modified, the UPDATE SQL statement will include all columns, whether I set them or not, and whether they actually changed or not. Computed columns should be ignored, but why incur the cost/risk of updating/overwriting values you don't intend to have been changed?

Throw message after successfully insert into database or error message if not successful

I am inserting into my database. My code is working fine, but am stuck trying to throw message if the insertion is successful or not. Please check my code below. For example, if the insertion is successful, a message like "Insertion successful" should be shown.
public class HomeController : Controller
{
public ActionResult Index()
{
SalesLayanEntities3 db = new SalesLayanEntities3();
List<Product_Category> list = db.Product_Category.ToList();
ViewBag.ProductName = new SelectList(list,"cat_id","cat_name");
return View();
}
public ActionResult SaveRecord(ProductForm model)
{
try
{
SalesLayanEntities3 db = new SalesLayanEntities3();
Product prod = new Product();
prod.prod_name = model.Prod_name;
prod.prod_model = model.Prod_model;
prod.prod_quantity = model.Prod_quantity;
prod.prod_description = model.Prod_description;
prod.prod_unit_cost_price = model.Prod_unit_cost_price;
prod.cat_id = model.Cat_id;
db.Products.Add(prod);
db.SaveChanges();
int latestProdId = prod.prod_id;
}
catch (Exception ex)
{
throw ex;
}
return RedirectToAction("Index");
}
}
You are not getting any message because you are redirecting to Index page.
you may want to redirect to success page RedirectToAction("Success", "Shared").
you can use Error page for thrown exceptions which I believe you are already doing that by default.
You might want to use TempData. It can be accessed in your view.
Controller:
TempData["status"] = "Success";
View:
{
#TempData["status"];
}
You could do it like this:
public ActionResult SaveRecord(ProductForm model)
{
try
{
SalesLayanEntities3 db = new SalesLayanEntities3();
Product prod = new Product();
prod.prod_name = model.Prod_name;
prod.prod_model = model.Prod_model;
prod.prod_quantity = model.Prod_quantity;
prod.prod_description = model.Prod_description;
prod.prod_unit_cost_price = model.Prod_unit_cost_price;
prod.cat_id = model.Cat_id;
db.Products.Add(prod);
db.SaveChanges();
int latestProdId = prod.prod_id;
TempData["status"] = "Success";
}
catch (Exception ex)
{
TempData["status"] = "Error";
throw ex;
}
return RedirectToAction("Index");
}
Try adding a parameter to the redirect (you will need to change the scope of latestProdId):
return RedirectToAction("Index", new {addedID = latestProdId );
and then add an optional parameter to Index
public ActionResult Index(int latestProdId = 0)
Test latestProdId for a non-zero value and then pass that back to the view to display.

How to do insert action on many-to-many relationship

I need your help please,
I do not know where to start, but I'm doing a recruitment management application with the asp.net mvc and wcf technologies, I have a management part of the offers, to start I have a many-to-many relationship between Offers and Candidates table.
Now I want that when the candidate chooses the offer that suits him, when he clicks on the apply button, the offer's id and the id of the candidate is registered in the CandidateOffre association table.
That's what I did, thank you for telling me what I miss so that the insert is done correctly,
Server side (wcf)
public CandidatDTO CreateCandidat(CandidatDTO c)
{
CandidatEmploi candidatEmp = new CandidatEmploi();
CSRMappers.MapCandidatDTOToEntity(c, candidatEmp);
if (c.OffreEmploi != null && c.OffreEmploi.Any())
{
foreach (var f in c.OffreEmploi)
{
if (f.IDOffre == 0)
{
OffreEmploi of = new OffreEmploi();
CSRMappers.MapOffreDTOToEntity(f, of);
candidatEmp.OffreEmploi.Add(of);
}
else
{
//look for offer
var of = CRE.OffreEmploi.Find(f.IDOffre);
if (of != null)
candidatEmp.OffreEmploi.Add(of);
}
}
}
CRE.CandidatEmploi.Add(candidatEmp);
CRE.SaveChanges();
c.IDCandidat = candidatEmp.IDCandidat;
return c;
}
Client side (MVC)
[HttpGet]
public ActionResult PostE(int id)
{
var candidat = new CSRServiceReference.CandidatDTO();
candidat.OffreEmploi = new List<OffreDTO>();
if (id > 0)
{
var offre = CREClient.GetOffre(id);
if(offre != null)
{
candidat.OffreEmploi.Add(offre);
}
}
candidat.Experiences = new List<CSRServiceReference.ExpProDTO>();
candidat.Formations = new List<CSRServiceReference.FormationDTO>();
candidat.ConnInfo = new List<CSRServiceReference.ConnaissInfoDTO>();
candidat.ConnLing = new List<CSRServiceReference.connaissLingDTO>();
return View(candidat);
}
[HttpPost, ActionName("PostE")]
public ActionResult PostEmp(CSRServiceReference.CandidatDTO candidat)
{
if (ModelState.IsValid)
{
candidat.StatutCandidat = "En attente";
var newC = CREClient.CreateCandidat(candidat);
return RedirectToAction("EditCandidat", "Home", new { id = newC.IDCandidat });
}
return View(candidat);
}
But still the association table is empty unfortunately,
I don't know where is the problem exactly: /
Thank you for answering me.

Entity Framework won't save changes to model

I'm using Database First in my website and i'm trying to update the data of one entry of the database but the changes are not being saved in one part but are working in another one.
This is where i populate the form with the model data or create a new one to reserve the ID:
public ActionResult Editar(int ID = 0)
{
var banner = new Banner();
var siteVM = new SiteViewModel();
if (ID == 0)
{
db.Banner.Add(banner);
db.SaveChanges();
}
else
{
banner = db.Banner.Find(ID);
siteVM.iePaginas = db.Pagina.ToList().Select(x => new SelectListItem
{
Value = x.ID.ToString(CultureInfo.InvariantCulture),
Text = x.nome.ToUpper()
}).ToList();
foreach (var pagina in siteVM.iePaginas)
{
foreach (var bannerPagina in banner.BannerPagina)
{
if (int.Parse(pagina.Value) == bannerPagina.paginaID)
{
pagina.Selected = true;
}
}
}
}
siteVM.Banner = banner;
return View(siteVM);
}
And this is where i save it:
[HttpPost]
public ViewResult Editar(SiteViewModel model)
{
try
{
var banner = model.Banner;
banner.rascunho = !banner.ativo;
if (banner.ID == 0)
{
db.Banner.Add(banner);
}
else
{
db.Entry(banner).State = EntityState.Modified;
}
//Remove the related pages
foreach (var source in db.BannerPagina.Where(x => x.bannerID == banner.ID))
{
db.BannerPagina.Remove(source);
}
//Record related pages
foreach (var pag in model.PaginasSelecionadas)
{
db.BannerPagina.Add(new BannerPagina { paginaID = int.Parse(pag.Value), bannerID = banner.ID });
}
db.SaveChanges();
ViewBag.Salvo = 1;
return View(model);
}
catch
{
ViewBag.Error = 1;
return View(model);
}
}
The db.Entry(banner).State = EntityState.Modified; doesn't work, i get the data in the model and my ModeState is valid but the data isn't changed on the database. Just below that i have:
db.BannerPagina.Remove(source); //BannerPagina contains a set of pages that are related to my banner so i can choose the pages i want this banner displayed
db.BannerPagina.Add(new BannerPagina { paginaID = int.Parse(pag.Value), bannerID = banner.ID });
And they work fine. I also have tried to use the attach method but it also doesn't work.
EDIT
Kind of found my problem, earlier i had changed the StoreGeneratedPattern option on my table properties to Computed instead of None (I did that so i could create an empty object in the database using it's default values), once i changed it back my changes started to be saved in the DB.
Is there a way to make the Computed configuration work ?

Categories

Resources