passing record to a view using entity framework - asp.net-mvc

I am using .NET 4.0, MVC3 and entity framework 4.0
I need to pass a database record of type "statement" to a view. The code to fetch the record is as below:
public ActionResult pdfStatement(string InvoiceNumber)
{
ObjectParameter[] parameters = new ObjectParameter[1];
parameters[0] = new ObjectParameter("InvoiceNumber", InvoiceNumber);
return View(_db.ExecuteFunction<Models.Statement>("uspInvoiceStatement", parameters));
}
where "uspInvoiceStatement" is my stored procedure used to fetch "statement"
I have created a strongly typed view which receives the "statement"
< % # Page Language="C#" Inherits="System.Web.Mvc.ViewPage < InvoiceSearch.Models.Statement >" %>
When i run the application i get an exception saying
"The model item passed into the dictionary is of type 'System.Data.Objects.ObjectResult`1[InvoiceSearch.Models.Statement]',
but this dictionary requires a model item of type 'InvoiceSearch.Models.Statement'.
Help me to escape from this trouble.
"

The error is surprisingly helpful - it tells you, you are sending ObjectResult<Statement> to the view asi viewmodel, while you should send Statement.
Your _db.ExecuteFunction always returns ObjectResult<Statement>, so you need to take Statement from it, like this :
var statementResult = _db.ExecuteFunction<Models.Statement>("uspInvoiceStatement", parameters);
var statement = statementResult.SingleOrDefault();
if (statement != null)
{
return View(statement); // isnt that nicer when you can see what your viewmodel is?
}
else
{
return View("NotFound");
}

Related

How to return view along with model in Async task in Asp.Net MVC

I am testing asynch task in MVC and creating asynchronous task following code. When I return model value along with view name return View("Index", EmpResponse), I am getting error. but if I simply return view return view(). it is working well.
public class AsynchController : Controller
{
string Baseurl = "http://dummy.restapiexample.com/api/v1/";
// GET: Asynch
public async Task<ActionResult> Index()
{
using (var client = new HttpClient())
{
//Passing service base url
client.BaseAddress = new Uri(Baseurl);
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Sending request to find web api REST service resource GetAllEmployees using HttpClient
HttpResponseMessage Res = await client.GetAsync("employees");
var EmpResponse = "";
//Checking the response is successful or not which is sent using HttpClient
if (Res.IsSuccessStatusCode)
{
//Storing the response details recieved from web api
EmpResponse = Res.Content.ReadAsStringAsync().Result;
//Deserializing the response recieved from web api and storing into the Employee list
}
//returning the employee list to view
return View("Index", EmpResponse);
}
}
In simply ActionResult, I know we can return view name and model both together. is it issue with Async task?
Your intention is to pass EmpResponse as the view-model for your Index view, but since you have already deserialized EmpResponse as a string, it matches the wrong overload of the View() helper method (the one which accepts both viewName and masterName).
Try to pass it as an object to match the correct overload:
return View("Index", EmpResponse as object);
A better approach would be to store the received data as a strongly-typed collection of objects:
var empResponse = await Res.Content.ReadAsAsync<IEnumerable<Employee>>();
Then pass it as a view-model:
return View("Index", empResponse);
This isn't really an async issue, but a model type issue. (Though there is an async issue waiting to become a problem... Don't call .Result directly, but instead use await to get the result.)
Your model is a string. But the overload for View() which takes a second string uses it to find a named view. Which is why it's looking for a view called your long JSON string. (Well, a "master view" in this case since you're sending it two strings.)
Don't use a string as a model. Use a model. Instead of sending one big JSON string to your view, deserialize it into a model of some sort. The type is up to you, but the deserialization might look something like:
var response = await client.GetAsync("employees");
YourModelTypeHere model = null;
if (response.IsSuccessStatusCode)
{
var responseString = await result.Content.ReadAsStringAsync();
model = JsonConvert.DeserializeObject<YourModelTypeHere>(responseString);
}
return View(model);
There may even be an option in result to read/deserialize as your model directly, saving you a line of code above. But the overall principle is the same. Use strongly typed models instead of complex serialized strings.
*In this case, YourModelTypeHere looks like it would in fact be an IEnumerable<YourModel> or perhaps an IList<YourModel>, based on the serialized JSON we're seeing.
*Note also that this uses your current logic of sending an empty model to the same view if nothing was successfully retrieved. For an empty string that may be okay, for null it may become problematic depending on what your view is doing. (Either way your view is going to have to change if it currently expects a string as a model.) Perhaps redirect or return an error in the case of no available model? The logic of how your system should behave is up to you.

Show a Message in MVC using TempData

