unable to do add/edit on same view and controller in mvc - asp.net

As I was trying to create and edit using single view and on the same controller,the error was coming while managing ID.
I was able to manage it while EDITING by :
#Html.HiddenFor(m => m.ID)
but while creating(ADDING) a record I was not able to get ID
[ ERROR : The ID field is required ]
.The error came while checking (ID is a Primary Key in DB):
ModelState.IsValid
as I was able to manage ID by using :
[Bind(Exclude = "ID")]
but again this will create an issue while editing.SO please give me a way to add and edit using same controller.

You always include id as a hidden field on your view:
#Html.HiddenFor(m => m.Id)
Do not make the Id property required so the validation will always pass. If it is 0 or null (if your Id property is of nullable type) then you treat it as an insert. Otherwise perform an update:
if (ModelState.IsValid)
{
if (!model.Id.HasValue || model.Id == 0){
{
// Do insert here..
} else {
// Do update here...
}
}

If you want to create a Single view for create and Edit then you should first check for the ID
If a form passes the id as 0 then its Create otherwise Edit
[HttpPost]
public ActionResult Create(Model model)
{
if(model.id != 0 )
{
//Edit Code here
}
if( model.id == 0)
{
//Create Code Here
}
return RedirectToAction("","");
}
As you have already maintained the ID as hidden. You wont have any problem on passing the ID.
But Before HTTPPOST
On HTTPGET... Follow Following Code
Public Action Result Create(int id = 0)
{
if(id != 0)
{
var model = //code for selecting the data for the respective id
return view (model);
}
return view();
}
EDITED : This should work out...

Try set Id=0 when adding and you need hidden input for Id for editing (to send back Id).
When you creating new object and you set Id=0 it's mean that EF will compute Id itself.
When you editing object you must send back Id to controller as Id is properties of your object and it's used by EF to determine which object is updating.
Excluding Id from binding it's not good idea ;) If you exclude Id so how controller/EF will know which object was sent back from view?
Clarification:
In your view you should have just: #Html.HiddenFor(m => m.Id)
In your case you can/should set Id=0 in controller/action when you adding new object.
If you want add/edit object in one view, you can create empty object (and set Id=0) in your add action and pass it trough to your view... Then you always have valid object/Model in your view whatever you adding or editing.
Example of add action:
public void Create(int id)
{
// ...
return View(new YourObject {
Id = 0,
});
}

Related

How to retrieve data for two different portions of a single web page using LINQ from the same database table?

I am working on a project in .NET MVC5 using code first approach, I have created a database table and I want to retrieve some specific records in two different portions (categories as of database) of Index page or (Home Page) from the same database table.
I have created the below controller which works fine for one of the portions named "Recent Events" but I am puzzled when I try to retrieve some records for the second portion of home page, suppose which is named as "Featured_Events" using the same controller?
Initially i just want the logic to work fine for the both portions, but in case if an expert is reading my post, Then kindly also tell me how can i retrieve specific number of records in Featured Events portion, As you know that i am retrieving 8 records in Recent Events, what if i want to retrieve 12 records in Featured Events section?
This is the controller:
public ActionResult Index()
{
var result = (from i in _context.consult
orderby i.date_of_event descending
where i.category == "recent_events"
select i).Take(8);
return View(result);
}
Just for further clarification: In the view I use foreach loop to retrieve data:
As an example It looks like this:
#model Ienumurable <Example.Models.consultation>
CODE FOR RECENT EVENTS PORTION
#foreach (var recent in Model)
{
if (#recent.category == "recent_events")
{
<p>#recent.date</p>
<p>#recent.details</p>
}
}
CODE FOR FEATURED EVENTS PORTION
#foreach (var featured in Model)
{
if (#featured.category == "featured_events")
{
<p>#featured.date</p>
<p>#featured.details</p>
}
}
I would be really glad if anyone could assist me in this regard.
assuming you have two lists of items you want to return, create a new class holding both lists as public properties, populate both in the controller and return the new class.
Your Model will now contain both and you can iterate over both using Razor.
example :
public class EventsDataModel
{
public List<Event> RecentEvents { get;set; }
public List<Event> FeaturedEvents { get;set; }
public EventsDataModel()
{
RecentEvents = new List<Event>();
FeaturedEvents = new List<Event>();
}
}
your controller now populates both:
public ActionResult Index()
{
var result = new EventsDataModel();
result.RecentEvents = (from i in _context.consult
orderby i.date_of_event descending
where i.category == "recent_events"
select i).Take(8);
result.FeaturedEvetns = whatever
return View(result);
}

Custom ValidationAttribute: How to check for duplicate value, ignoring the object being edited

I'm trying to write a custom ValidationAttribute that verifies no records already exist with the same value.
The problem is that if the user is editing an existing record, then my code will find a record that matches the same value. It will find the one that is being edited if the user has not changed that value.
So I thought I could compare the value to the value in ValidationContext.ObjectInstance to detect when it hasn't changed, something like this:
public class UrlTagValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
string tag = value as string;
if (string.IsNullOrWhiteSpace(tag))
return new ValidationResult("URL Tag is required.");
// We allow a valid that is equal to the original value
if (context.ObjectInstance is TrainerModel model && model.Tag == tag)
return ValidationResult.Success;
// Cannot be an existing tag
using (var dbContext = new OnBoard101Entities())
{
if (!dbContext.TrainerDetails.Any(td => td.Tag == tag))
return ValidationResult.Success;
}
return new ValidationResult("This URL Tag is not available. Please enter a different one.");
}
}
But this doesn't work. I find that the value in ValidationContext.ObjectInstance often matches the value entered even when I'm creating a new record.
I'm having a hard time finding good and current documentation on the current usage of ValidationContext. Can someone suggest a way to check if any records exist that match the entered value BUT allowing it when a record is being edited and the value of this field has not changed?
The item which is currently being edited most likely has some kind of property to identify it (look it up in the database). Therefore, you need to get that property so you can exclude that when you are searching the database for duplicate tags. Here is how to do that in your custom validation class. I am making the assumption the identifier is named TrainerId:
public class UrlTagValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
string tag = value as string;
if(string.IsNullOrWhiteSpace(tag))
return new ValidationResult("URL Tag is required.");
var currentTrainer = validationContext.ObjectInstance
as TrainerModel;
if (currentTrainer == null)
{
// What do you want to do? You cannot always return an error
// because another type could be using this custom validation.
// Or you can return an error. Depends on your requirements and
// and usage.
}
using(var dbContext = new OnBoard101Entities())
{
if(dbContext.TrainerDetails.Any(td => td.Tag == tag && td.TrainerId !=
currentTrainer.TrainerId))
{
return new ValidationResult("This URL Tag is not available. Please enter a different one.");
}
}
return ValidationResult.Success;
}
}

