Efficiently and Eloquently Inserting/Deleting/Updating Child Records - c#

I have page a that shows a header record along with a list of detail records. I'm struggling with making a clean and efficient way of inserting/deleting/updating detail records when the user clicks Save.
The detail records are shown in a jQuery DataTable, and the view model behind each detail record has an IsNew and IsRemoved property. When the user adds a detail record, its IsNew property is set to true. When the user removes a detail record, it is soft-deleted and its IsRemoved property is set to true.
When the user clicks Save and posts the page to my controller, my logic right now looks like this
[HttpPost]
public ActionResult EditData(ViewModel viewModel)
{
// Update the record's header details here
// ...
foreach (var childViewModel in viewModel.Children)
{
// Use AutoMapper to map the view model to a model
MyChildRecord childModel = this.mapper.Map<MyChildRecord>(childViewModel);
if (childViewModel.IsNew)
{
this.context.MyChildRecords.Add(childModel);
}
else if (childViewModel.IsRemoved)
{
this.context.MyChildRecords.Attach(childModel);
this.context.MyChildRecords.Remove(childModel);
}
else
{
this.context.Entry(childModel).State = EntityState.Modified;
}
}
this.context.SaveChanges();
return RedirectToAction("EditData", new { id = viewModel.Id } );
}
The thing I don't like about this code is that even if nothing about a child record is changed, I'm still updating it in the database. The only solutions I can come up with to prevent that are
Have each view model store a copy of its original values and then compare its current value to its original values when the user saves the page. I don't like this solution because I'll have to have a bunch of code to store the original values, and then I have to put the original values as hidden fields on my ASP page so that they get carried between web requests.
When the user saves the page, have the controller iterate through each child view model, load the original data from the database, and compare the view model's current values to see if the row needs to be updated or not. I don't like this method because it involves a lot of extra code for the field comparison, and I still have to make unncessary trips to the database.
This seems like a common scenario so there must be a commonly accepted way of doing this. How should I be going about this?

First, consider that while the extra updates do incur additional network traffic, the likelihood is that your actual database server is smart enough not to actually do anything to the database on disk if nothing changed.
Secondly, consider that your application might not be the only program working with your database table. Somebody else might have changed the record while you were looking at it. To be safe, you really need both solutions together: check whether the user changed your form, AND check whether the database row is the same as it was when you got the data to show to the user. If both have changed, usually it is considered an error and the user is notified.

Related

How do you set the Database details on Sitecore.Context.Item.Database item in Sitecore

I am currently reviewing an existing Sitecore project. One of the items has a controller rendering that outputs a form onto the Layout. In the Action Method, for the controller rendering, there is a line that seems to get the Item's Database Name credentials. I have had a look at the Item's Layout, however I can't find any Database field. I know that Sitecore.Context.Item is meant to get the current Item. However, I am know sure in the code below, how where Sitecore.Context.Item.Database.Name is pointing to. Any explanation would be really appreciated.
public ActionResult Form()
{
Item currentItem = Sitecore.Context.Item;
if (!IsValid(currentItem))
{
return Redirect(Sitecore.Context.Site.VirtualFolder);
}
FormModel model = new FormModel(currentItem);
model.PageModel.Db = Sitecore.Context.Item.Database.Name;
model.PageModel.ItemId = Sitecore.Context.Item.ID.ToString();
return View(model);
}
Sitecore.Context.Item.Database.Name provides the context database in which you are viewing the item. So if you are inside Experience Editor you will get master or if you are on site itself then you will get web.
Sitecore.Context is to provide context information like item, database or language. So for example Sitecore.Context.Item.Language will provide context language in which you are viewing the content on site.
Your "Database" property is not something you will find in a field or anything - it refers to the Sitecore database where the item is located. In a simple setup that will most likely be "master" or "web". The name property of the database will just refer to a string that indicates the database (master - web - ...).
As in Sitecore your item can come from different databases, this property can be used to identify that source. Published items will in a standard setup be in the web database, the master database will contain all items and versions and is used while editing.

Pass entire object from view to controller in ASP.NET MVC 5

