Im building an admin interface for a medical records management app.
My client has asked me for a way to easily select the patient the user wants to work with without having to select the patient everytime he wants to perform an action.
So, say for instance he wants to store a record for the patient's current status (weight, size, etc) and then assign the same patient to a different doctor or change the company the patient currently works for.. he doesnt want to select the same patient all three times... he wants a select dropdown for patients and perform the different actions for that patient.
Im thinking this should be somehow stored in a session variable.. I have a table of patients and Im using LinqtoSql classes.... what do you reccommend?? help please.
Sounds like you want to put something into Session--perhaps some of the basic "recent patient" information, such as a patient ID, patient name, etc.
Definitely take a look at this post on how to do it in a very graceful way.
You could use the session to keep a list of recently active patient records for a given users session. Every time the user selects a new patient simply add that patient's name to the "Recent" list. Since you can control the length of the session you could just allow the list to expire when the user's session does. As far as not making the user select a customer again, just have it auto-select the most recent (last entry) on the list of recent customers.
Personally I would consider caching as an option here. By the sounds of it you want to load ALL data for ALL patients which is fine for a small amount of data but will not scale gracefully.
Consider going to the database the FIRST time you need the patient's data, and getting your data from the cache for subsequent queries...
The best way is to have the id in the route.
Use something like this when registering routes:
routes.MapRoute(
"Standard",
"{controller}/{action}/{PatientId}",
new
{
controller = "home",
action = "index",
PatientId = ""
}
);
When you select a Patient you post to an action that sets the PatientID in the RouteParameterCollection and redirect to the the action that displays the form for changing the patient. this way you always have the patientID in the URL.
Using session has some drawbacks:
If you have 2 windows open both use
the same session. This might confuse
users.
You can not bookmark a page
Session is usually stored in memory
on the appserver. This might lead to
performance problems if extensively used.
Related
I have an MVC application which has news that i can create in the database and display for my users. I want to provide users with the ability to "dismiss" a news article and never see the article again. What would be a clever and sensible way to store / provide this functionality?
Some rough guidance would be great
Thanks
Chris
Assuming you have a User table and an Article table, I'd suggest creating a many-to-many UserArticle table that contains a composite primary key of the User Id and Article Id, as well as a datetime field to record when the user has dismissed the article.
In your front-end code, when the user clicks the "dismiss" link/button, hook it up to a Jquery ajax call which calls a controller action which adds an entry to your UserArticle table with the current user Id, article Id and current datetime.
Then in your query that retrieves the list of news articles, add to the WHERE clause an additional clause that filters out articles dismissed by the current user.
I'm pretty new with Ruby on Rails. I have done tutorials and an online class. I know enough to have my way around the development environment, to create tables, controllers, views etc. However, the classes and tutorials don't really show you in depth things you may have to work with in a real software development situation.
I'm in the process of converting a piece of software to the Ruby on Rails platform. Here is the situation. I have a Salesman controller that has salesman information. Then i have a client controller that handles client information. A salesman has many clients and clients have one salesman. I already coded the REST architecture for the Salesman object. Now, i have to create the client. But before i can get to the new action in the controller, i have to select a salesman first. This select salesman page is going to be needed on many other occasions as i will have other controllers such as commissions , purchase orders etc that have to be tied to a salesman first. How do i proceed to code such a "temporary select" page before actually going to the new action in the controller. Can we define custom actions after which i would just redirect_to the new action passing the salesman id as a param?
You need to design the app similar to a multi-tenant app. In your case, Sales Person is like a tenant.
Here is the conceptual design:
add current_id class attribute to SalesMan
cattr_accessor :current_id, instance_writer: false, instance_reader: false
In all your models that belong to a sales person, define the default scope:
default_scope { where(sales_man_id: SalesMan.current_id) }
Ensure that current sales man is set before executing any actions that depend on sales man being pre-selected. There are multiple approaches for achieving this and it also depends what kind of user experience you want to provide. Couple of options:
Only show the navs such as New Client only if a salesman is pre-selected. Provide a menu option to select a salesman. In that action set SalesMan.current_id.
If the salesman is going to be different across many subsequent transactions, you may want to implement a salesman selection (step 1 of workflow) through a controller before action. For example, when user selects New Client nav option, the before action can redirect to sales man selection and set a flag. SalesMan controllers selected action should redirect to New Client action and ensure that proper flags (parameters) are set to avoid cycles. Define a new controller SalesManSelectionController and subclass all the controllers whose models depend on salesman.
I am just starting MVC and I would like to know the best practice to pass sensitive information like IDs across views ...
Let's assume that I have a scenario.
I have a car service managing MVC application which allow users to choose product for their registered car.
The user have to register their car first before they choose a product for their service.
In register view, they fill out the car detail and it redirects to purchase product page when they click the submit button. At the time when they click the submit button, we store car details with user ID (which I can get from Identity) and generate unique car ID from the database. I want to pass this newly created car ID to next view.
In purchase product page, they can choose different product A or B and when they choose, it redirects to checkout page.
What I want to achieve now is then in checkout page, how securely we can carry the car ID that user get after they have registered their car and product ID from previous product view so I can process transaction with userID, carID, and productID.
Is Session way to go with this ? Or any other better way to tackle this problem .?
Someone with small example will be great help for me.
Thanks,
In your example given I would certainly recommend storing the ID in a session. The web is a stateless beast, and what you're essentially after doing is recording state for the duration of the user's visit to the website/application - this is essentially what sessions are designed to do.
Creating, storing and retrieving data from a session is simple and can be done like so:
Setting a variable in the session object
[HttpPost]
public ActionResult Login(int carId)
{
...
Session["carId"] = carId;
...
}
Retrieving a variable from the Session object
public ActionResult Load()
{
...
int carId = Session["carId"];
...
}
Whilst this is a basic example, it gives you an idea as to how to store/retrieve simple types of data from a session.
For storing more information such as large objects you can use the [Serialize] class attribute outlined in my answer in this post.
My domain model is this: we have a bunch of schools as the root of the "hierarchy". Each school has teachers and courses, and each course has one teacher. I am trying to model this with the logic of the mvc framework and I 'm quite confused. For example, the \school\details\x should give the first page of a school. That should contain a link to a list of its teachers, and a list to each courses.
A list of teachers means that the index action should be parametric to the school the user is looking at: \teacher\id where id is the school. The same with the course list. And then create teacher or course should also be parametric to what school we are looking at:\teacher\create\x where x=school.
How do I carry around the school id? Is there some neat way to do it, or do I need to pass it around all the time, into every view that needs it? It also makes the site URLs very cryptic. I was thinking of a way to make the url structure like {school-alias}\{controller}\{action}\{id}, still I have to find a way to pass around the school. If this is accomplished, then I need to implement some kind of filter that will not allow a user to perform certain actions if the schoolId he is requesting does not match the one in his profile.
I figure that if I 'm carrying the schoolid around the URL, the site is more REST-like, compared to, for example, getting the schoolId from the user's profile.
I would create acronym for every school. For example:
School no. 1 - ABC
School no. 2 - DEF
If i wanted to list teachers, I would write
http://site-address/ABC/teachers/list or just http://site-address/ABC/teachers
To show basic information about school
http://site-address/ABC
The code for routing would be:
routes.MapRoute(
"Default", // Route name
"{acronym}/{controller}/{action}/{id}", // URL with parameters
new {controller = "School", action = "Details", id = ""} // Parameter defaults
);
I would create authorization action filter on teachers,school and classes controller to check if user has access to school defined by acronym parameter in URL. You can check it by comparing filterContext.RouteData.Values["acronym"] with data stored in profile.
Write an extension method to overload rendering of links that extracts the school identifier ( acronym or whatever you choose to use ) from the routing data and adds it to the route values already passed in. This way your action can choose to use the identifier if it is present but is not required to add it to the view data and you do not have to remember to include it in any action links ( you just have to remember to use your action link overload ).
I would make the action link overload quite obviously different so anyone following behind you can see you are doing something unusual. This could be as simple as Html.SchoolActionLink( ...).
For example:
If your url is http://mydomain.com/abc/teachers/list and your route is defined as {school}/{controller}/{action} then the route value dictionary will have the value "abc" at the key "school". The route values can be accessed via HtmlHelper.ViewContext.RouteData.Values.
In the end I 'm answering my own question.
The real solution to this is :Restfull Routing. It implements the functionality in RoR, which is exactly what I need. Too bad this is not a requirement from more people so that it can go into mvc-trunk.
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.