How to Update Model in ASP NET MVC 6?

Scenario: How to update a model?
ASP MVC 6
I am trying to update a model. For passing the model information to the client(browser/app) I am using the DTO.
Question 1: For updating, should I post the whole object back?
Question 2: Is there a way I can easily pass only the information that is updated? If yes, how?
Question 3: Can I use JSON Patch for updation?
Question 2: Is there a way I can easily pass only the information that
is updated? If yes, how?
Yes. You should create a view model which should have only those properties needed for the view.
Let's assume your use case is to build a view which allows user to edit only their last name.
public class EditUserViewModel
{
public int Id {set;get;}
public string LastName {set;get;}
}
And in your Get
public ActionResult Edit(int id)
{
var user = yourUserRepository.GetUser(id);
if(user!=null)
{
var v = new EditUserViewModel { Id=id,LastName=user.LastName};
return View(v);
}
return View("NotFound");
}
And the view
#model EditUserViewModel
#using(Html.BeginForm())
{
#Html.TextBoxFor(s=>S.LastName)
#Html.HiddenFor(s=>s.Id)
<input type="submit" id="saveBtn" />
}
and your HttpPost action
[HttpPost]
public ActionResult Edit(EditUserViewModel model)
{
// Since you know you want to update the LastName only,
// read model.LastName and use that
var existingUser = yourUserRepository.GetUser(model.Id);
existingUser.LastName = model.LastName;
yourUserRepository.Save();
// TO DO: redirect to success page
}
Assuming yourUserRepository is an object of your data access classes abstraction.
Question 1: For updating, should I post the whole object back?
Depends on what you want from the end user. With this view model approach, It is going to post only the Id and LastName and that is our use case.
Can I use JSON Patch for updating?
Since you are only sending the data which needs to be updated (partial data), you should be fine.
If you want,you may simply serialize your form data(which has only Id and LastName) and use jQuery post method to send it to your server.
$(function(){
$("#saveBtn").click(function(e){
e.preventDefault(); //prevent default form submit
var _form=$(this).closest("form");
$.post(_form.attr("action"),_form.serialize(),function(res){
//do something with the response.
});
});
});
To prevent overposting, you can use a binding whitelist using Bind attribute on your HttpPost action method. But the safest strategy is to use a view model class that exactly matches what the client is allowed to send.
Instead of this
UpdateModel(model);
You now can call this
await TryUpdateModelAsync(model);