Is there a way to pass entire object from ASP.NET MVC 5 View to a Controller? This is my situation:
I have a View that displays all rows from a DB table
The view's model is IEnumerable
Each row has a link after it's data that leads to the scaffolded UPDATE view
Is there a way to pass the entire object to the Update controller method so it would initially fill the form inputs with the old data? Something like:
#Html.Action("Update me!", "Update", new { objectFromModelList })
And then in the controller
public ActionResult Update(MyType parameter)
{
return View(parameter);
}
Or something like that. Please help, I am new to this and can't find the answer anywhere.
Your objects could be so big! Query string's has a limitation on how much data you can pass via those based on the browser. You should consider passing a unique id value (of the record) and using which get the entire record from db in your action method and pass that to the view.
#foreach(var item in SomeCollection)
{
<tr>
<td> #Html.Action("Update me!", "Update", new { id = item.Id }) </td>
</tr>
}
and in the action method
public ActionResult Update(int id)
{
var item = GetItemFromId(id);
return View(item);
}
Assuming GetItemFromId method returns the method/view model from the unique id value. Basically you get the entire record using this unique id from your db table/repository.
Assuming that your Update View isn't of type IEnumerable...
You just need to pass the ID of the record that you want to send to the Update view...
Like so:
#Html.Action("Update me!", "Update", new { id = item.ID })
Then your Update action would look like this:
[HttpGet]
public ActionResult Update(int id)
{
var parameter = db/* connection string variable */.TableName.Find(id);
return View(parameter);
}
Then your link should work appropriately.
Hope this helps!
I have searched myself and the best way, aside from passing the ID, that I have found is to store any other variables that you might need into hidden input fields or HTML5 tags. Then you can script a way to handle any button/link click events. This way you can store vital object properties of each record and easily pass them back to a controller. Think Client-side here, Once the data ends up Client-Side, use Client-Side tools to handle and pass it back to server side/controller.
I do something similar with a type of library reservation system that allows users to reserve items on available dates. I pass all available records to the view. Each record has a few fields that I want to hold onto including the ID for users reference. When the user clicks the button, I collect the needed fields.
You could use HTML5 form input fields that are hidden or you could just use JavaScript to collect those values using GetElementByID. An example of this would be to store the ID in the div wrapper. Then have another div hold a sub parameter. You can use Javascript to find the record ID and then get the second div by it's id. Example would be get the id NameRecord from XRecord where X = the ID passed.
I then pass those values to the controller, instantiate a new class/object for the reservation. The new class object also has the item class/object as a property. For example consider the following;
var reservation = new Reservation
{
myKit = new ResourceKit()
};
After that, you can store it in a session if you need to build on it. In my case I am holding it in a session because I allow the user to check availability/dates. These items are a physical resources that gets checked out similar to a library and are transferred via office mail.
If you dont mind the data sitting client-side, you can store it using LocalStorage and JavaScript. This type of data isnt secure at all much like a cookie. One of the ways that I have used this is to set site preferences. Users can select a color scheme and those preferences are stored in LocalStorage. That way when the return to the site those preferences remain. This is a key attribute of LocalStorage and might not be applicable to your needs/circumstances.

How to display specific row of records from database ASP.Net?

I have created a form that includes a registration and login form in Visual Studio using the default Web Form that is already provided. So the code for someone to register and login is already provided and their inputted details are already sent to a default membership database.
What I want is once a user is logged in, they can access a members-only webpage where they enter additional details and those details can be inserted into a database I created myself. However, what I also want, is once the person enters their details into my database, they can later go back and change it.
What I would like to know is how can I get that specific user to access only their records?
I understand the use of FormView and DetailView, but they seem to display the entire database table, while I only want that specific view. I thought of trying to display the recently added records, but this causes problems because when someone else logs back into the website again and view their details, it will probably end up showing another record instead.
You should write the user ID in the session, when he logging in. After that you can retrieve it from the session and know exact which user is log on.
Session["_USERID"] = value;
You can create property:
public Guid UserID
{
get
{
if(Session["_USERID"] != null)
return (Guid)Session["_USERID"];
}
set
{
Session["_USERID"] = value;
}
}
This property will be set to the userID on log on and set to null on log out. It will be nice to have base class which every UI page will inherit and put this property in the base class. In this case when you open current user page, you can take this property.

