Editing Master Detail Records using Linq-2-Sql - asp.net-mvc

I'm using Linq-2-Sql as ORM in asp.net mvc-3 application. First have a look at the form:
This is a master-detail form for editing batch info. Master part (Batch table) includes BatchNo, SKUTitle, StageID and Date field. Detail part (BatchDetail) contains fields PackingInstruction, ExpectedYield, Units and TargetDate. I have managed to save this information on single page in create scenario like
public ActionResult CreateBatch(Batch batch, BatchDetail detail)
{
batch.BatchDetails.AddRange(detail.ToList());
ctx.Batches.InsertOnSubmit(batch);
ctx.SubmitChanges();
}
but edit scenario is quite tricky. in this form as shown in figure above, user can add new records in detail portion, delete and edit existing records and by the time data is posted to controller we have no way of knowing which records are newly added, which are modified and which are deleted. So, as a consequence I have to do something like
public ActionResult EditBatch(int id, BatchDetail detail)
{
var batch = ctx.Batches.SingleOrDefault(x=>x.BatchID == id);
ctx.BatchDetails.DeleteAllOnSubmit(ctx.BatchDetails.ToList());
UpdateModel(batch);
batch.BatchDetails.AddRange(detail);
ctx.SubmitChanges();
}
I hate that part of deleting detail records in this scenario. is there a way in Linq-2-Sql that I can get around deleting all records in similar scenarios? If not can I accomplish this in EF without unnecessary deletion?
Edit
I have been studying Attach method of System.Data.Linq.Table but can't seem to figure out anything.

In the MVC Music Store Demo there is a small part that covers deleting items from the shopping cart by using JQuery AJAX to call the delete Controller action. The example is in part 8 of this tutorial.
The actual AJAX call explanation is about 3/4 of the way down the page. The headline for the section is "Ajax Updates with jQuery" if you want to search and find it quickly.

Related

ASP.NET MVC - Complicated view logic

I'm making the transition from webforms to MVC (I know, 3 years late) and I "get it" for the most part, but there's a few things I'd like advice and clarification on:
First off, what happens if you want to dynamically add inputs to a view? for example, in an old webform for generating invoices I had a button with a server-side click event handler that added an extra 5 invoice item rows. The stateful nature of webforms meant the server handled the POST event "safely" without altering the rest of the page.
In MVC I can't think how I'd do this without using client-side scripting (not a showstopper, but I would like to support clients that don't have scripting enabled).
The second problem relates to the invoices example. If my Model has a List, how should I be generating inputs for it?
I know data binding is a possible solution, but I dint like surrendering control.
Finally, back to the "stateful pages" concept - say I've got a Dashboard page that has a calendar on it (I wrote my own calendar Control class, the control itself is stateless, but can use the webform viewstate to store paging information) - how could a user page through the calendar months? Obviously POST is inappropriate, so it would have to be with GET with a querystring parameter - how can I do this in MVC? (don't say AJAX).
Thanks!
In MVC you design your actions to accommodate your needs. For example, if you wanted to be able to add 5 rows to an invoice NOT using client-side scripting, you'd probably have your GET action for the invoice generation take a nullable int parameter for the number of rows. Store the current number of rows in the view model for the page and generate a link on the page to your GET action that has the parameter value set to 5 more than the current value. The user clicks the link and the GET view generates the page with the requested number of rows.
Controller
[HttpGet]
public ActionResult Invoice( int? rows )
{
rows = rows ?? 5; // probably you'd pull the default from a configuration
...
viewModel.CurrentRows = rows;
return View( viewModel );
}
View
#Html.ActionLink( "Add 5 Lines", "invoice", new { rows = Model.CurrentRows + 5 }, new { #class = "add-rows" } )
You would probably also add some script to the page that intercepts the click handler and does the same thing via the script that your action would do so that in the general case the user doesn't have to do a round trip to the server.
<script type="text/javascript">
$(function() {
$('.add-rows').click( function() {
...add additional inputs to the invoice...
return false; // abort the request
});
});
</script>
Likewise for your calendar. The general idea is you put enough information in your view model to generate all the actions that you want to perform from your view. Construct the links or forms (yes you can have multiple forms!) in your view to do the action. Use parameters to communicate to the controller/action what needs to be done. In the rare case where you need to retain state between actions, say when performing a wizard that takes multiple actions, you can store the information in the session or use TempData (which uses the session).
For things like a calendar you'd need the current date and the current view type (month/day/year). From that you can construct an action that takes you to the next month/day/year. For a paged list you need the current page, the current sort column and direction, the number of items per page, and the number of pages. Using this information you can construct your paging links that call back to actions expecting those parameters which simply do the right thing for the parameters with which they are called.
Lastly, don't fear AJAX, embrace it. It's not always appropriate (you can't upload files with it, for example), but your users will appreciate an AJAX-enabled interface.
In MVC you can store application state in various ways. In your controller you have direct access to the Session object and you can also store state to the database.
your view can contain basic control flow logic, so, if your model has a list you can iterate over it in the view and, for example, render an input control for each item in the list. you could also set a variable in a model to be the maximum number of rows on the viewpage and then render a row in a table for the number specified by the model.
paging is basically the same thing. you can create a partial view (user control in the webform world) that shows page numbers as links, where each link calls an action that fetches the data for that page of results.
i'm not sure what your beef is with ajax or javascript

ASP.NET MVC ViewModel to Hold Lists & "Images or YouTube URL's"

I'm posting this question because I do not know the best/correct way of doing the following.
My team-mate (the designer) sent me a good looking design that includes a wizard for adding new items (for auction). The user has to fill in all the required details which include the title, description, starting price...etc AND a list of tags (up to 4 tags - chosen from the database, will use auto complete) as well as a list of up to 3 images/youtube url's (for the sake of better explanation check this image out: http://i55.tinypic.com/2v11zzr.png)
Ok so I figured out how I should do the wizard ( reference: how to make a wizard with ASP.Net MVC) but I'm not sure about how to collect the lists and the images/url's. Here's what I'm thinking:
For the images/url's, I should create a parent view model from which two sub-classes (ImageViewModel & YoutubeUrlViewModel) would inherit from and then in the controller action when I parse the post data, I would check to see the instance of the parent view model and act accordingly.
Now about the lists, I'm not sure whether I should include a List in my view model or whether I should include 4 string properties representing the tags (the same will apply to the list of images/url's).
So what's the best way of doing this?
And Haacked to the rescue: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
:)

