I have a model with a property called "datetime_inclusion" and I need to set a value for this ONLY when I save the first time, there is a way to do this treatment in the model?
I use C# MVC5 and entity framework 5
When you save the first time your object won't have an ID until it gets put into the DB. You can check against this to set your value.
if(myEntity.ObjectID <= 0)
{
myEntity.DateAdded = DateTime.Now;
}
Shoe's answer is perfect. But if you are worry that property can be change in edit time, control that. In create action use Shoe's code and in Edit action use:
[HttpPost]
public ActionResult Edit(int id, Model model)
{
using (var db = new YourEntities())
{
//control that, it does not change
model.datetime_inclusion = db.YourTable.Find(id).datetime_inclusion;
db.Entry(model).State = System.Data.EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
Related
Entity Framework, Code First
I have a model with a lot of fields. One field ("Name") I do not want to be editable but read-only after insert. But I still want to show this field as part of my "Edit" form (and viewModel for edit). I'd like to show it as readonly textbox (like #Html.TextBoxFor(c => c.Name, new {#readonly = "readonly"})) but if someone change it's value with browser dev.tools it should not be saved to DB.
Is there some beautiful way to do it?
Now I'm getting an instance from DB on and set "Name" field of form data back to value from DB.
When processing the ViewModel which is sent from the frontend, ignore the "read-only" fields before saving them in the database. AFAIK EF does not support ignoring of properties.
One way to approach this using Entity Framework is to create a database model and a view model. Make sure you have the CRUD translation in the back end when saving.
public async Task<YourModelContext> SaveModel(YourModelContext context)
{
var existing = await YourTable.FirstOrDefaultAsync(f => f.Id == context.Id);
if (existing == null)
{
existing = new YourDatabaseModel
{
Created = DateTime.UtcNow
}
YourTable.Add(existing);
}
// Name will be saved and changed by the user
// existing.Name = context.Name;
// existing.Description = context.Description;
// existing.SomeOtherField = context.SomeOtherField;
// Not specifying name will not update the name.
// You'll have to set the readonly on textbox in the web page
existing.Description = context.Description;
existing.SomeOtherField = context.SomeOtherField;
// Save the changes to the underlying database
await SaveChangesAsync();
// Assign the inserted database id to the view model id
context.Id = existing.Id;
// Return the view model context to the html page
return context;
}
Here is the problem. User can come to set and enter in text in an upper and lower text box. After they have done so that text will then be stored in a datatable which will hold a Guid Id, a Title string, and a Body string.
How do I arrange it so that a if the page reloads (meaning the data gets sent to the data base) that if the user changes come text and hits submit again that the table will change accordingly and not make a new row.
I am thinking I will need an if statement to pull this off but I am not sure what to put within the statement. There is the code I have thus far in a .net mvc controller
[HttpPost]
public ActionResult ActName(ModelNameModel item)
{
if( ModelState.IsValid)
{
using (DB.DatabaseName db = new DB.DatabaseName())
{
DB.Model newRecord = new DB.Model();
newRecord.Title = item.Title;
newRecord.Body = item.Body;
newRecord.Id = Guid.NewGuid();
db.Models.Add(newRecord);
db.SaveChanges();
}
}
return View(item);
}
What other if statement can I use to verify the Guid Matches? Thank you!
I am writing a asp.net mvc4 app and I am using entity framework 5. Each of my entities have fields like EnteredBy, EnteredOn, LastModifiedBy and LastModifiedOn.
I am trying to auto-save them by using the SavingChanges event. The code below has been put together from numerous blogs, SO answeres etc.
public partial class myEntities : DbContext
{
public myEntities()
{
var ctx = ((IObjectContextAdapter)this).ObjectContext;
ctx.SavingChanges += new EventHandler(context_SavingChanges);
}
private void context_SavingChanges(object sender, EventArgs e)
{
ChangeTracker.DetectChanges();
foreach (ObjectStateEntry entry in
((ObjectContext)sender).ObjectStateManager
.GetObjectStateEntries
(EntityState.Added | EntityState.Modified))
{
if (!entry.IsRelationship)
{
CurrentValueRecord entryValues = entry.CurrentValues;
if (entryValues.GetOrdinal("LastModifiedBy") > 0)
{
HttpContext currContext = HttpContext.Current;
string userName = "";
DateTime now = DateTime.Now;
if (currContext.User.Identity.IsAuthenticated)
{
if (currContext.Session["userId"] != null)
{
userName = (string)currContext.Session["userName"];
}
else
{
userName = currContext.User.Identity.Name;
}
}
entryValues.SetString(
entryValues.GetOrdinal("LastModifiedBy"), userName);
entryValues.SetDateTime(
entryValues.GetOrdinal("LastModifiedOn"), now);
if (entry.State == EntityState.Added)
{
entryValues.SetString(
entryValues.GetOrdinal("EnteredBy"), userName);
entryValues.SetDateTime(
entryValues.GetOrdinal("EnteredOn"), now);
}
else
{
string enteredBy =
entry.OriginalValues.GetString(entryValues.GetOrdinal("EnteredBy"));
DateTime enteredOn =
entry.OriginalValues.GetDateTime(entryValues.GetOrdinal("EnteredOn"));
entryValues.SetString(
entryValues.GetOrdinal("EnteredBy"),enteredBy);
entryValues.SetDateTime(
entryValues.GetOrdinal("EnteredOn"), enteredOn);
}
}
}
}
}
}
My problem is that entry.OriginalValues.GetString(entryValues.GetOrdinal("EnteredBy")) and entry.OriginalValues.GetDateTime(entryValues.GetOrdinal("EnteredOn")) are not returning the original values but rather the current values which is null. I tested with other fields in the entity and they are returning the current value which were entered in the html form.
How do I get the original value here?
I think the problem may be that you are using the instance provided by the model binder as the input to your controller method, so EF does not know anything about that entity and its original state. Your code may look like this:
public Review Update(Review review)
{
_db.Entry(review).State = EntityState.Modified;
_db.SaveChanges();
return review;
}
In that case, EF knows nothing about the Review instance that is being saved. It is trusting you and setting it as modified, so it will save all of its properties to the database, but it does not know the original state\values of that entity.
Check the section named Entity States and the Attach and SaveChanges Methods of this tutorial. You can also check the first part of this article, that shows how EF does not know about the original values and will update all properties in the database.
As EF will need to know about the original properties, you may first load your entity from the database and then update its properties with the values received in the controller. Something like this:
public Review Update(Review review)
{
var reviewToSave = _db.Reviews.SingleOrDefault(r => r.Id == review.Id);
//Copy properties from entity received in controller to entity retrieved from the database
reviewToSave.Property1 = review.Property1;
reviewToSave.Property2 = review.Property2;
...
_db.SaveChanges();
return review;
}
This has the advantage that only modified properties will be send and updated in the database and that your views and view models don't need to expose every field in your business objects, only those that can be updated by the users. (Opening the door for having different classes for viewModels and models\business objects). The obvious disadvantage is that you will incur an additional hit to the database.
Another option mentioned in the tutorial I referenced above is for you to save the original values somehow (hidden fields, session, etc) and on save use the original values to attach the entity to the database context as unmodified. Then update that entity with the edited fields. However I would not recommend this approach unless you really need to avoid that additional database hit.
Hope that helps!
I was running into a similar problem when trying to audit log the Modified values of an Entity.
It turns out during the post back the ModelBinder doesn't have access to the original values so the Model received is lacking the correct information. I fixed my problem by using this function which clones the current values, relods the object, and then reset the current values.
void SetCorrectOriginalValues(DbEntityEntry Modified)
{
var values = Modified.CurrentValues.Clone();
Modified.Reload();
Modified.CurrentValues.SetValues(values);
Modified.State = EntityState.Modified;
}
You can gain access to the DbEntityEntry though the change tracker, or the entry function from your context.
I'm wanting to capture the old values within a model so I can compare with the new values after submission, and create audit logs of changes a user makes.
My guess is doing it with hidden input boxes with duplicated old value properties would be one way. But wondering if there are any other good alternatives?
Thanks
In the save method, just go and get the original object from the database before saving the changes, then you have your old and new values to compare against? :)
This sounds like standard auditing. You should not worry about what has changed just capture EVERYTHING and who made the change. Unless there is some sort of real time reporting that needs to be done.
Possible auditing implementations:
CQRS, in a nutshell it tracks every change to a given object. The downside is it's an architecture that is more involved to implement.
The Rolling ledger. Each insert is a new row in the database. The most current row is used for display purposes, but with each update, a new row is inserted into the database.
Yet another approach is to save it off into an audit table.
All get the job done.
You could also store the original model in the view bag and do something like this...
// In the controller
public ActionResult DoStuff()
{
// get your model
ViewBag.OriginalModel = YourModel;
return View(YourModel);
}
// In the View
<input type="hidden" name="originalModel" value="#Html.Raw(Json.Encode(ViewBag.OriginalModel));" />
// In the controller's post...
[HttpPost]
public ActionResult DoStuff(YourModel yourModel, string originalModel)
{
// yourModel will be the posted data.
JavaScriptSerializer JSS = new JavaScriptSerializer();
YourModel origModel = JSS.Deserialize<YourModel>(originalModel);
}
I didn't get a chance to test this, just a theory :)
Exactly what mattytommo says is the preferred method all around
Instantiate new view model for creating a new entity
public ActionResult Edit(int id) {
var entity = new Entity(id); // have a constructor in your entity that will populate itself and return the instance of what is in the db
// map entity to ViewModel using whatever means you use
var model = new YourViewModel();
return View(model);
}
Post changes back
[HttpPost]
public ActionResult Edit(YourViewModel model) {
if (ModelState.IsValid) {
var entity = new YourEntity(model.ID); // re-get from db
// make your comparison here
if(model.LastUserID != entity.LastUserID // do whatever
... etc...
}
return View(model);
}
I'm struggling myself trying to get the contents of a form which is a complex model and then update the model with that complex model.
My account model has many individuals
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult OpenAnAccount(string area,[Bind(Exclude = "Id")]Account account, [Bind(Prefix="Account.Individuals")] EntitySet<Individual> individuals){
var db = new DB();
account.individuals = invdividuals;
db.Accounts.InsertOnSubmit(account);
db.SubmitChanges();
}
So it works nicely for adding new Records, but not for update them like:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult OpenAnAccount(string area,[Bind(Exclude = "Id")]Account account, [Bind(Prefix="Account.Individuals")] EntitySet<Individual> individuals){
var db = new DB();
var record = db.Accounts.Single(a => a.Reference == area);
account.individuals = invdividuals;
try{
UpdateModel(record, account); // I can't convert account ToValueProvider()
db.SubmitChanges();
}
catch{
return ... //Error Message
}
}
My problem is being how to use UpdateModel with the account model since it's not a FormCollection.
How can I convert it? How can I use ToValueProvider with a complex model?
I hope I was clear enough
Thanks a lot :)
UPDATE
That's what I was looking for:
http://goneale.com/2009/07/27/updating-multiple-child-objects-and-or-collections-in-asp-net-mvc-views/
This scenario is not supported unless you have your Account type implement IValueProvider.
That would be some pretty strange MVC though. The model binder should make sense of the HTTP request and translate that to your model not take bind your entities to your other entities.
Upon further inspection I think you're looking for:
http://msdn.microsoft.com/en-us/library/dd487246.aspx
Try this:
try{
db.Accounts.ApplyCurrentValues(record);
db.SubmitChanges();
}