Which one is a generally better concept: store selected item or only its key (aka id)?

I have a quite big dilemma nowadays about general viewmodel design concepts. I mean general, like it's not exactly bound to a given language or environment: I had same dilemma when I wrote viewmodels for Winforms, WPF or KnockoutJS.
As a simplified use case, consider that I have a view where I have to select a country and a city from two select boxes. Both are represented in the database with a unique ID, a Name, and some other relevant information like - let's say - Population. Now imagine that I have to present a textual form of the currently selected data in for example the view's heading like "You've selected London, England". Now here is my two alternatives for creating a viewmodel, I will try to enumerate the pros/contras which I'm already thinking of below each version. The code is written in kind of pseudo way to be as generic as possible.
class RegionModel {
ID: number;
Name: string;
Population: number;
}
Version 1: Storing the selected object.
class MainView {
SelectedCountry: RegionModel;
SelectedCity: RegionModel;
SelectionInfo: string; // computed, should return the "You've selected ...." caption
Countries: List<RegionModel>; // datasource for country select
Cities: List<RegionModel> // datasource for city select
}
Pros:
Straightforward and easy to understand due to that the selected
item's type is the same as the selectable items' type.
Easy to compute such infos like "You've selected ..." because all the
members of the currently selected item are present directly.
Cons:
It holds more information than usually a consumer API needs. Usually
it needs only the ID.
If it's used in a client-side app, the whole selected object will be
returned to the server, consuming bandwidth.
If the consumer API needs only ID's (like in most cases), I have to
solve some kind of conversion before I pass it. In a web app probably
during serialization to JSON for example.
Version 2: Storing only the ID's of the selected items
class MainView {
SelectedCountryID: number;
SelectedCityID: number;
SelectionInfo: string; // computed, should return the "You've selected ...." caption
Countries: List<RegionModel>; // datasource for country select
Cities: List<RegionModel> // datasource for city select
}
Pros:
It's efficient in the way that it contains only the information which
is most likely needed by consumer APIs.
No additional conversion is needed, and efficiently can be passed
nearly "as is" to a server-side or other API.
Cons:
Not so straightforward and readable (in my opinion).
What about computing the info string? That's now much harder, I need
to grab the needed members from the selection source lists with a
search by the given ID, so it depends heavily on the consistency of
those lists (I mean the item must be present there).
I hope it won't be closed quickly as unconstructive. Any kind of advices, thoughts or experiences will be appreciated. Also, if the answer is "it depends", please try to give my some points where and when to use which.
UPDATE
I think my question was a bit unclear. I know about decoupling viewmodel from database entities, here I never mentioned database entities. I mentioned an "abstract consumer API". In a concrete scenario: if the API needs the selected items' Names, and my API needs only the IDs, which alternative should I choose, and where should do the conversion?
For example my server expects a data format like this (JSON):
{
"SelectedCountryID": 2,
"SelectedCityID": 5
}
and nothing else. How could I handle it in an elegant way? I would like to avoid repeating myself by doing a manual conversion.
Depending on how your datasources are implemented, it may make not difference: if you are retrieving the list of countries and cities, you can either store a reference to the selected value, to one of its fields or its index in the list.
Disregarding that, you should decouple your view model entities from your database entities and put into your view model ones only those fields required by the views. This way, your information traffic is minimized and your code is less affected by changes in the database.
EDIT following OP's update:
Talking about interacting with an API instead of a database, I think you can apply the same ideas, just replacing "database entities" by "service layer entities" (for instance, the JSON coming in/out your server). Take the returned data that into your view model objects, holding those attributes that you need. Obviously you also may need to store an id as you stated, when you'll need to refer to the same entity later on.
From a theoretical point of view, you should not include any other fields not consumed by the view, but you could do so depending on your requirements. For instance, in cases when you'll need to pass those fields back to the service layer and you don't want to query again by id to retrieve the service entity. However there are other alternatives to this (for example, some kind of cache), the exact balance depends on your requirements.
Base on MVVM pattern your viewModel should be an object with all properties which you need to display in view. ViewModel should be only used to be strictly binded to the view. Anyway your example it's not very good in my opinion. You shouldn't think about viewModel in case of storing something, please think more about presenting data.
Please remember that before you have data in database you have to insert it. So if you have some form with First Name and Last Name, user at first must fill this form and data must be insert into database, without it you don't have any ID.
To summarize in my opinion viewModel should have properties which you have to present to the end-user.

