My models has some fields that are not to be presented in views (like Id field).
So, when I post the form, these fields return with "null" value, unless I insert then as hidden fields in form.
There are another away to update a model, using only the fields in form ?
My actual code:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(Profissao model)
{
if (ModelState.IsValid)
{
using (var escopo = Db.Database.BeginTransaction())
{
try
{
if (model.Id == 0)
Db.Profissoes.Add(model);
else
Db.Profissoes.Update(model);
Db.SaveChanges();
escopo.Commit();
return RedirectToAction("Index");
}
catch (Exception)
{
escopo.Rollback();
}
}
}
return View(model);
}
You should use Dto's (Data transfer objects) to handle this.
public class User
{
public string Name { get; set; }
public string Passord { get; set; }
public string Email { get; set; }
}
public class UserDto
{
public string Name { get; set; }
public string Passord { get; set; }
public string Email { get; set; }
public UserDto FromModel(User user)
{
Name = user.Name;
Passord = user.Passord;
Email = user.Email;
return this;
}
public User UpdataModel(User user)
{
user.Name = Name;
user.Email = Email;
return user;
}
}
then you can pass around the Dto object to your view and in your post.
your post controller should look somthing like
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(ProfissaoDto model)
{
if (ModelState.IsValid)
{
using (var escopo = Db.Database.BeginTransaction())
{
try
{
if (model.Id == 0)
Db.Profissoes.Add(ProfissaoDto.UpdateModel(new Profissao()));
else
var model = Db.Profissao.find(Model.id);
Db.Profissoes.Update(ProfissaoDto.UpdateModel(model));
escopo.Commit();
return RedirectToAction("Index");
}
catch (Exception)
{
escopo.Rollback();
}
}
}
return View(model);
}
Related
Can you help me. How can i create column with information about who Created an element(column CreatedBy) I created asp net core 2.0 MVC Web-Application with Windows authentication. I implemented information about who was modifying last, sucessfuly, but i dont get it with CreatedBy.
My model is
public class TestModel
{
public int Id { get; set; }
public string Description { get; set; }
public string CreatedBy { get; set; }
[DataType(DataType.Date)]
public DateTime Created { get;}
public TestModel()
{
Created = DateTime.Now;
}
public string ModifiedBy { get; set; }
public DateTime Modified { get; set; }
}
My controller of Create
public async Task<IActionResult> Create([Bind("Id,Description")] TestModel testModel)
{
if (ModelState.IsValid)
{
_context.Add(testModel);
testModel.CreatedBy = this.User.Identity.Name;
// testModel.ModifiedBy = this.User.Identity.Name;
testModel.Modified = DateTime.Now;
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(testModel);
}
Edit controller
public async Task<IActionResult> Edit(int id, [Bind("Id,Description")] TestModel KestModel)
{
if (id != KestModel.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TestModelExists(KestModel.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View();
}
As per your comment I understood that you want to update modifyby on update request and assign createdby at the create request,
For this you should check the Id which is already assign or not, If id is already assign than it is update request else it is create request
Try below code changes
public async Task<IActionResult> Create([Bind("Id,Description")] TestModel testModel)
{
if (ModelState.IsValid)
{
if(testModel.Id > 0){
// the entity is already created and it is modify request
_context.Entry(testModel).State = EntityState.Modified;
testModel.ModifiedBy = this.User.Identity.Name;
testModel.Modified = DateTime.Now;
}
else{
// it is create request
_context.Entry(testModel).State = EntityState.Added;
testModel.CreatedBy = this.User.Identity.Name;
testModel.Created = DateTime.Now;
}
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(testModel);
}
I'm having a trouble with my project (ASP.NET MVC 5/AJAX/BOOTSTRAP).
When click on Save button on Page, .Net calls in POST the proper action, but the Hidden Fields for PSATOKEN does not contain value (see #Html.HiddenFor(m => m.PSAToken) in the View), despite PSAToken contains a GUID value (saw in Debug Mode) in the Controller method.
Let's see some code below.
Many thanks to answerers!
Model
public interface IPSAPageViewModel
{
String PSAToken { get; set; }
int IdPSAAzienda { get; set; }
}
public abstract class BasePSAPageViewModel : IPSAPageViewModel
{
public String PSAToken { get; set; }
public int IdPSAAzienda { get; set; }
}
public class DatiGeneraliViewModel : BasePSAPageViewModel
{
public DatiGeneraliViewModel()
{
this.Item = new InformazioniGenerali();
}
public Crea.PSA.ServiceLayer.BO.InformazioniGenerali Item { get; set; }
public List<SelectListItem> FormeGiuridicheList { set; get; }
public List<SelectListItem> FormeConduzioneList { set; get; }
}
Controller
private ViewResult ViewPSAPage(IPSAPageViewModel vm)
{
base.createViewBagPaginePrecSucc();
return View(vm);
}
[HttpPost]
[ValidateAntiForgeryToken]
[HttpParamAction]
public ActionResult SalvaDatiGeneraliProsegui(DatiGeneraliViewModel vm)
{
return salvataggioDatiGenerali(vm, true);
}
[HttpPost]
[ValidateAntiForgeryToken]
[HttpParamAction]
public ActionResult SalvaDatiGenerali(DatiGeneraliViewModel vm)
{
//Here vm.PSAToken doesn't contain the value setted
return salvataggioDatiGenerali(vm);
}
private ActionResult salvataggioDatiGenerali(DatiGeneraliViewModel vm, bool proseguiCompilazione = false)
{
if (ModelState.IsValid)
{
var resp = aziendeManager.Save(vm.PSAToken, vm.Item, SessionManager.UserIdConnected, CONTROLLERNAME);
if (resp.Success)
{
var psaAzienda = resp.DataObject;
setVarsInSession(psaAzienda.idToken.ToString(), psaAzienda.idPsaAzienda.ToString(), psaAzienda.Aziende.ragioneSociale);
//Here there is some Value (POST)
vm.PSAToken = psaAzienda.idToken.ToString();
//vm.IdPSAAzienda = psaAzienda.idPsaAzienda.ToString();
if (proseguiCompilazione)
return RedirectToAction("DatiAziendaliRiepilogativi", new { id = psaAzienda.idToken });
}
else
ModelState.AddModelError("", resp.Message);
}
setSuccessMessage();
vm.FormeGiuridicheList = aziendeManager.GetAllFormeGiuridiche().ToSelectItems();
vm.FormeConduzioneList = aziendeManager.GetAllFormeConduzione().ToSelectItems();
return ViewPSAPage(vm);
}
View
to see the view click here
Here you can see the value at debug in VS
But in the generated HTML the Hidden Field of PSATOKEN is empty
I found the solution here:
patrickdesjardins.com/blog/… .
I've added a few specific Properties to the ApplicationUser of the standart project.
some of the added Properties are of custom classes.
Since i use EntityFramework, it creates a dbtable for users and one for each custom class.
i added the Properties to my ManageController and Views and adding these Properties to the specific dbtable works, but i cant access them. in the dbo.AspNetUsers there is a column added, that is called after the attribute + ID (In my example "NameID").
Now if i am loading the user in my ManageController, every normal Attribute is loaded, but the custom ones are null.
My Question is, how can i load the custom objects (that are really stored in the other table).
ApplicationUser.cs:
namespace refProject.Models
{
public class ApplicationUser : IdentityUser
{
public Name Name { get; set; }
}
}
ManageController.cs
//other usings
using refProject.Models;
using refProject.Models.ManageViewModels;
namespace refProject.Controllers
{
[Authorize]
public class ManageController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
//other managers
public ManageController(
UserManager<ApplicationUser> userManager,
//other managers
)
{
_userManager = userManager;
//other managers
}
//
// GET: /Manage/Index
[HttpGet]
public async Task<IActionResult> Index(ManageMessageId? message = null)
{
ViewData["StatusMessage"] =
message == ManageMessageId.ChangeNameSuccess ? "Your name has been changed."
: message == ManageMessageId.SetNameSuccess ? "Your name has been set."
: "";
var user = await GetCurrentUserAsync();
if (user == null)
{
return View("Error");
}
var model = new IndexViewModel
{
//other Properties
//
//
// THIS ONE IS NULL
//
//
Name = user.Name
//other Properties
};
return View(model);
}
// GET: /Manage/ChangeName
[HttpGet]
public IActionResult ChangeName()
{
return View();
}
//
// POST: /Manage/ChangeName
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ChangeName(ChangeNameViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
var user = await GetCurrentUserAsync();
if(user != null)
{
Name NewName = new Name();
NewName.FirstName = model.NewFirstName;
NewName.LastName = model.NewLastName;
user.Name = NewName;
IdentityResult result = await _userManager.UpdateAsync(user);
if (result.Succeeded)
{
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.ChangeNameSuccess });
}
AddErrors(result);
return View(model);
}
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
}
//
// GET: /Manage/SetName
[HttpGet]
public IActionResult SetName()
{
return View();
}
//
// POST: /Manage/SetName
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SetName(SetNameViewModel model)
{
if(!ModelState.IsValid)
{
return View(model);
}
var user = await GetCurrentUserAsync();
if(user != null)
{
Name NewName = new Name();
NewName.FirstName = model.NewFirstName;
NewName.LastName = model.NewLastName;
user.Name = NewName;
IdentityResult result = await _userManager.UpdateAsync(user);
if(result.Succeeded)
{
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.SetNameSuccess });
}
AddErrors(result);
return View(model);
}
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
}
#region Helpers
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
public enum ManageMessageId
{
Error,
ChangeNameSuccess,
SetNameSuccess,
}
private Task<ApplicationUser> GetCurrentUserAsync()
{
return _userManager.GetUserAsync(HttpContext.User);
}
#endregion
}
}
Name.cs
namespace refProject.Models
{
public class Name
{
public int ID { get; set; }
public string fTitle { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string lTitle { get; set; }
public override string ToString()
{
return fTitle + " " + FirstName + " " + LastName + " " + lTitle;
}
}
}
That is a known issue. It is not considered a bug, but rather a design decision.
The recommended way is to access the user through DbContext rather than from the UserManager implementation.
"Just to add a bit more detail: as a performance optimization ASP.NET Core Identity currently only loads the entities related to a user or a role as needed to satisfy API calls. I.e. it won't load related entities (not even the built-in ones) eagerly on a method call like such as FindByName() because the find methods are only required to return the root.
At this point issuing queries against the DbContext is the recommended
way to load related data. If you want to abstract this from the
application code you can extend both the Identity store and manager
classes to add methods to retrieve and return your custom related
data."
Comment link
You could change your GetCurrentUserAsync method as follows:
private ApplicationUser GetCurrentUserAsync()
{
return _userManager.Users.Include(x => x.Name).FirstOrDefault(x => x.Id == _userManager.GetUserId(User));
}
I created a small website using ASP.NET MVC but it can not create new, edit or delete data from the database. The data only shows on the webpage but when i use SELECT * command in SQL the data is not shown.
My connection string in webconfig:
<add name="CodeFileDBContext"
providerName="System.Data.SqlClient"
connectionString="Data Source=HOANG-PC\SQLSERVER01;Initial Catalog=Ciaos;User Id=sa;Password=**********;MultipleActiveResultSets=True" />
Model:
namespace Ciao.Models
{
public class CodeFile
{
[Key]
public int ColdeFile_ID { get; set;}
public string Website_Name { get; set;}
public string Service_Name { get; set;}
public DateTime Date_In { get; set;}
public DateTime Date_Out { get; set;}
public int Service_Status { get; set;}
}
public class CodeFileDBContext : DbContext
{
public DbSet<CodeFile> tbl_CodeFile { get; set; }
}
}
Controller:
namespace Ciao.Controllers
{
public class CodeFileController : Controller
{
private CodeFileDBContext db = new CodeFileDBContext();
//
// GET: /CodeFile/
public ActionResult Index()
{
return View(db.tbl_CodeFile.ToList());
}
//
// GET: /CodeFile/Details/5
public ActionResult Details(int id = 0)
{
CodeFile codefile = db.tbl_CodeFile.Find(id);
if (codefile == null)
{
return HttpNotFound();
}
return View(codefile);
}
//
// GET: /CodeFile/Create
public ActionResult Create()
{
return View();
}
//
// POST: /CodeFile/Create
[HttpPost]
public ActionResult Create(CodeFile codefile)
{
if (ModelState.IsValid)
{
db.tbl_CodeFile.Add(codefile);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(codefile);
}
//
// GET: /CodeFile/Edit/5
public ActionResult Edit(int id = 0)
{
CodeFile codefile = db.tbl_CodeFile.Find(id);
if (codefile == null)
{
return HttpNotFound();
}
return View(codefile);
}
//
// POST: /CodeFile/Edit/5
[HttpPost]
public ActionResult Edit(CodeFile codefile)
{
if (ModelState.IsValid)
{
db.Entry(codefile).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(codefile);
}
//
// GET: /CodeFile/Delete/5
public ActionResult Delete(int id = 0)
{
CodeFile codefile = db.tbl_CodeFile.Find(id);
if (codefile == null)
{
return HttpNotFound();
}
return View(codefile);
}
//
// POST: /CodeFile/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
CodeFile codefile = db.tbl_CodeFile.Find(id);
db.tbl_CodeFile.Remove(codefile);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
}
Add constructor to your CodeFileDBContext class:
For example like this:
public CodeFileDBContext() : base("Name=CodeFileDBContext")
{
var adapter = (IObjectContextAdapter)this;
var objectContext = adapter.ObjectContext;
objectContext.CommandTimeout = 30; // value in seconds
}
DbContext base class accepts connectionstring name as parameter. Try passing the connection string name through CodeFileDBContext
Could you give us more details, what is the exact error you get?
Another thing you should check is that each data member in the model has its column in that specific table. If their names do not exactly match use the 'Column' attribute.
For instance, if they do not match try:
[Column("Table_Id")]
public int ID { get; set; }
If they all match, add the 'Table' attribute, it helped me many times before.
[Table("YourTableName")]
{
public class CodeFile....
}
I would like to get into the habit of using ViewModels.
In the past I have only used them in my Create Actions and I never figured how to use them in Edit Actions. I used Domain Entities instead.
Let's say I have the following:
Using Entity Framework Code First
POCO class in Domain project
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PersonId { get; set; }
public string Name { get; set; }
public string Website { get; set; }
public DateTime? Created { get; set; }
public DateTime? Updated { get; set; }
}
In my Data Project
Abstract Folder:
public interface IPersonRepository
{
IQueryable<Person> People{ get; }
void SavePerson(Person person);
}
Concrete Folder:
EfDb class
public class EfDb : DbContext
{
public EfDb() : base("DefaultConnection") {}
public DbSet<Person> People{ get; set; }
}
EfPersonRepository class
#region Implementation of Person in IPersonRepository
public IQueryable<Person> People
{
get { return _context.People; }
}
public void SavePerson(Persona person)
{
if (person.PersonId == 0)
{
_context.People.Add(person);
}
else if (person.PersonId> 0)
{
var currentPerson = _context.People
.Single(a => a.PersonId== person.PersonId);
_context.Entry(currentPerson).CurrentValues.SetValues(person);
}
_context.SaveChanges();
}
#endregion
PersonCreateViewModel in WebUI Porject ViewModels folder
public class PersonCreateViewModel
{
[Required]
[Display(Name = "Name:")]
public string Name { get; set; }
[Display(Name = "Website:")]
public string Website { get; set; }
}
Person Controller and Create Action:
public class PersonController : Controller
{
private readonly IPersonRepository _dataSource;
public PersonController(IPersonRepository dataSource)
{
_dataSource = dataSource;
}
// GET: /Association/
public ActionResult Index()
{
return View(_dataSource.Associations);
}
// GET: /Person/Details/5
public ActionResult Details(int id)
{
return View();
}
// GET: /Person/Create
[HttpGet]
public ActionResult Create()
{
return View();
}
// POST: /Person/Create
[HttpPost]
public ActionResult Create(PersonCreateViewModel model)
{
if (ModelState.IsValid)
{
try
{
var Person = new Person
{
Name = Model.Name,
Website = model.Website,
Created = DateTime.UtcNow,
Updated = DateTime.UtcNow
};
_dataSource.SavePerson(person);
return RedirectToAction("Index", "Home");
}
catch
{
ModelState.AddModelError("", "Unable to save changes. ");
}
}
return View(model);
}
}
Now unless I am mistaken, I expect my PersonEditViewlModel to look exactly like my PersonCreateViewlModel. But I can't figure out how to use that in my Edit action, provided I also have to call SavePerson(Person person) like I did in my Create action.
Note: Please no suggestions of AutoMapper or ValueInjecter.
How is this done?
It'll be just like create except you need the record Id.
[HttpGet]
public ActionResult Edit(int id)
{
var personVm = _dataSource.People.Single(p => p.PersonId == id)
.Select(e => new PersonEditViewModel {
e.PersonId = p.PersonId,
e.Name = p.Name,
e.Website = p.Website
...
});
return View(personVm);
}
[HttpPost]
public ActionResult Edit(PersonEditViewModel model)
{
if (ModelState.IsValid)
{
var person = _dataSource.People.Single(p => p.PersonId == model.PersonId);
person.Name = model.Name;
person.Website = model.Website;
...
_dataSource.EditPerson(person);
return RedirectToAction("Index", "Home");
}
return View(model);
}
Edit:
So you don't do another query on edits
public void EditPerson(Person person)
{
_context.Entry(person).State = EntityState.Modified;
_context.SaveChanges();
}