May sound like a dumb question but here goes.
I instantiate a LIST from my homepage, the list is in a global class file, and returns all the information about the person logging in. the person, could have one or more accounts associated with the site, and therefore i need to code against a default flag to display their default account informaiton. However, i then also need to build their other account information and display this for them.
The additional account(s) are listed in a drop down box. when the drop down box fires off, instead of calling out to the class again, and retrieving all the necessary information, as i've already done this once, how can i store the object, so that it can be used?
I've looked at Session Variables, but this gets a bit messy (I have 35 fields being returned in my list), plus, the Session variables only get set the first time around, not on DDL changed.
therefore, I need a way of having quick access to the object. - what's the best approach?
As per me , Session is the best possible object for your type of requirement and on DDL changed event try to rebind the Session object with new modified values
Related
Hello fellow developers.
First of all I apologize beforehand for the wall of text that follows, but after a day going crazy on this, I need to call for help.
I've stumbled across a problem I cannot seem to solve. I'll try to describe the scenario in the best possible way.
Task at hand: in an existing Asp.Net Mvc application, create a lookup table for an integer field, and use the textual value from the lookup in the editing view. When saving, we must first check if the lookup already has a corresponding text value for the same Root ID. If there is, use that. Otherwise, create it and then use it.
The structure:
The data model is a graph of objects where we have the root object, a collection of level A child objects, and every level A child object has a collection of level B child objects, so something like this:
Root (with fields)
Level A child (with fields) x n
Level B child (with fields) x n
The field we have to handle is on the LevelB objects.
There is a single Mvc view that handles the whole data. For collection objects, all fields are named like levelA1levelB1MyField, levelA1levelB2MyField, etc so every single field has unique name during the post. When the post happens, all values are read through a formCollection parameter which has average 120/130 keys. The keys are isolated by splitting them and looping on the numerical part of the names, values are read and parsed to the expected types and assigned to the object graph.
The datalayer part backing the object graph is all stored procedures, and all the mapping (both object to sproc and sproc to object) is hand written. There's a single stored procedure for the read part, which gets multiple datasets, and the method calling it reads the datasets and creates the object graph.
For the saving, there are multiple sprocs, mainly a "CreateRoot" and "UpdateRoot". When the code has to perform such tasks, the following happens:
For create scenario, "CreateRoot" is called, then the sprocs "CreateLevelA" and "CreateLevelB" are called in loop for each element in the graph;
For update scenario, "UpdateRoot" is called, which internally deletes all "LevelA" and "LevelB" items, then the code recreates them calling the aforementioned sprocs in loop.
Last useful piece of information is that the "business objects graph" is used directly as a viewmodel in the view, instead of being mapped to a plain "html friendly" viewmodel. This is maybe what is causing me the most trouble.
So now the textbox on the view handles an "integer" field. That field must now accept a string. The field on LevelB must remain an integer, only with a lookup table (with FK of course) and the text field from the lookup must be used.
The approaches I tried with no success:
My first thought was to change the datatype on the property MyField from integer to string on the object, then change the sprocs accordingly and handle the join at sproc level: I'd have a consistent object for my view, and the read/write sprocs could translate from string to integer and viceversa, but I can't do that because the join keys to retrieve the integer when writing are part of the Root item (as I stated in the first lines of this wall of text), which I don't know in the CreateLevelB sproc, and changing the whole chain of calls to pass those parameters would have a huge impact on the rest of the application, so no good.
My next try was to keep things "as they are" and call some "translation methods": when reading, pass the integer to the view, and there call the translation method to display the text value. When saving, use the posted text to retrieve the integer. The save part would work, I'd have all the parameters I need, but for the read part, I'd have to instantiate the "data access layer" and call its method at View level, and there's no need to explain why that is a very bad choice, so I ruled this out too.
Now I'm out of options (or ideas anyway). Any suggestion to solve this is very welcome, and also if something is not clear enough just point it out and I will edit my post with more accurate information.
Thanks.
This is not a real answer but you could rip out all sprocs and use the updating facilities of an OR mapper. This will resolve all the layering issues. You just update data how you see fit and submit at the end.
I guess this would also make the questions around "should I use an int or a string" go away.
Edit: After reading your comment I thought of the following: Do not implement alternative 1. You rather want to sacrifice code quality in the view than in the data storage model. The last one is more important and more centrally used.
I would not be too concerned with messing up the view by calling the DAL from it or the like. Changes in a view are localized and do not mess up the application's architecture. They just degrade the view.
Maybe you could create a view model in your controller and do the translations between DAL-model and view model? Or is that pattern not allowed?
I have found many questions here about storing values in viewstate, but haven't found a good answer.
I have a situation when i retrieve large amount of data from database. Then i filter and manipulate the data according to my needs (so it is a preety heavy process). Then I put the result inside a list of custom class. For example lets say this class will be Person
List<Person> persons = new List<Person>();
private void FillPersons()
{
//Call to webservice
persons = ws.GetPersonsList();
//Do all kind of custom filtering
//Manipulate the data
}
Now the whole FillPersons() method is a heavy process that returns pretty small amount of data. And unfortunately it can't be moved to SQL and the heaviness is in the process, but that is not the point.
The point is that i need to reuse this data on the page between post backs.
Right now in order to spare the additional call to FillPersons() I mark Person class as serializeable and store the list in the viewstate, that works fine except the fact that the page becomes 1mb size because of the viewstate. According to what i have read, it is not so acceptable approach i.e. it is not secure and it blows the source code making the page heavy etc. (second is what most concerns me)
So it leaves me with a session. However session is persisted not only between postbacks, but much after it, even when user leaves the page. Or worst- the session will end before user decide to postback. So finding the best time span for session lifetime is mission impossible.
My question is what is the best practice to reuse "datasets" between postbacks?
What you guys do in such cases?
Thanks.
PS: hidden fields etc. is not an option.
You can store this kind of data in the Cache. It is application wide, so depending on what you add use the key accordingly.
var key = UserID + "_personList";
Cache.Add(key, personList, null,
DateTime.Now.AddSeconds(60),
Cache.NoSlidingExpiration,
CacheItemPriority.High,
null);
Note that you can never assume that the data is in the cache (it might have been flushed) so always check if it returns null and than refill it.
Viewstate is not a good way of storing large objects. As you mentioned your page size will get bigger and every postback will take lots of time.
I would suggest using cache. By using cache your list wont be saved there till end of session and you can set how much time it should be stored there. For caching you may use HttpCache or some distibuted caching system like AppFabric or MemCached . This nuget package will help using these cache systems.
this link will help how to configure AppFabric.
I should edit with some code to make it more helpful.
https://bitbucket.org/glav/cacheadapter/wiki/Home
var cacheProvider = AppServices.Cache; // will pick cachadapter using web.config ( can be Http, Memory, AppFabric or MemCached)
var data1 = cacheProvider.Get<SomeData>("cache-key", DateTime.Now.AddSeconds(3), () =>
{
// This is the anonymous function which gets called if the data is not in the cache.
// This method is executed and whatever is returned, is added to the cache with the
// passed in expiry time.
Console.WriteLine("... => Adding data to the cache... 1st call");
var someData = new SomeData() { SomeText = "cache example1", SomeNumber = 1 };
return someData;
});
Other than a cache (good idea by Magnus), the only other way I can think of is to keep the results of your heavy operation stored in the database server.
You mention that it takes a lot of time to retrieve the data. Once done, store it in a purposely established table with some type of access key. Give that key to the browser and use it for pulling what pieces you need back out.
Of course, without knowing the full architecture it's really hard to give a solution. So, in order of preference:
Store it back in the database with a unique key for this user.
Store it in a remote cache
Store it in a local cache
Under no circumstance would I store it in the page (viewstate), cookie (sounds too big anyway), or in session.
Have you considered using ASP.NET caching?
You should choose a key that will suite your exact needs and you will have your data stored in the server memory. But keep in mind cache is application specific and is valid for all users.
If the data you process is not often changed, the processing algorithm doesn't depend on user specific settings and it is not critical to always have the latest data maybe this is the best option I can think of.
Store your filtered collection on disk in a file. Give the file the same name as a key you can store in viewstate. Use that key to retrieve the file on postbacks. In order to keep the file system from filling up, have two folders. Alternate the days for which folder you save the files to. That way you can wipe out the contents of the folder that is not being used that day. This method has extremely good performance, and can scale with a web farm if your folder locations are identified by a network path.
I think personlist is a shared object. Does everyone use the same list? You can store on Application.
Application["PersonList"] =persons;
persons = (List<"Person">)Application["PersonList"]
Or you can Store on Static class.
public static class PersonList { public static List<"Person"> Get {get;set;} }
You should write this code to Application_Start on Global.asax file
PersonList.Get = ws.GetPersonsList();
And you can get List by using this code
persons = PersonList.Get;
I'm building a DAL for a widget-based reporting application, its been designed in such a way that users pick, configure and deploy reporting 'widgets' to their home screens. Widgets can report across various kinds of company data - sites, brands, employees and so on.
Whilst all users can access all the widgets/reports, all users are not authorised to access all data. If I work for Company-A I can't view sales reports for Company-B or staff attendance data for a salesman at Company-C, however I can configure such a report and add it to my 'dashboard'.
At runtime, an intermediate 'DataService' class has the job of checking the user's credentials and, if access is permitted, returning the appropriate object collection to the client.
On the initial build I just returned an empty List if access to the data was not allowed, but this is also what I do if no data is returned by the report (which can happen). I'd like to show an 'Access Denied' message on the front end if the user isn't authorised to view the data but obviously if all I get back in either eventuality is an empty collection its impossible to know if this was because of insufficients rights or just no data.
I'd be grateful if you could suggest a way of coding around this, my first thought was to move the credential-checking into another object which in turn calls the data access class but time constraints mean this isn't an option.
The only thing I can think of, which goes against everything I've ever learnt, is to throw a custom exception e.g. InsufficientApplicationPrivilegeException if access isn't granted, but this smells bad.
Thanks for reading.
I think you have a couple of options. One is to make a composite object that your data service class returns. The composite object looks something like this: -
class DataResult<T>
{
IEnumerable<T> Data;
Result ServiceResult;
}
ServiceResult contains metadata about the outcome of your service call - it could be an enum which contains e.g. Success, AuthenticationFailure etc. etc.. You can then switch on this in order to do different behaviour.
An alternative option might be to use the NullObject pattern that shows a single item of data in the view which instead of real data simply shows "Access Denied" for the display properties of the object. The advantage of this approach is that your front-end doesn't need to have any conditional logic etc.; however if you want to show a specific message box or similar rather than just displaying a dummy row of data in your widget, then this probably isn't appropriate.
I'm curious if there is a way to dynamically change the session ID from the context of a SessionStateStoreProvider (or some other customizable module) in C# for ASP.NET.
I am implementing a custom SessionStateStoreProvider, and I was thinking about augmenting the session ID to tell the store provider where to go look for a session. I've implemented a custom SessionIDManager, which lets me augment newly created session IDs with the required tag. The problem is that the desired value of that tag might change during the life of a session. For example, the session may be read from one location, but may need to written to a different location. In this instance the ID would have originally been tagged for location A, but on writing the store would want to write to location B. The tag should be updated to reflect location B for the next session read.
So, from the context of SessionStateProviderBase override...
public override void SetAndReleaseItemExclusive(HttpContext context,
string id, SessionStateStoreData item, object lockId, bool newItem)
... is it possible to change the session ID? I know that the HttpContext.Session.SessionID property is not settable. Is there some other way to feedback an update like this? Or is there a more appropriate to carry state like this across calls?
I'm new to C# and web development in general, so any suggestions would be appreciated.
After asking this question, I became reasonably certain that it is not possible to change the Session ID dynamically.
Instead of tagging the session ID with the location information, I ended up setting a cookie using the HttpContext passed into the SessionStateStoreProvider interfaces:
context.Response.SetCookie(new HttpCookie(...));
I'm writing a workflow that needs to perform certain actions depending on which fields are changed when someone edit's an item. For example, if a user goes in and removes a role (job) from an item (staff member) then I need the workflow to realise that the role field was changed, deduce which role was removed (or potentially added) and then notify the manager of that role and do any other necessary tasks. Another example would be if the address fields in an item get changed then the appropiate HR department need to be notified of the change.
To do this I'm going to try a code block when the workflow is started that compares the top two history entries and any fields that differ will be flagged as changed and I'll take the appropriate actions dependent on each field.
Could anyone please tell me what the other options are for getting this functionality as I'd like to know if there's a better way. Thanks
Using SPD workflows it would not be that hard, depending on number of roles.
Create a column and then go into the content type and hide it. Create a SPD workflow that executes on new or change. Compare the hidden column and the one the user entered, if changed compare values against a role name and do what needs to be done. When that is done copy the user entered column into the hidden column.
Ugly and long but if you don't have the abaility to get workflow code implemented on the server, thanks corporate IT, then it is an option.
I would enable versioning on the list and then use:
SPListItem currentItem = workflowProperties.Item;
SPListItemVersion previousItemVersion = currentItem.Versions[1];
//Compare the fields in currentItem and previousItemVersion
But if i understand your question correctly, that’s what you’re about to do already.