I've been looking for a proper way to mark a property to NOT be changed when updating a model in MVC.
For example, let's take this small model:
class Model
{
[Key]
public Guid Id {get; set;}
public Guid Token {get; set;}
//... lots of properties here ...
}
then the edit method MVC creates looks like this:
[HttpPost]
public ActionResult Edit(Model model)
{
if (ModelState.IsValid)
{
db.Entry(model).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
now if my View does not contain the Token, it will be nullified through that edit.
I'm looking for something like this:
db.Entry(model).State = EntityState.Modified;
db.Entry(model).Property(x => x.Token).State = PropertyState.Unmodified;
db.SaveChanges();
The best way so far I found is to be inclusive and set all properties I want included by hand, but I really only want to say which ones to be excluded.
we can use like this
db.Entry(model).State = EntityState.Modified;
db.Entry(model).Property(x => x.Token).IsModified = false;
db.SaveChanges();
it will update but without Token property
Anyone looking for how to achieve this on EF Core. It's basically the same but your IsModified needs to be after you add the model to be updated.
db.Update(model);
db.Entry(model).Property(x => x.Token).IsModified = false;
db.SaveChanges();
#svendk updated:
And if you (as me) are wondering why model don't have the token either before or after db.SaveChanges(), it's because with Update, the entity is actually not retrieved - only an SQL Update clause is sent - so the context don't know of your model's preexisting data, only the information you gave it in db.Update(mode). Even if you Find(model.id) you are not getting your context updated, as there is already loaded a model in the context, it is still not retrieved from database.
If you (as me) wanted to return the finished model as it looks like in the database, you can do something like this:
db.Update(model);
db.Entry(model).Property(x => x.Token).IsModified = false;
db.SaveChanges();
// New: Reload AFTER savechanges, otherwise you'll forgot the updated values
db.Entry(model).Reload();
Now model is loaded from database with all the values, the updated and the (other) preexisting ones.
Create new model that will have limited set of properties that you want to update.
I.e. if your entity model is:
public class User
{
public int Id {get;set;}
public string Name {get;set;}
public bool Enabled {get;set;}
}
You can create custom view model that will allow user to change Name, but not Enabled flag:
public class UserProfileModel
{
public int Id {get;set;}
public string Name {get;set;}
}
When you want to do database update, you do the following:
YourUpdateMethod(UserProfileModel model)
{
using(YourContext ctx = new YourContext())
{
User user = new User { Id = model.Id } ; /// stub model, only has Id
ctx.Users.Attach(user); /// track your stub model
ctx.Entry(user).CurrentValues.SetValues(model); /// reflection
ctx.SaveChanges();
}
}
When you call this method, you will update the Name, but Enabled property will remain unchanged. I used simple models, but I think you'll get the picture how to use it.
I made an easy way to edit properties of entities I will share with you.
this code will edit Name and Family properties of entity:
public void EditProfileInfo(ProfileInfo profileInfo)
{
using (var context = new TestContext())
{
context.EditEntity(profileInfo, TypeOfEditEntityProperty.Take, nameof(profileInfo.Name), nameof(profileInfo.Family));
}
}
And this code will ignore to edit Name and Family properties of entity and it will edit another properties:
public void EditProfileInfo(ProfileInfo profileInfo)
{
using (var context = new TestContext())
{
context.EditEntity(profileInfo, TypeOfEditEntityProperty.Ignore, nameof(profileInfo.Name), nameof(profileInfo.Family));
}
}
Use this extension:
public static void EditEntity<TEntity>(this DbContext context, TEntity entity, TypeOfEditEntityProperty typeOfEditEntityProperty, params string[] properties)
where TEntity : class
{
var find = context.Set<TEntity>().Find(entity.GetType().GetProperty("Id").GetValue(entity, null));
if (find == null)
throw new Exception("id not found in database");
if (typeOfEditEntityProperty == TypeOfEditEntityProperty.Ignore)
{
foreach (var item in entity.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty))
{
if (!item.CanRead || !item.CanWrite)
continue;
if (properties.Contains(item.Name))
continue;
item.SetValue(find, item.GetValue(entity, null), null);
}
}
else if (typeOfEditEntityProperty == TypeOfEditEntityProperty.Take)
{
foreach (var item in entity.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty))
{
if (!item.CanRead || !item.CanWrite)
continue;
if (!properties.Contains(item.Name))
continue;
item.SetValue(find, item.GetValue(entity, null), null);
}
}
else
{
foreach (var item in entity.GetType().GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.GetProperty))
{
if (!item.CanRead || !item.CanWrite)
continue;
item.SetValue(find, item.GetValue(entity, null), null);
}
}
context.SaveChanges();
}
public enum TypeOfEditEntityProperty
{
Ignore,
Take
}
I guess you don't want the property to be changed just in some cases, because if you are not going to use it never in your application, just remove it from your model.
In case you want to use it just in some scenarios and avoid its "nullification" in the case above, you can try to:
Hide the parameter in the view with HiddenFor:
#Html.HiddenFor(m => m.Token)
This will make your original value to be kept unmodified and passed back to the controller.
Use TryUpdateModel: http://msdn.microsoft.com/en-us/library/dd460189(v=vs.108).aspx
Load again your object in the controller from your DBSet and run this method. You can specify both a white list and a blacklist of parameters that shall or shall not be update.
I use dapper but my solution will work for EF too. If you are potentially going to change your ORM in the future my solution might be better for you.
class Model
{
public Foo { get; set; }
public Boo { get; set; }
public Bar { get; set; }
// More properties...
public void SafeUpdate(Model updateModel, bool updateBoo = false)
{
// Notice Foo is excluded
// An optional update
if (updateBoo)
Boo = updateModel.Boo;
// A property that is always allowed to be updated
Bar = updateModel.Bar;
// More property mappings...
}
}
As you can observe I allow updates for only the properties that I wish.
A downside of my approach is that you'll need to manually update this method if you introduce new properties (that are allowed to be updated) to your model. But I believe this in not always a downside but sometimes an advantage, in the sense that you'll need to be aware of what is being updated, this might be beneficial in terms of security.
Let us see a demonstration of this approach.
// Some code, DI etc...
public IActionResult Put([FromBody] Model updateModel)
{
var safeModel = new Model();
safeModel.Update(updateModel);
// Add validation logic for safeModel here...
_modelRepository.Update(safeModel);
}
Related
I want to update an existing entry with entity framework and here is my current code:
[HttpPost]
public IActionResult Edit(Product product)
{
if (ModelState.IsValid)
{
var result = _productRepository.Query().FirstOrDefault(x => x.Id == product.Id);
if (result == null)
return RedirectToAction("Index", "Products");
_productRepository.Update(product);
//result = product;
_productRepository.Save();
return View("Edit", result);
}
What have I tried:
result = product; doesn't seem to update the row in db.
public void Update(T item)
{
_context.Entry(item).CurrentValues.SetValues(item);
}
doesn't seem to update the row in db.
result.Title = product.Title - works, but then I have to do this for each field, is there a way to update a row simply by replacing the values with another object?
Edit
Actually, I realized that the code below will not work because you are already tracking the same entity, this is caused by this line:
var result = _productRepository.Query().FirstOrDefault(x => x.Id == product.Id);
So you need to either remove that line and just use the Update method below with the product object as the parameter or use the result object and update it based on the data from your product class and then save:
result.Name = product.Name;
[...]
In this case you don't need to call _repository.update, just _repository.save
Using product to update
Assuming your Product class is an object of the same class as your Product entity class you need to make sure that it is being tracked by entity framework and to mark it as modified before it can be saved:
To do that, modify your update method as follows:
public void Update(T item)
{
if (!_context.Set<T>().Local.Any(e => e == item))
{
_context.Set<T>().Attach(item);
}
_context.Entry(item).State = EntityState.Modified
}
Then just save it and it should work:
_productRepository.Update(product);
_productRepository.Save();
A better approach?
Instead of sending entity framework entities back and forth to and from your views, you could create a model class specifically for your view and then retrieve and update your database entity models as needed:
For example if your Product database entity looks like this:
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
public int InternalId { get; set; }
}
In your view you don't need / want to use the InternalId field, so you would have a model in your Website assembly that could look like the following:
public class ProductModel
{
public int Id { get; set; }
public string ProductName { get; set; }
}
And then in your controller, this is what you will use:
[HttpPost]
public IActionResult Edit(ProductModel product)
{
if (!ModelState.IsValid)
{
return View(product);
}
var dbProduct = _productRepository.Query().FirstOrDefault(x => x.Id == product.Id);
if (dbProduct == null)
{
//Product doesn't exist, create one, show an error page etc...
//In this case we go back to index
return RedirectToAction("Index", "Products");
}
//Now update the dbProduct using the data from your model
dbProduct.ProductName = product.ProductName;
If you have a lot of fields, you don't want to do this manually, there are some libraries that will do this for you, for example, AutoMapper or my personal favorite (faster, easier to use) ValueInjecter
Using ValueInjecter you would do something like this to assign all the common properties automatically
dbProduct.InjectFrom(product);
Finally, just call save, this time you don't need to change the state because EF is already tracking your entity:
_productRepository.Save();
In my web app I have class with many properties. User is able to modify these properties with bunch of selectboxes, each responsible for one property. Everytime when given property is changed, the change event is triggered and ajax sends the new value of this property to Update method in the Controller.
So far I have one UpdateDto which consists of nullable fields. In my Update controller I check each DTO's field if it is null or not. If it's not null it means that user want to change this property and it is updated and saved in database.
Unfortunately the Update method's code looks a little bit ugly for me. It checks each property and it's quite long. Have a look:
Update Method in the controller:
public IHttpActionResult UpdateScrumTask(int id, ScrumTaskDetailsDto scrumTaskDto)
{
var scrumTaskFromDb = _context.ScrumTasks
.SingleOrDefault(s => s.Id == id);
if (scrumTaskFromDb == null)
return NotFound();
if (!ModelState.IsValid)
return BadRequest();
if (!string.IsNullOrWhiteSpace(scrumTaskDto.UserId))
{
var user = _context.Users
.SingleOrDefault(u => u.Id.Equals(scrumTaskDto.UserId));
scrumTaskFromDb.UserId = user?.Id;
}
else if (scrumTaskDto.EstimationId != null)
{
var estimation = _context.Estimations
.SingleOrDefault(u => u.Id == scrumTaskDto.EstimationId.Value);
scrumTaskFromDb.EstimationId = estimation?.Id;
}
else if (scrumTaskDto.Priority != null)
{
if (scrumTaskDto.Priority.Value == 0)
scrumTaskDto.Priority = null;
scrumTaskFromDb.Priority = scrumTaskDto.Priority;
}
else if (scrumTaskDto.TaskType != null)
{
scrumTaskFromDb.TaskType = scrumTaskDto.TaskType.Value;
}
_context.SaveChanges();
return Ok();
}
UpdateDTO:
public class ScrumTaskDetailsDto
{
public int? EstimationId { get; set; }
[Range(0, 5)]
public byte? Priority { get; set; }
[Range(0, 2)]
public TaskType? TaskType { get; set; }
public string UserId { get; set; }
}
Note that these properties are also nullable in the database. That's why if for example UserId is not found the property in database is set to null.
I wonder how it should look like. What is better solution?
Keep one Update method and use one UpdateDto with many nullable fields OR
Devide Update method into many methods, each responsible for one property and create separate DTOs. It brings that there will be a lot of DTOs connected with one model with single property (is it good?)
Another question is:
Should I use validtion (DataAnnotation) inside DTO? If not, what is alternative solution?
I'd suggest couple of improvements:
Separate your query logic from your core business logic, using repository pattern or query and command object using a library like MediatR, also controller only should call other methods to do something for it and return a response, it shouldn't have any query or any other logic
The DTO looks ok to me, as long as it's centered around one specific task
I would definitely separate the update from controller, as for it being one method or more, it's 35... lines, which is not ideal, maybe you could separate it into two methods, one responsible for validation and one responsible for the actual update, also you can use dependency injection to decouple the update and validation method from your controller
In an ASP.NET MVC application using entity framework, I have an Edit view where I am updating some properties on one of my entity models. I'd like to put an "add" button for one of the properties if the user wants to add a new property of that type (but without deleting the older entry, I understand this requires a List of those type of properties). When I click this add button, using Ajax.ActionLink will retrieve a new Html.EditorFor helper from a partial view and add it to the current form.
Currently my ActionMethod looks like it's only updating the fields that are already there, what kind of logic can I add to say "if this is a new property, then db.Add"?
heres what my method looks like now:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult TimeSeriesData(List<TimeSeriesData> List1)
{
int empid = List1[0].EmployeeID;
if (ModelState.IsValid)
{
foreach (TimeSeriesData ln in List1)
{
db.Entry(ln).State = EntityState.Modified;
}
db.SaveChanges();
return RedirectToAction("Edit", new { id = empid });
}
return new EmptyResult();
}
I'd like to add logic somewhere inside the foreach loop and say if the item in the list is a new item, then db.MyModel.List.Add(item); but I'm not sure how to check if the list item is new
would it be something like
if (newproperty.propertyid == null) {
db.MyModel.List.Add(newproperty);
}
Please let me know if you need more clarification
EDIT
Or another way to ask my question is: how can I check if the item already exists in the database,
models:
//summarized
class Employee {
int EmployeeID {get;set;}
List<TimeSeriesData> TimeSeriesData {get;set;}
}
class TimeSeriesData {
int TimeSeriesDataID {get; set;}
int EmployeeID {get;set;}
string Value {get;set;}
[ForeignKey("EmployeeID")]
public Employee Employee { get; set; }
}
There's a problem within your code structure. If you will notice,
db.Entry(ln).State = EntityState.Modified;
you are just updating/editing that specific entity and not adding a property. And also, I assume that you kinda missed out some logic.
If you're going to add another property for it, you should put it in a separate table and reference it to the primary key of the parent table. By this, you will not have any problem adding another value for that.
To answer your second question, you can use .Any property of the context to check out if the item exists in the database. You can have something like this:
var db = new Context();
if(!db.mytable.Any(m => m.field == value)){
db.mytable.Add(value);
db.SaveChanges();
}
Should you have any question, please feel free to ask.
What is the correct way to save a graph of objects whose state you don't know? By state I mean whether they are new or existing database entries that are being updated.
For instance, if I have:
public class Person
{
public int Id { get; set; }
public int Name { get; set; }
public virtual ICollection<Automobile> Automobiles { get; set; }
}
public class Automobile
{
public int Id { get; set; }
public int Name { get; set; }
public short Seats { get; set; }
public virtual ICollection<MaintenanceRecord> MaintenanceRecords { get; set ;}
public virtual Person Person { get; set; }
}
public class MaintenanceRecord
{
public int Id { get; set; }
public int AutomobileId { get; set; }
public DateTime DatePerformed { get; set; }
public virtual Automobile Automobile{ get; set; }
}
I'm editing models, similar to these objects above, and then passing those models into the data layer to save, where for this instance I happen to be using entity framework. So I'm translating these models into POCO entities internal to the DAL.
It appears that unless my models have a state indicating whether they are new or updated, I have quite a bit of work to do to "Save" the changes. I have to first select the Person entity, update it, then match any existing Automobiles and update those and add any new, then for each automobile check for any new or updated maintenance records.
Is there a faster/easier way of doing this? It's possible I can keep track of the Model state, which I guess would be helpful with this, but it would mean changes to code outside of the data layer which i would prefer to avoid. I'm just hoping there is a pattern of usage out there that I can follow for updates like this.
I ran into this issue a while back and have been following this thread on the EF Codeplex site. https://entityframework.codeplex.com/workitem/864
Seems like it is being considered for the next release, I'm assuming EF 7, which apparently is a pretty large internal overhaul of EF. This may be worth checking out... http://www.nuget.org/packages/RefactorThis.GraphDiff/
Back when I was working on this I found another EF post on SO, and someone had an example of how to do this manually. At the time I decided to do it manually, not sure why, GraphDiff looks pretty cool. Here is an example of what I did.
public async Task<IHttpActionResult> PutAsync([FromBody] WellEntityModel model)
{
try
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var kne = TheContext.Companies.First();
var entity = TheModelFactory.Create(model);
entity.DateUpdated = DateTime.Now;
var currentWell = TheContext.Wells.Find(model.Id);
// Update scalar/complex properties of parent
TheContext.Entry(currentWell).CurrentValues.SetValues(entity);
//We don't pass back the company so need to attached the associated company... this is done after mapping the values to ensure its not null.
currentWell.Company = kne;
// Updated geometry - ARGHHH NOOOOOO check on this once in a while for a fix from EF-Team https://entityframework.codeplex.com/workitem/864
var geometryItemsInDb = currentWell.Geometries.ToList();
foreach (var geometryInDb in geometryItemsInDb)
{
// Is the geometry item still there?
var geometry = entity.Geometries.SingleOrDefault(i => i.Id == geometryInDb.Id);
if (geometry != null)
// Yes: Update scalar/complex properties of child
TheContext.Entry(geometryInDb).CurrentValues.SetValues(geometry);
else
// No: Delete it
TheContext.WellGeometryItems.Remove(geometryInDb);
}
foreach (var geometry in entity.Geometries)
{
// Is the child NOT in DB?
if (geometryItemsInDb.All(i => i.Id != geometry.Id))
// Yes: Add it as a new child
currentWell.Geometries.Add(geometry);
}
// Update Surveys
var surveyPointsInDb = currentWell.SurveyPoints.ToList();
foreach (var surveyInDb in surveyPointsInDb)
{
// Is the geometry item still there?
var survey = entity.SurveyPoints.SingleOrDefault(i => i.Id == surveyInDb.Id);
if (survey != null)
// Yes: Update scalar/complex properties of child
TheContext.Entry(surveyInDb).CurrentValues.SetValues(survey);
else
// No: Delete it
TheContext.WellSurveyPoints.Remove(surveyInDb);
}
foreach (var survey in entity.SurveyPoints)
{
// Is the child NOT in DB?
if (surveyPointsInDb.All(i => i.Id != survey.Id))
// Yes: Add it as a new child
currentWell.SurveyPoints.Add(survey);
}
// Update Temperatures - THIS IS A HUGE PAIN = HOPE EF is updated to handle updating disconnected graphs.
var temperaturesInDb = currentWell.Temperatures.ToList();
foreach (var tempInDb in temperaturesInDb)
{
// Is the geometry item still there?
var temperature = entity.Temperatures.SingleOrDefault(i => i.Id == tempInDb.Id);
if (temperature != null)
// Yes: Update scalar/complex properties of child
TheContext.Entry(tempInDb).CurrentValues.SetValues(temperature);
else
// No: Delete it
TheContext.WellTemperaturePoints.Remove(tempInDb);
}
foreach (var temps in entity.Temperatures)
{
// Is the child NOT in DB?
if (surveyPointsInDb.All(i => i.Id != temps.Id))
// Yes: Add it as a new child
currentWell.Temperatures.Add(temps);
}
await TheContext.SaveChangesAsync();
return Ok(model);
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
return InternalServerError();
}
This is a huge pain to me too. I extracted the answer from #GetFuzzy to a more reusable method:
public void UpdateCollection<TCollection, TKey>(
DbContext context, IList<TCollection> databaseCollection,
IList<TCollection> detachedCollection,
Func<TCollection, TKey> keySelector) where TCollection: class where TKey: IEquatable<TKey>
{
var databaseCollectionClone = databaseCollection.ToArray();
foreach (var databaseItem in databaseCollectionClone)
{
var detachedItem = detachedCollection.SingleOrDefault(item => keySelector(item).Equals(keySelector(databaseItem)));
if (detachedItem != null)
{
context.Entry(databaseItem).CurrentValues.SetValues(detachedItem);
}
else
{
context.Set<TCollection>().Remove(databaseItem);
}
}
foreach (var detachedItem in detachedCollection)
{
if (databaseCollectionClone.All(item => keySelector(item).Equals(keySelector(detachedItem)) == false))
{
databaseCollection.Add(detachedItem);
}
}
}
With this method in place I can use it like this:
public void UpdateProduct(Product product)
{
...
var databaseProduct = productRepository.GetById(product.Id);
UpdateCollection(context, databaseProduct.Accessories, product.Accessories, productAccessory => productAcccessory.ProductAccessoryId);
UpdateCollection(context, databaseProduct.Categories, product.Categories, productCategory => productCategory.ProductCategoryId);
...
context.SubmitChanges();
}
However when the graph gets deeper, I have a feeling this will not be sufficient.
What your looking for is the Unit of Work pattern:
http://msdn.microsoft.com/en-us/magazine/dd882510.aspx
You can either track UoW on the client and pass it in with the DTO or have the server figure it out. Both the veritable DataSet and EF Entities have their own internal implementation of UoW. For something stand alone there is this framework, but I have never used it so have no feedback:
http://genericunitofworkandrepositories.codeplex.com/
Alternatively another option is to do real time updates with undo functionality, kind of like when you go into Gmail contacts and it saves the changes as you make them with the option to undo.
It depends HOW you are accomplishing adding/changing the entities.
I think you may be trying to do too much with an entity at any given time. Allowing editing and adding at the same time can get you into a situation where your not sure what is being done with the entity, especially in a disconnected scenario. You should only perform a single action on a single entity at a time, unless you are deleting entities. Does this seem monotonous, sure, but 99% of your users want a clean and easily understandable interface. Many time we end up making screens of our applications "god" screens where everything and anything can be done. Which 9/10 times isn't needed (YAGNI).
This way, when you edit a user, you know you are doing an update operation. If you are adding a new maintenance record, you know you are creating a new record that is attached to an automobile.
To summarize, you should limit how many operations you are making available for a single screen and make sure you provide some type of unique information for the entity so you can try to look up the entity to see if it exists.
I had the similar problem, and couldnt find my own solution. I think that problem is complex. Complete solution for updating graphs in disconected scenario with EF6 I find in extension method RefactoringThis.GraphDiff produced by Brent McKendric.
Exemple brings by author is:
using (var context = new TestDbContext())
{
// Update the company and state that the company 'owns' the collection Contacts.
context.UpdateGraph(company, map => map
.OwnedCollection(p => p.Contacts, with => with
.AssociatedCollection(p => p.AdvertisementOptions))
.OwnedCollection(p => p.Addresses)
);
context.SaveChanges();
}
See more at:
http://blog.brentmckendrick.com/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a-graph-of-detached-entities/
I posted this question, but there was a guffaw where I overwrote my actual question
and just posted code and then it was voted to be closed. I figured the question
was pretty much fubared and didn't get its fair shake, so I'm posting it again.
I'm using Entity Framework 4.1. I have a form that has a good number of fields on it. The table itself actually has more fields than what is on the form. I'm trying to find a way to update just the fields that have changed. I found an example of that here:
EntityFramework update partial model
I've revised that to the code below so that I don't have to specify 40+ fields manually. I'm hoping that there's a cleaner way to do this than what is below. Is there?
Warning the code below is a first draft and is rather crude. All suggestions are welcomed. Thanks!
[HttpPost]
public ActionResult Edit(Location location, FormCollection fields)
{
if (ModelState.IsValid)
{
//db is my context
db.Locations.Attach(location);
StringBuilder sb = new StringBuilder();
//Get properties of Location object
PropertyInfo[] pi = typeof(Location).GetProperties();
//loop over keys of fields submitted by the post
foreach (string submittedField in fields.Keys)
{
//If a property name on the Location object matches a field name
//of one of the submitted properties then mark the property as
//modified
if (pi.Any(prop => prop.Name.Equals(submittedField)) &&
!"ID".Equals(submittedField) )
{
db.Entry(location).Property(submittedField).IsModified = true;
sb.AppendLine(submittedField + "Value: " + db.Entry(location).Property(submittedField).CurrentValue );
}
}
LogUtil.WriteCondensed(sb.ToString());
//Save changes to the database
db.SaveChanges();
return RedirectToAction("Index");
}
return View(location);
}
I wouldn't rely on what's passed to your Edit action to know what can be updated and what cannot for security reasons (remember that what's posted to the server can be changed by the client).
public class YourEntity
{
public long ID { get; set; }
// Updatable fields
public string Field1 { get; set; }
....
}
In the view, use an hidden field for the ID of the object to update (razor syntax):
#model YourEntity
...
#Html.HiddenFor(model => model.ID)
...
#Html.EditorFor(model => model.Field1)
I would then:
check the user has rights to update the entity
retrieve the actual entity from the DB using its ID
manually update property by property
save to DB
Controller:
[HttpPost]
public ActionResult Edit(YourEntity model)
{
try
{
yourServiceLayer.Update(User.Identity.Name, model);
}
catch (CustomSecurityException)
{
...
}
}
Service:
public class YourServiceLayer
{
public YourEntity Update(string userName, YourEntity entity)
{
// Check user has rights to edit the entity (if necessary)
if (this.CanUpdate(userName, entity.ID))
{
// Gets actual entity from DB
var yourDbEntity = this.GetByID(entity.ID);
// Updates each property manually
yourDbEntity.Field1 = entity.Field1;
....
// Saves to DB
this.Save(yourDbEntity);
return yourDbEntity;
}
else
{
// Security exception
throw new CustomSecurityException(...);
}
}
public bool CanUpdate(string userName, long entityID)
{
// Insert logic here
return ....;
}
...
}
If you have 40+ fields, then yes it's boring to write, but at least you have full control on properties that can be updated and properties that cannot.