How To: Use MVC and Ajax to add / remove a row in grid for data entry + model binding?

I'm new to Ajax, but I think I know how to reasonably use MVC + model binding.
What I'm trying to do is to create an Add button (or Ajax.ActionLink) to add a new row in my grid for data entry. Example: Think of a typical Order entry system with Order (header) and Product (items). My OrderViewModel contains an "Order" object, and the Order object contains a collection List.
The way I plan to do this is that my View render the grid in a PartialView, and the PartialView is a simple for-loop to create the table tags from the List. I will use the default model binder (for collections).
Anyone have suggestions on how to do this?
I've already figured out how to do this using jQuery, but I want (i think I want) to try and use Ajax so that I can add my custom business logic (e.g. like setting defaults, translations, etc.)as opposed to do this client-side.
In other words, I want to do do something similar to what the Telerik grid does with its Ajax Editing with the Add/Remove link/buttons.
Tips and sample code would be greatly appreciated.
One of my challenges, and not sure if I'm going down the wrong way, is that I don't know how to pass back the model back to the Controller Action from the Ajax submit. When I look at Telerik's code, it looks like they store the persisted items in HttpContext.Session, and this is exactly the reason why I don't want to use their grid.
Thanks.
They might choose the session repository storage for demonstration purposes. If you transform the logic from their SessionProductRepository class for your model and implement identical Update/Insert/Delete methods for it, you'll probably get what you want.

ASP.NET MVC: Best practices for keeping session state in a wizard-like app