i want to show a message in asp.net mvc. for this, i create a partial view. name of this partial view is _feedback. in body of this partial view i write this codes.
#model MyProject.SharedTools.OperationStatus
#if (Model != null)
{
if (Model.IsSuccess)
{
#:Model.Message;
}
else
{
#:Model.Message;
}
}
i put this code in _layout file:
#Html.Partial("_feedback")
and when i want to see a message from controller, using this code:
operationStatus = _provinceRepository.Save();
if (operationStatus.IsSuccess)
{
TempData["OperationStatus"] = operationStatus;
return RedirectToAction("Index");
}
but i give this error:
The model item passed into the dictionary is of type 'MyProject.Models.ProvinceModel', but this dictionary requires a model item of type 'MyProject.SharedTools.OperationStatus'.
Make sure that you have passed the correct model that your partial is expecting:
#Html.Partial("_feedback", Model.SomePropertyOfTypeOperationStatus)
If you do not specify a model as second argument to the Html.Partial helper, then it will automatically pass the model of the current view (which in your case is of type MyProject.Models.ProvinceModel) and that's why you are getting the error : your partial expects a model of type MyProject.SharedTools.OperationStatus.
Also it is not quite clear where you are using the TempData value that you stored in your controller inside your partial. Maybe it should be something like this:
#model MyProject.SharedTools.OperationStatus
#if (Model != null)
{
#TempData["OperationStatus"]
}
or didn't you just mean to display directly the value you stored in TempData in your partial without using a model?
#TempData["OperationStatus"]

Error copying a record from one entity to another using Entity Framework and Automapper

I am trying to move a record from one table into another matching (almost) table using EF5, MVC, and Automapper.
This code is what I am using:
In My Global Application_Start
//Create Map and manually map StatusCode to Status
Mapper.CreateMap<InstitutionStaging, InstitutionStaging_Archive>()
.ForMember(dest => dest.Status,o =>o.MapFrom(src=>src.StatusCode));
In my Controller
private MyContext db = new MyContext();
Public ActionResult ArchiveMe(int id = 0){
var institutionstaging = db.InstitutionStagings.Find(id);
if (institutionstaging == null)
{
return HttpNotFound();
}
if (ModelState.IsValid)
{
var institutionArchive = Mapper.Map<InstitutionStaging, InstitutionStaging_Archive>(institutionstaging);
//Set Archive date to now.
institutionArchive.ArchiveDate = DateTime.Now;
//Error happens on the next line
db.InstitutionStaging_Archives.Add(institutionArchive);
db.InstitutionStagings.Remove(institutionstaging);
db.Entry(institutionArchive).State = EntityState.Added;
//Commit the changes
var result = db.SaveChanges();
}
}
When it hits the line marked "Error happens here==>" I get the following error message.
{"The entity type InstitutionStaging_Archive is not part of the model for the current context."}
The MyContext contains DbSets for both InstitutionStaging and InstitutionStaging_Archive.
Any idea what is happening?
TIA
J
This error isn't typically a problem with AutoMapper, but rather a problem with your Entity Framework model setup.
It can be because you are using the wrong connection string, or it can be because you don't have the model mapped correctly.
Since we don't know what your model is, what your database looks like, or how your mappings are.. can't help much beyond that.
To prove it to yourself, just comment out the automapper stuff and do it by hand, and I'm pretty sure you'll get the same error.

InvalidOperationException when using updatemodel with EF4.3.1