How can i prevent editing a Parent object if it has been assigned to a child object in my asp.net MVc web application

I have two objects:-
LabTest
LabTestDetails
Where a LabTest object can have zero or many LabTestDetails objects.
I need to implement the following business rule:-
The user should not be able to edit a LabTest object if it has been assigned to one or more LabTestDetails objects.
Currently i have implemented a helper method named IsAlreadyAssigned on the LabTest object (to check if the LabTest object has been assigned to any LabTestDetails object):-
public partial class LabTest
{
public bool IsAlreadyAssigned(int id)
{
return (LabTestDetailss.Any(r2 => r2.LabTestID == id));
}}
Then i have added the following checks on the Get & Post Edit action methods:-
public ActionResult Edit(int id)
{
LabTest c = repository.GetLabTest (id);
if ((c == null) || (c.IsAlreadyAssigned (id)))
{
return View("Error");
}
return View(c);
}
[HttpPost]
public ActionResult Edit(int id, FormCollection colletion)
{
LabTest c = repository.GetLabTest (id);
if ((c == null) || (c.IsAlreadyAssigned (id))) // *******
{
return View("Error");
}
try
{
if (TryUpdateModel(c))
{
elearningrepository.Save();
return RedirectToAction("Details", new { id = c.LabTestID });
}
}
The above might work fine on most of the cases, but if the LabTest object were just assigned to a labTestDetails object by another user after the if ((c == null) || (c.IsAlreadyAssigned (id))) check on the post action method i mark it as(*) on the above code , then my business logic will be broken.
so is there a way to implement my action methods so that it will always prevent editing a LabTest object if it has been assigned to a LabTestdetail object .
BR
You could use a stored procedure, as suggested in the comments, but you could also create a service method that checks whether or not a LabTest is assigned, like
public bool LabTestIsAssigned(int labTestId)
{
using (var context = new YourContext())
{
return context.LabTestDetails.Any(d => d.LabTestID == id);
}
}
The advantage of using this method, rather than the navigation property, is that it is guaranteed to reflect the current state of the database.
Note that you'll have to do this check just before saving changes as well! Even then, an insert may occur right after evaluating the check and just before saving the changes.

How to get the updated contents in a YUI simple editor from your view model

I am using ASP.NET MVC3 with the razor view engine. I am also using a the Yahoo User Interface 2 (YUI2) simple editor.
My view has a view model called ProductEditViewModel. In this view model I have a property defined as:
public string LongDescription { get; set; }
In my view I would create the YUI2 simple editor from this input field. The field is defined in the view like:
<td>#Html.TextAreaFor(x => x.LongDescription, new { cols = "75", rows = "10" })<br>
#Html.ValidationMessageFor(x => x.LongDescription)
</td>
Here is a partial view of my Edit action method:
[Authorize]
[HttpPost]
[ValidateInput(false)]
public ActionResult Edit(ProductEditViewModel viewModel)
{
if (!ModelState.IsValid)
{
// Check if valid
}
// I added this as a test to see what is returned
string longDescription = viewModel.LongDescription;
// Mapping
Product product = new Product();
product.InjectFrom(viewModel);
// Update product in database
productService.Update(product);
return RedirectToRoute(Url.AdministrationProductIndex());
}
When I view the contents of the longDescription variable then it should contain the values from the editor. If I edit the contents in the editor then longDescription still only contains the original contents, not the updated contents. Why is this?
I suspect that somewhere in your POST action you have written something like this:
[Authorize]
[HttpPost]
[ValidateInput(false)]
public ActionResult Edit(ProductEditViewModel viewModel)
{
...
viewModel.LongDescription = "some new contents";
return View(viewModel);
}
If this is the case then you should make sure that you have cleared the value from the ModelState before modifying it because HTML helpers will always first use the value from model state and then from the model.
So everytime you intend to manually modify some property of your view model inside a POST action make sure you remove it from modelstate:
ModelState.Remove("LongDescription");
viewModel.LongDescription = "some new contents";
return View(viewModel);
Now when the view is displayed, HTML helpers that depend on the LongDescription property will pick the new value instead of using the one that was initially submitted by the user.

Resources