Let's say I have a Web application implemented like a set of wizard pages to edit a complex object. Until the user clicks on the "Finish" button, the object doesn't get saved to the back-end system (a requirement), so in the meantime I have to keep the whole information about the object in some kind of a session state.
Also, some of the wizard pages have to show combo and list boxes with potentially large number of items. These items are fetched from the back-end system using a Web service.
Coincidentally, the wizard allows the user to freely jump from one wizard page to any other (using tab links on top of the form), so it's not a simple "next, next... finish" thing.
Additional constraint: the Web application runs on a Web farm and the customer is weary of using server-side session state. In the best case they want to keep the size of the session state minimal (they had problems with this in the past).
So basically there are two problems here:
How/where to keep data entered by the user in the Wizard?
Whether to cache the combo/list items received from the back-end and if so, where?
Options I'm considering:
Storing the object in a WebForms-like ViewState (by serializing it into the HTML page). This would also include the combo box items. Obviously, there could be a problem with HTML pages becoming very large and thus Web application will be slow.
Storing it into server-side session state, regardless of the customer's wishes and without knowing how the performance will be affected until it is tested on the actual Web farm (late in the project).
I cannot decide between the two. Or is there another alternative?
Why cache at all? You could just have the tabbed pages where each page is a div or panel and just display the current div relating to your tab. That way you dont have to keep track and process all the inputs when the user submits the form.
Is it possible to store the wizard data in a temporary table in the database? When the user finishes the wizard the data is copied from the temporary table and deleted. The temporary table includes a timestamp to remove any old uncompleted data.
As Daisy said, it doesn't have to be cached. You could also use hidden form fields. Because these could map to the same object on each controller action, you could progressively build the object through successive pages.
//Here's a class we're going to use
public class Person
{
public int Age {get;set;}
public string Name {get;set;}
public Person()
{
}
}
//Here's the controller
public Controller PersonCreator
{
public ActionResult CreatePerson()
{
//Posting from this page will go to SetPersonAge, as the name will be set in here.
return View();
}
public ActionResult SetPersonAge(Person person)
{
//This should now have the name and age of the person
return View(person);
}
}
//Here is your SetPersonAge, which contains the name in the model already:
<%= Html.Hidden("Name", Model.Name) %>
<%Html.TextBox("Age") %>
And that's pretty much it.
I can suggest a few more options
Having the entire wizard as a single page with the tabs showing and hiding content via javascript on the client-side. This may cause the the initial page to load slower though.
Caching the data at the server using the caching application block (or something similar). This will allow all the users to share a single instance of this data instead of duplicating across all sessions. Now that the data is lighter, you may be able to convince the customer to permit storing in the session.
There is a lot of resistance in the MVC community against using Sessions. Problems are that a lot of us developers are building login systems like a bank website. One could argue for hidden fields and that works for some situations but when we need to time a user out for security and compliance, then you have several options. Cookies are not reliable. Relying on Javascript timers are not reliable and are not 508 compliant as the goal should be to degrade gracefully. Thus for a Login, a Session is a good option. If you write the time to the client browser, to the server database or server file system, you still have to manage the time per user.
Thus use Sessions sparingly, but don't fear them. For the wizards, you technically can serialize hidden fields passing them around. I suspect the need and scope will become much greater and an authorization/authentication implementation with Sessions will be the crux of the application.
If you cannot use ajax (for validation & dropdowns and ability to convert wizard to tabbed page) and cannot use html5 (for dropdown caching and form state saving in local storage), then I think you are pretty out of available "best practices" and you have to resort to bad (or worse) one.
As MVC is opponent of WebForms regarding session usage, maybe you can use a workaround? For example, besides storing all these values in some temporary database records you need to clean up later, you could set up AppFabric extension for Windows Server and use it to store dropdown list items (and scope can be for all users, so if more users are using system at the same time you need only one call to web service to refresh cache), and also to temporary store your objects between steps. You can set your temporary objects in AppFabric to automatically expire so cleanup is not necessary. It can also be of help for speeding up other parts of your system if you extensively call another system over web services.
I've been dealing with the same issue and, while my requirements are a little simpler (keeping state for just a few strings), my solution may work for you. I'd also be interested in hearing others thoughts on this approach.
What I ended up doing is: in the controller I just dump the data I want into the Session property of the Controller and then pull it out next time I need it. Something like this for your situation:
//Here's the controller
public Controller PersonCreator
{
public ActionResult CreatePerson()
{
//get the age out of the session
int age = (int)(Session["age"]);
//do something with it...
return View();
}
public ActionResult SetPersonAge(Person person)
{
//put the age in the session
Session.Add("age", person.Age);
return View(person);
}
}
The thing I like about this is I don't have to put a bunch of hidden params around on my view pages.
The answer to this can be found in Steve Sanderson's ASP.NET MVC 2/3, and requires a reference to the MVC Futures assembly. This link to Google Books is exactly what he does.
In essence, you serialize the wizard data to the View. A hidden field is rendered storing all of the acquired information.
Your controller can work out what to do through the use of the OnActionExecuting and OnResultExecuted (to cater for redirects) to pass it to the next view.
Have a read - he explains it much more thoroughly than I can.

Data entry screen in asp.net MVC

You may thick that it is a silly question but I am confused!
I have a situation that I have a Lecture table.
And I want to store attendance of who have registered for it.
I have master table of People who will register, lecture table, and Important one table is attendance that stores P.k. as f.k. of rest of the table.
On Index view of lecture operator will select Attendance and enter attendance information.
My problem It show only one page for attendance entry and that page can also open open in EDIT mode, for editing attendance.
So what would be the design of the page and process flow of taking attendance?
Some of the ways:
You can use same page for editing and listing if you define block where are you rendering partial/dynamic block(user control). You need to extend HtmlHelper so you can pass name of a UserControl and you have to from controller pass to view(with ViewData) name of user control you want rendered. This way you can use same template(View/Page) with different actions and different response.
You can have editing on client. You can use jQuery UI library to popup dialog box for editing. For posting editing information back, you can use jquery .ajax method. As things happening on client and you request different actions on controller, it doesn't matter how you will approach this in design sense. You can use dialogs, you can transform existing elements on page etc.
Hope this helps

Resources