When I update my model I get an error on a child relation which I also try to update.
My model, say Order has a releationship with OrderItem. In my view I have the details of the order together with an editortemplate for the orderitems. When I update the data the link to Order is null but the orderid is filled, so it should be able to link it, TryUpdateModel returns true, the save however fails with:
InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.]
My update method:
public ActionResult ChangeOrder(Order model)
{
var order = this.orderRepository.GetOrder(model.OrderId);
if (ModelState.IsValid)
{
var success = this.TryUpdateModel(order);
}
this.orderRepository.Save();
return this.View(order);
}
I tried all solutions I saw on SO and other sources, none succeeded.
I use .Net MVC 3, EF 4.3.1 together with DBContext.
There are a number of code smells here, which I'll try to be elegant with when correcting :)
I can only assume that "Order" is your EF entity? If so, I would highly recommend keeping it separate from the view by creating a view model for your form and copying the data in to it. Your view model should really only contain properties that your form will be using or manipulating.
I also presume orderRepository.GetOrder() is a data layer call that retrieves an order from a data store?
You are also declaring potentially unused variables. "var order =" will be loaded even if your model is invalid, and "var success =" is never used.
TryUpdateModel and UpdateModel aren't very robust for real-world programming. I'm not entirely convinced they should be there at all, if I'm honest. I generally use a more abstracted approach, such as the service / factory pattern. It's more work, but gives you a lot more control.
In your case, I would recommend the following pattern. There's minimal abstraction, but it still gives you more control than using TryUpdateModel / UpdateModel:
public ActionResult ChangeOrder(OrderViewModel model) {
if(ModelState.IsValid) {
// Retrieve original order
var order = orderRepository.GetOrder(model.OrderId);
// Update primitive properties
order.Property1 = model.Property1;
order.Property2 = model.Property2;
order.Property3 = model.Property3;
order.Property4 = model.Property4;
// Update collections manually
order.Collection1 = model.Collection1.Select(x => new Collection1Item {
Prop1 = x.Prop1,
Prop2 = x.Prop2
});
try {
// Save to repository
orderRepository.SaveOrder(order);
} catch (Exception ex) {
ModelState.AddModelError("", ex.Message);
return View(model);
}
return RedirectToAction("SuccessAction");
}
return View(model);
}
Not ideal, but it should serve you a bit better...
I refer you to this post, which is similar.
I assume that the user can perform the following actions in your view:
Modify order (header) data
Delete an existing order item
Modify order item data
Add a new order item
To do a correct update of the changed object graph (order + list of order items) you need to deal with all four cases. TryUpdateModel won't be able to perform a correct update of the object graph in the database.
I write the following code directly using a context. You can abstract the use of the context away into your repository. Make sure that you use the same context instance in every repository that is involved in the following code.
public ActionResult ChangeOrder(Order model)
{
if (ModelState.IsValid)
{
// load the order from DB INCLUDING the current order items in the DB
var orderInDB = context.Orders.Include(o => o.OrderItems)
.Single(o => o.OrderId == model.OrderId);
// (1) Update modified order header properties
context.Entry(orderInDB).CurrentValues.SetValues(model);
// (2) Delete the order items from the DB
// that have been removed in the view
foreach (var item in orderInDB.OrderItems.ToList())
{
if (!model.OrderItems.Any(oi => oi.OrderItemId == item.OrderItemId))
context.OrderItems.Remove(item);
// Omitting this call "Remove from context/DB" causes
// the exception you are having
}
foreach (var item in model.OrderItems)
{
var orderItem = orderInDB.OrderItems
.SingleOrDefault(oi => oi.OrderItemId == item.OrderItemId);
if (orderItem != null)
{
// (3) Existing order item: Update modified item properties
context.Entry(orderItem).CurrentValues.SetValues(item);
}
else
{
// (4) New order item: Add it
orderInDB.OrderItems.Add(item);
}
}
context.SaveChanges();
return RedirectToAction("Index"); // or some other view
}
return View(model);
}

ASP.NET MVC, LINQ and ModelBinders

Is there a pre-built ModelBinder I can use with LINQ to get an object from a DataContext and update it on a HTTP post?
For example, currently I have this block of code:
[AcceptVerbs (HttpVerbs.Post)]
public ActionResult Edit (Project project)
{
var projectService = Factory.GetService<IProjectService> ();
project = projectService.GetProject (project.ProjectId);
UpdateModel<Project> (project);
if (!ModelState.IsValid)
return View (project);
project = projectService.SaveProject (project);
return RedirectToAction ("Details", new { id = project.ProjectId });
}
(IProjectService wraps up calls to a LINQ data context)
In order to actually perform the update to the database via the LINQ data context, I need to get the project instance again and then update that instance.
Any attempt to simply save the project instance without first getting it from the data context results in nothing being written back to the database - I'm assuming because the LINQ data context knows nothing of the object it doesn't do anything with it.
Using the Attach method on the Projects table class doesn't work either btw, it throws an exception.
You should look at the implementation in Mike Hadlow's (new BSD) SutekiShop.
In there you will find a DataBindAttribute and BindUsingAttribute which, if I understand correctly, do exactly what you want to do. Notice how the DataBindAttribute.Fetch property is used to rebind the incoming data, or not, (from an HttpPost) to a LINQ entity.
I followed this pattern for one of my projects using ASP.NET MVC and LINQ-To-SQL. It works beautifully.
Here's the source: http://www.google.com/codesearch?hl=en&lr=&q=DataBind+package%3Ahttp%3A%2F%2Fsutekishop.googlecode.com&sbtn=Search
I think the project you pass in to the method is the one you want to perform UpdateModel with isn't it?
Otherwise you are trying to update with pre-existing values not new ones.
Just a thought,
Dan
Code cut out below
[AcceptVerbs (HttpVerbs.Post)]
public ActionResult Edit (Project project)
UpdateModel<Project> (project);
if (!ModelState.IsValid)
return View (project);
var projectService = Factory.GetService<IProjectService> ();
project = projectService.SaveProject (project);
return RedirectToAction ("Details", new { id = project.ProjectId });
}
You need to retrieve the original project as you do then to update it with the properties that have changed in project to update then to submit the update request.
EDIT
Try this code I found:
public static void CloneProperties(this object origin, ref object destination)
{
if (destination == null) throw new ArgumentNullException("destination", "Destination object must first be instantiated.");
foreach (var destinationProperty in destination.GetType().GetProperties())
{
if (origin != null && destinationProperty.CanWrite)
{
origin.GetType().GetProperties().Where(x => x.CanRead && (x.Name == destinationProperty.Name && x.PropertyType == destinationProperty.PropertyType)) .ToList() .ForEach(x => destinationProperty.SetValue(destination, x.GetValue(origin, null), null));
}
}
}

Resources