Read and write to ASP.NET cache from static method - asp.net

I have a static method in a helper class named helper.getdiscount(). This class is ASP.NET frontend code and used by UI pages.
Inside this method I check if some data is in the ASP.NET cache then return it, otherwise it makes a service call and store the result in the cache and then returns that value.
Will this be a problem considering multiple threads might be accessing it at the same time?
if (HttpContext.Current.Cache["GenRebateDiscountPercentage"] == null)
{
IShoppingService service = ServiceFactory.Instance.GetService<IShoppingService>();
rebateDiscountPercentage= service.GetGenRebateDiscountPercentage().Result;
if (rebateDiscountPercentage > 0)
{
HttpContext.Current.Cache.Add("GenRebateDiscountPercentage", rebateDiscountPercentage, null, DateTime.Now.AddDays(1), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
}
}
else
{
decimal.TryParse(HttpContext.Current.Cache["GenRebateDiscountPercentage"].ToString(), out rebateDiscountPercentage);
}
Please advise if this is fine or any better approach could be used.

try something like this with lock object.
static readonly object objectToBeLocked= new object();
lock( objectToBeLocked)
{
if (HttpContext.Current.Cache["GenRebateDiscountPercentage"] == null)
{
IShoppingService service = ServiceFactory.Instance.GetService<IShoppingService>();
rebateDiscountPercentage= service.GetGenRebateDiscountPercentage().Result;
if (rebateDiscountPercentage > 0)
{
HttpContext.Current.Cache.Add("GenRebateDiscountPercentage", rebateDiscountPercentage, null, DateTime.Now.AddDays(1), System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
}
}
else
{
decimal.TryParse(HttpContext.Current.Cache["GenRebateDiscountPercentage"].ToString(), out rebateDiscountPercentage);
}
}
Also you can look into following thread.
What is the best way to lock cache in asp.net?

Use these generic methods to use the cache for any type:
`public static void AddCache(string key, object Data, int minutesToLive = 1440)
{
if (Data == null)
return;
HttpContext.Current.Cache.Insert(key, Data, null, DateTime.Now.AddMinutes(minutesToLive), Cache.NoSlidingExpiration);
}
public static T GetCache<T>(string key)
{
return (T)HttpContext.Current.Cache.Get(key);
} `
Now to solve your problem:
`if(GetCache<decimal>("GenRebateDiscountPercentage") == null)
{
IShoppingService service = ServiceFactory.Instance.GetService<IShoppingService>();
rebateDiscountPercentage= service.GetGenRebateDiscountPercentage().Result;
if (rebateDiscountPercentage > 0)
{
AddCache("GetGenRebateDiscountPercentage", rebateDiscountPercentage);
}
}
else
{
rebateDiscountPercentage = GetCache<decimal>("GetGenRebateDiscountPercentage");
}
`

Related

WebFlux returning http.okay vice http.notFound

New to WebFlux, reactive, and handlers. I've got things "working", but am not understanding why following code is returning "okay" with empty body, vice "not found".
Clarification: The issue-of-concern is in the final return statement of DemoPOJOHandler.getById(). The "short-circuit" code works as expected (i.e., returns "Bad Request" status), but the "switchIfEmpty" path of the final return statement does not appear to get exercised if a DemoPOJORepo.getById(int) returns Mono.empty().
(Note: I've hacked up a list-based "repo" to avoid dealing with database while figuring out handlers and http return types.)
Router implementation ("/v1" is a set of annotation based RESTful endpoints)...
#Configuration
public class DemoPOJORouter {
#Bean
public RouterFunction<ServerResponse> route(DemoPOJOHandler requestHandler) {
return nest(path("/v2"),
nest(accept(APPLICATION_JSON),
RouterFunctions.route(RequestPredicates.GET("/DemoPOJO"), requestHandler::getAll)
.andRoute(RequestPredicates.GET("/DemoPOJO/{id}"), requestHandler::getById)
.andRoute(RequestPredicates.POST("/DemoPOJO"), requestHandler::add)));
}
}
Handler implementation has been "stripped down" to only the code in question. I have a feeling that much of the style is "still imperative", but I've attempted to put the reactive stuff where it "makes the most sense".
If I supply a bad value on the URI (i.e., "foo"), then I get the http "bad request" returned. But, never seem to get the "not found" that should be generated by "switchIfEmpty" if a validly formatted int value is supplied, but it does not map to an entry in the repo.
#Component
public class DemoPOJOHandler {
public static final String PATH_VAR_ID = "id";
private DemoPOJORepo repo = null;
public Mono<ServerResponse> getById(ServerRequest request) {
Mono<DemoPOJO> monoDemoPOJO = null;
Map<String, String> pathVariables = request.pathVariables();
int id = -1;
checkRepoRef(); // part of the list hack
// short-circuit if request doesn't contain id (should never happen)
if ((pathVariables == null)
|| (!pathVariables.containsKey(PATH_VAR_ID))) {
return ServerResponse.badRequest().build();
}
// short-circuit if bad id value
try {
id = Integer.parseInt(pathVariables.get(PATH_VAR_ID));
} catch(NumberFormatException e) {
return ServerResponse.badRequest().build();
}
// get entity by keyValue
monoDemoPOJO = repo.getById(id);
return monoDemoPOJO
.flatMap(demoPOJO -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.syncBody(demoPOJO)
.switchIfEmpty(ServerResponse.notFound().build()));
}
}
Hack of a list-based repo to avoid dealing with data/APIs while working on handlers and http return types.
// local hack to avoid a database for testing
public class DemoPOJORepo {
private static DemoPOJORepo fpRepo = null;
private static int NUM_ROWS = 100;
private Map<Integer, DemoPOJO> fooPOJOMap;
private DemoPOJORepo() {
initMap();
}
public static DemoPOJORepo getInstance() {
if (fpRepo == null) {
fpRepo = new DemoPOJORepo();
}
return fpRepo;
}
public Mono<DemoPOJO> getById(int id) {
Mono<DemoPOJO> monoDP;
if (fooPOJOMap.containsKey(id)) {
monoDP = Mono.just(fooPOJOMap.get(id));
} else {
monoDP = Mono.empty();
}
return monoDP;
}
private Mono<Void> initMap() {
fooPOJOMap = new TreeMap<Integer, DemoPOJO>();
int offset = -1;
for(int ndx=0; ndx<NUM_ROWS; ndx++) {
offset = ndx + 1;
fooPOJOMap.put(offset, new DemoPOJO(offset, "foo_" + offset, offset+100));
}
return Mono.empty();
}
}
Your brackets are in the wrong place causing the swithIfEmpy to apply to the ServerResponse.ok() publisher not the monoDemoPOJO, replace the return with this and it should work:
return monoDemoPOJO
.flatMap(demoPOJO -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).syncBody(demoPOJO))
.switchIfEmpty(ServerResponse.notFound().build());
As I can see the code is right. The response code is Bad request because you are trying to convert "foo" to Integer, and when it throws an exception you are returning a Bad request response, so I think it works perfectly fine.
If you use an Integer id that is not present in your database then the answer must be a not found response

ASP.NET Entity Framework DataContext use issue

I 've built an ASP.NET website using EF. I created a DataContext class which implements the singleton pattern. My DAO classes (singletons too) instanciate this datacontext and store it in a property. They use it in order to query the SQLServer DataBase. This worked ok for 3 months but I suddenly got exception messages like :"Connection must be valid and open / connection already open". It seemed that datacontext was not disposed. The only change, according to me, was the data size and number of users increasing.
I then found multiple posts saying that singleton was a bad idea foe datacontext, so I tried to instanciate datacontext in a using statement in every request and that resolved the problem, except for update queries which had no effects in database. I had to attach the db object to the context and then set its EntityState to "modified" to have my SaveChanges work.
Like this :
public bool DoucheXpsu(as_headers session) {
using (MyDBEntities MyContext = new MyDBEntities()) {
try {
as_status status = GetStatus(session);
if (status != null) {
if (status.mainstatusvalue == 300) {
status.DateDoucheXpsu = DateTime.Now;
status.DoucheXpsu = 1;
MyContext.as_status.Attach(status);
MyContext.ObjectStateManager.ChangeObjectState(status, EntityState.Modified);
MyContext.SaveChanges();
return true;
} else {
return false;
}
} else {
return false;
}
} catch (OptimisticConcurrencyException) {
return false;
} catch (Exception) {
return false;
}
}
}
The problem is that it actually didn't work for ONE method (which has nothing different from the other update method) !
The exception occured as I tried to attach the object : "The object cannot be attached because it is already in the object context. An object can only be reattached when it is in an unchanged state. " So I had to comment the attach and ChangeObjectState methods to have it work as expected :
public bool SetSessionToDelete(string numSession) {
using (MyDBEntities MyContext = new MyDBEntities()) {
try {
view_headerStatus view = (from v in MyContext.view_headerStatus
where v.CodeSession == numSession
where v.lastinserted == 1
select v).First();
if (view != null) {
as_status status = (from s in MyContext.as_status
where s.jobclsid == view.jobclsid
where s.lastinserted == 1
select s).First();
if (status != null) {
status.DeleteSession = 1;
//MyContext.as_status.Attach(status);
//MyContext.ObjectStateManager.ChangeObjectState(status, EntityState.Modified);
MyContext.SaveChanges();
return true;
} else {
return false;
}
} else {
return false;
}
} catch (OptimisticConcurrencyException) {
return false;
} catch (Exception) {
return false;
}
}
}
The question is WHY should this one behave differently ???
I've read many posts about EF and dataContext but I feel I'm missing something. I would be glad if anyone can help.
Thanks.
In your first example, this line here:
as_status status = GetStatus(session);
I would assume this populates using a DIFFERENT context, and when it leaves the GetStatus() method the context it used to load is disposed. That is why your subsequent Attach() works. However in your second example you do not need to attach because it was loaded using the current (connected) context.
To solve you may want to either pass the context to your methods like GetStatus() resulting in no need to reattach. I don't typically reattach unless I am resurrecting an object over the wire or from a file.

How to persist search criteria and results of a c# .NET application page

Scenario:
User submits search criteria and selects an item from search results on the same page, which navigates to a new page of details for the selected item.
When the User returns to the search screen, the search criteria & results (including selected page and sort-order) should be preserved from their last visit.
Related information:
All form submissions are POSTs.
Navigation back to the search screen may not be available from last browser history (e.g. more than one details screen may be encountered, or the user may navigate directly to the search screen from an alternative menu.)
Search results are provided using Telerik RadGrid control.
I'm looking for a generic solution that will be able to be applied to different search screens.
In some instances, the item may be DELETED from within the details screen, and should therefore not appear in the search results when the screen is next encountered.
Thoughts:
I've read a lot of suggested methods for addressing various parts of this scenario, but I'm still confused; no comprehensively "correct" solution jumps to the forefront.
I guess I'm asking for recommendations/approach rather than a whole solution spelled out for me (although that would be nice! ;-)
The .NET VIEWSTATE would seem to do exactly what I'm after (with the exception of #5) - Is there some way of leveraging off this so that viewstate can be used between pages, and not just between postbacks to the same page? (e.g. can I store/restore viewstate to/from a session variable or something? I haven't seen this suggested anywhere and I'm wondering if there's a reason why.)
Thanks in advance.
Thanks for all the advice.
For the benefit of others, here is a solution to this issue (no doubt there's room for improvement, but this works satisfactorily for the moment).
4 functions...
StoreSearchCookie - Persist the state/values of a panel of search criteria and a results grid in a cookie with a specified UID.
RestoreSearchCookie_Criteria - Read the cookie and re-populate the search criteira
RestoreSearchCookie_Results - Read the cookie and restore the grid state.
FindFormControls - Helper method to recursively find form-input controls in a container (pinched & modified from elsewhere on Stack Overflow)
NB...
I haven't addressed the multiple-tabs issue because our application doesn't allow them anyway.
RestoreSearchResults utilises GridSettingsPersister.cs available from Telerik website, (but I had to modify it to store the page number as well)
Usage is as follows...
protected void Page_PreRender(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
// Store the state of the page
StoreSearchCookie("SomeSearchPage", pnlSearchCriteria, gridResults);
}
else
{
// Restore search criteria
RestoreSearchCookie_Criteria("SomeSearchPage");
// Re-invoke the search here
DoSearch(); // (for example)
// Restore the grid state
RestoreSearchCookie_Results("SomeSearchPage");
}
}
Code follows...
protected void StoreSearchCookie(string cookieName, Panel SearchPanel, RadGrid ResultsGrid)
{
try
{
HttpCookie cookieCriteria = new HttpCookie("StoredSearchCriteria_" + cookieName);
// 1. Store the search criteria
//
List<Control> controls = new List<Control>();
FindFormControls(controls, SearchPanel);
foreach (Control control in controls)
{
string id = control.ID;
string parentId = control.Parent.ID;
string uid = string.Format("{0}>{1}", parentId, id);
string value = "";
Type type = control.GetType();
bool isValidType = true; // Optimistic!
if (type == typeof(TextBox))
{
value = ((TextBox)control).Text;
}
else if (type == typeof(DropDownList))
{
value = ((DropDownList)control).SelectedValue;
}
else if (type == typeof(HiddenField))
{
value = ((HiddenField)control).Value;
}
else if (type == typeof(RadioButton))
{
value = ((RadioButton)control).Checked.ToString();
}
else if (type == typeof(CheckBox))
{
value = ((CheckBox)control).Checked.ToString();
}
else
{
isValidType = false;
}
if (isValidType)
{
cookieCriteria.Values[uid] = value;
}
}
cookieCriteria.Expires = DateTime.Now.AddDays(1d);
Response.Cookies.Add(cookieCriteria);
// 2. Persist the grid settings
//
GridSettingsPersister SavePersister = new GridSettingsPersister(ResultsGrid);
HttpCookie cookieResults = new HttpCookie("StoredSearchResults_" + cookieName);
cookieResults.Values["GridId"] = ResultsGrid.ID;
cookieResults.Values["GridSettings"] = SavePersister.SaveSettings();
cookieResults.Expires = DateTime.Now.AddDays(1d);
Response.Cookies.Add(cookieResults);
}
catch (Exception exception)
{
Logger.Write(exception);
}
}
protected void RestoreSearchCookie_Criteria(string cookieName)
{
try
{
HttpCookie cookieCriteria = Request.Cookies["StoredSearchCriteria_" + cookieName];
if (cookieCriteria != null)
{
foreach (string key in cookieCriteria.Values.AllKeys)
{
string value = cookieCriteria[key];
string[] ids = key.Split('>');
string parentId = ids[0];
string id = ids[1];
Control control = FindControl(parentId).FindControl(id);
Type type = control.GetType();
if (type == typeof(TextBox))
{
((TextBox)control).Text = value;
}
else if (type == typeof(DropDownList))
{
((DropDownList)control).SelectByValue(value);
}
else if (type == typeof(HiddenField))
{
((HiddenField)control).Value = value;
}
else if (type == typeof(RadioButton))
{
((RadioButton)control).Checked = Boolean.Parse(value);
}
else if (type == typeof(CheckBox))
{
((CheckBox)control).Checked = Boolean.Parse(value);
}
}
}
}
catch (Exception exception)
{
Logger.Write(exception);
}
}
protected void RestoreSearchCookie_Results(string cookieName)
{
try
{
HttpCookie cookieResults = Request.Cookies["StoredSearchResults_" + cookieName];
if (cookieResults != null)
{
string gridId = cookieResults.Values["GridId"];
string settings = cookieResults.Values["GridSettings"];
RadGrid grid = (RadGrid)FindControl(gridId);
GridSettingsPersister LoadPersister = new GridSettingsPersister(grid);
LoadPersister.LoadSettings(settings);
grid.Rebind();
}
}
catch (Exception exception)
{
Logger.Write(exception);
}
}
private void FindFormControls(List<Control> foundSofar, Control parent)
{
List<Type> types = new List<Type> { typeof(TextBox), typeof(DropDownList), typeof(RadioButton), typeof(CheckBox), typeof(HiddenField) };
foreach (Control control in parent.Controls)
{
if (types.Any(item => item == control.GetType()))
{
foundSofar.Add(control);
}
if (control.Controls.Count > 0)
{
this.FindFormControls(foundSofar, control); // Use recursion to find all descendants.
}
}
}

Is this common practice for a read-only property that accesses the database

If I have a read-only property on an object that fills itself via the DB, is this what I should be doing, or is there a better way to make sure it's already been evaluated?
private List<Variable> _selectedVariables;
public new List<Variable> SelectedVariables
{
get
{
if (_selectedVariables == null)
{
_selectedVariables = SomeFunctionThatCallsDB();
}
return _selectedVariables;
}
}
That's fine for a single thread; but you will have problems if that is going to be in a situation where you have multithreaded gets.
EDIT: Threadsafing:
Simple Threadsafe pattern:
private readonly object _objectLock = new object();
private List<T> _someList = null;
public List<T> MyStuff
{
get
{
if(_someList == null)
{
lock(_objectLock)
{
if(_someList == null)
_someList = LoadFromDB();
}
}
return _someList;
}
}
You check to see if set, then lock, then check again to make sure you covered the race condition.

Multiple web sits application visitor counter

I have a multiple web sits asp.net application.
In this application different domains using the same pages.
All pages inherit from base class named: PageBase
wich inherit from System.Web.UI.Page.
By using: HttpContext.Current.Request.ServerVariables["HTTP_HOST"]
i cen determine what is the domain and then get all the info i need
for this domain and everything is working good.
My problem begin when i want to use different visitor counter for each site based on session.
Because Global.asax have Global events:
Session_Start
Session_End
simple counter will count all visitors on all sites together.
I try to make code behind for the Global.asax in different class
but i cold not get in that class the PageBase(System.Web.UI.Page) web site info.
I will be very thankful for any ideas to solve this problem
cheinan
I am assuming that you are able to browse from one "site" to the other within the same session and that there is only one session created.
In this case, you need to add the following to your Session:
Session["CountedHosts"] = new List<string>();
Then, on your base page, add the following:
var host = Request.ServerVariables["HTTP_HOST"];
var countedHosts = Session["CountedHosts"] as List<string>;
if (countedHosts != null && !countedHosts.Contains(host))
{
countedHosts.Add(host);
}
Then on session end, record each host that was visited.
var countedHosts = Session["CountedHosts"] as List<string>;
if (countedHosts != null)
{
foreach (var host in countedHosts)
{
//Log it
}
}
I am not able to browse from one "site" to the other within the same session each site have is on
different session created.
but i am very thankful to you because you gave me en idea
how to solv this problem
here is what i did:
i created counter class with Dictionary "onlineList" were i automatic creat for each site a key:
public abstract class counter{
public static Dictionary<string, int> onlineList = new Dictionary<string, int>();
//do add one count
public static void doSiteCountOn(string siteID)
{
if (onlineList.ContainsKey(siteID))
{
onlineList[siteID] += 1;
}
else
{
onlineList.Add(siteID, 1);
}
}
//do less one count
public static void doSiteCountOff(string siteID)
{
if (onlineList.ContainsKey(siteID))
{
onlineList[siteID] -= 1;
}
else
{
onlineList.Add(siteID, 0);
}
}
//get the count
public static string onlineCount(string siteID)
{
if (onlineList.ContainsKey(siteID))
{
return onlineList[siteID].ToString();
}
else
{
return "0";
}
}
//reset the count if needed
public static void resetCount(string siteID)
{
if (onlineList.ContainsKey(siteID))
{
onlineList[siteID] = 0;
}
}}
on my base page i check if there is Session["siteID"]
and if not i start one and make my counter class to add 1 to the site counter:
if (Session["siteID"] == null){
Session["siteID"] = _siteID;
counter.doSiteCountOn(_siteID);}
and finaly on my Session_End i do one count less:
void Session_End(object sender, EventArgs e){
if (Session["siteID"] != null)
{
counter.doSiteCountOff(Session["siteID"].ToString());
}}
thank for your halp
and sorry for my late respons
cheinan

Resources