Data Persistence across ASP.NET postbacks

Context:
I've often been in situations where our ASP.NET pages would have to show data to the user on a GridView, let him change it as he pleases (Textbox on cells) and only save it to the database when he actually hits the "Save Button". This data is usually a virtual state of the information on the page, meaning that the user can change everything without really saving it until he hits the "Save Button".
In those cases, there's always list of data that needs to be persisted across ASP.NET Postbacks. This data could be an instance of a DataTable or just some List<Someclass>.
I often see people implementing this and persisting the data on Session. On that cases i also usually see problems when it comes to some user navigating with multiple tabs open, some times on the same page. Where the data of two different tabs would get merged and cause problems of information being scrambled.
Example of how Session is often used:
private List<SomeClass> DataList
{
get
{
return Session["SomeKey"] as List<SomeClass>;
}
set
{
Session["SomeKey"] = value;
}
}
People often tries to solve it by doing something like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
DataList = null
}
else
{
FillGridView(DataList);
}
}
But what about when two tabs are already loaded and the user is changing the GridView values and for some weird reason he tries to save the data by hitting the Save button on the other page? I personally dislike this option.
Other ways to do this would be to put the data on ViewState. However, when it comes to persisting substantially big lists, it could impact the page heavily when it's stored on the page (HiddenField).
But, what's the best way to make that work? Once, i thought in using Session together with ViewState where the ViewState would hold an unique identifier which would index the Session saved data. That would prevent sharing the data between tabs on the browser:
private List<SomeClass> DataList
{
get
{
if (ViewState["SomeKey"] == null)
{
ViewState["SomeKey"] = Guid.NewGuid().ToString();
}
return Session[ViewState["SomeKey"].ToString()] as List<SomeClass>;
}
set {
if (ViewState["SomeKey"] == null)
{
ViewState["SomeKey"] = Guid.NewGuid().ToString();
}
Session[ViewState["SomeKey"].ToString()] = value;
}
}
On the other hand it would store a new list of data to the Session every time the user enters the page. Which would impact the server memory. Maybe they could be erased in some way.
Question:
What would be the best way of persisting that kind of data across Postbacks, considering the contexts of multiple tabs on the browser, with the less cost to the server and to the maintenance coding team?
Update:
As #nunespascal nicely posted, one option would be to store the ViewState in the Session using the SessionPageStatePersister. But unfortunately that's not an option on my case. And yet it is not very different from my last example, saving the data on the Session indexed by an UniqueId stored on the ViewState.
Would there be any other options?
There is a simple solution to that problem. Store the ViewState in the Session.
For that you need to use the SessionPageStatePersister
Refer: Page State Persister
All you need to do is override the PageStatePersister and make it use SessionPageStatePersister instead of the default HiddenFieldPageStatePersister
protected override PageStatePersister PageStatePersister
{
get
{
return new SessionPageStatePersister(this);
}
}
This even saves you the headache of maintaining a unique key. A hidden field will be used automatically to keep a unique key per instance of the page.
I've come across a similar situation. The idea is if you allow long sessions for each user to change the grid view, this means you'll also have a concurrency problem because eventually you will accept only one last set of modifications to your data.
So, my solution was, to allow changes on the database but make sure all the users see the same state via SignalR.
Now, the concurrency problem has disappeared but you still need to make the changes on the fly. You might not want to save the changes after all. I've solved this problem by applying the command design pattern. Now any set of changes can either be approved or discarded. Whenever you check the index you will see the last approved gridview. Go to update page and you see the live-updated gridview. Also, go to revisions to see old approved gridview -another advantages of command design pattern-.

Categories

Resources