Remove Output Cache for User Controls in ASP.NET Webforms - c#

I have an asp.net webforms SaaS application where multiple ecommerce websites are running. Each website has its own domain (abc.com, xyz.com etc.) and each website's content is fetched from the database based on the domain.
Now, in order to improve home page performance I am implementing Output Cache. Please note that the home page already contains multiple user controls (header, footer, top menu, user menu, mini cart, banners, home products etc.). All the user controls are eligible for Output Cache accept user menu (where logged in usernames are displayed, otherwise signup/login links) and mini cart (where no. of cart items are displayed and on click it shows the list of items in cart).
I added Output cache directive on each user control (that I want to be cached) with VaryByCustom to create separate cache for each domain.
<%# OutputCache Duration="300" VaryByParam="*" VaryByCustom="Host" %>
As VaryByHeader is not an available option for UserControls, I added an override function in Global.asax to return current host.
public override string GetVaryByCustomString(HttpContext context, string arg)
{
if (arg == "Host")
{
return context.Request.Url.Host;
}
return String.Empty;
}
Till now, everything is working perfect. User controls are being cached for different domains (hosts) and are being expired on the specified time.
THE PROBLEM: I want to give an option in the admin panel to the website admin users to manually refresh cache of their websites. For that I created a page (refreshcache.aspx) in the frontend application, and simply open that url (for example: abc.com/refreshcache.aspx) when the admin users click the refresh cache button from the admin panel.
I researched a lot and tried multiple approaches to clear user controls cache but failed. The last thing that I implemented is the following code which I added in the home page aspx which creates an object of StaticPartialCachingControl and adds key dependency on user controls cache.
In Home.aspx, I added the following code which is called in Page_Load
protected void LoadControlsCache()
{
CacheKey = "Host-" + Request.Url.Host;
CacheKeyArray[0] = CacheKey;
if (Cache[CacheKey] == null)
{
AddControlCache(header1);
AddControlCache(footer1);
AddControlCache(banner1);
AddControlCache(products1);
}
}
protected void AddControlCache(UserControl uc)
{
StaticPartialCachingControl pcc = (StaticPartialCachingControl)uc.Parent;
pcc.Dependency = new CacheDependency(null, CacheKeyArray);
Cache.Insert(CacheKey, "value", null, DateTime.Now.AddSeconds(300), Cache.NoSlidingExpiration);
}
And to remove the cache for a particular host, I used Cache.Remove method with the host specific key.
In refreshcache.aspx I added the following code
protected void Page_Load(object sender, EventArgs e)
{
Cache.Remove("Host-" + Request.Url.Host);
Response.Redirect("/");
}
I am not sure what I am missing or doing wrong. Just want a way to clear usercontrols cache for a particular host (domain).

Finally got the issue resolved by creating separate keys for all user controls, and adding dependency on user control object.
protected void LoadControlsCache()
{
string CacheKey = Request.Url.Host;
AddControlCache(header1, "header-" + CacheKey);
AddControlCache(footer1, "footer-" + CacheKey);
AddControlCache(banner1, "banner-" + CacheKey);
AddControlCache(products1, "products-" + CacheKey);
}
protected void AddControlCache(UserControl uc, string CacheKey)
{
if (Cache[CacheKey] == null && uc != null)
{
uc.Cache.Insert(CacheKey, 1);
uc.CachePolicy.Dependency = new System.Web.Caching.CacheDependency(null, new string[] { CacheKey });
}
}
Then to clear the cache, used Cache.Remove() with all the usercontrol keys.
protected void Page_Load(object sender, EventArgs e)
{
string CacheKey = Request.Url.Host;
Cache.Remove("header-" + CacheKey);
Cache.Remove("footer-" + CacheKey);
Cache.Remove("banner-" + CacheKey);
Cache.Remove("products-" + CacheKey);
Response.Redirect("/");
}
Hope it might help someone with a similar question!

Related

Xamarin cache pages - make it so input doesn't disappear

I'm building my first full-scale Xamarin.Forms app and trying to figure out how to keep user input between navigation. After doing some searching online I've read that the default behavior is to completely reload pages each time you navigate, but you can change the default behavior by setting the NavigationCacheMode to true or required, but I've tried to set this attribute in both xaml and C# with no success - it seems like the property is not recognized.
Is there a simple way to make it so that user input does not disappear when navigating between pages? If anyone can show me how to set the NavigationCacheMode that would be great, but I'm also open to any reasonable solution that will keep the user input from disappearing during navigation.
Additional details: My app has a UWP and Android project. I am using a master detail page for navigation. Here is my MenuList_ItemSelected event handler:
private void MenuList_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = (MenuItem)e.SelectedItem;
var title = item.Title;
var page = (Page)Activator.CreateInstance(item.TargetType);
Detail = new NavigationPage(page); //TODO: when menu item is clicked and you're already on that page, the menu should just slide back. (currently it does nothing and stays out).
IsPresented = false;
}
Finally was able to solve this! I adapted this code from a related post which implements a Dictionary that keeps track of the navigation stack:
In the constructor for my Master Detail Page:
public partial class MenuPage : MasterDetailPage
{
Dictionary<Type, Page> menuCache = new Dictionary<Type, Page>();
}
Then in the ItemSelected method:
private void MenuList_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (menuCache.Count == 0)
menuCache.Add(typeof(AttendancePage), Detail);
var item = (MenuItem)e.SelectedItem;
if (item != null)
{
if (menuCache.ContainsKey(item.TargetType))
{ Detail = menuCache[item.TargetType]; }
else
{
Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType));
menuCache.Add(item.TargetType, Detail);
}
menuList.SelectedItem = null; //solves issue with nav drawer not hiding when same item is selected twice
IsPresented = false;
}
}

Best way to pass values from popup page to parent page

I have a main page for customer in which i need to select location for the customer . to pick a location we have desined a popup page which has a grid which displays all locations . once user pics the loction that particular location should be returned back to the main page .
Location object contains feilds - LocId,LocName,LocState,LocCountry,PinCode .
The entire location object should be returned to the main page not a single value .
My code for opening location is
<asp:ImageButton ID="ImageButton2" runat="server" ImageUrl="../Content/Images/search.png" Height="21px" ToolTip="Search Location" Width="21px"
OnClientClick="ShowLocation();" />
function ShowLocation() {
window.showModalDialog('../StandardLookups/Location.aspx', '_blank', 'dialogWidth:820px; dialogHeight:400px; dialogLeft:250px; dialogTop:250px;status:no; scroll:no; help:no; resizable:no,edge: Raised,doPostBackAfterCloseCallback: false,postBackElementId: null');
}
Code in popup window once the row is selected by the user
protected void btnSelect_Click(object sender, EventArgs e)
{
List<object> locationValues = gvLocationLookup.GetSelectedFieldValues(new string[] { "LocId", "LocName", "LocState","LocCountry","PinCode" });
var locationValue = (object[])locationValues[0];
var location= new Location
{
LocId = (int?)locationValue[0],
LocName = (string)locationValue[1],
LocState = (string)locationValue[2]
LocCountry = (string)locationValue[3]
PinCode = (string)locationValue[4]
};
Session["SELECTED_LOCATION"] = location;
Response.Write("<script> window.opener.location.reload(false); window.close();</" + "script>");
Response.End();
}
Currently i use sessions values to move values . Is there any better approaches ?
You can check here what method suits to your needs.
http://msdn.microsoft.com/en-us/library/6c3yckfw%28v=vs.100%29.aspx
I've created a system like this before, using only client side code (javascript, no C#).
The requirement was that, when filling in a form, a specific value (e.g. a location) could be selected in a popup.
Once the value was clicked in the popup, the parent page receives this information through javascript, and the popup closes itself.
Note that the parent page must have the needed fields to be filled. In my case it is an autocomplete textbox (=FieldName) and a linked HiddenField (=FieldName_key), which holds the unique key to the text in the textbox.
function confirmSelection(code, key)
{
try {
window.opener.document.getElementById('<%= FieldName %>').value = code;
window.opener.document.getElementById('<%= FieldName %>_key').value = key;
window.close();
}
catch (e) {window.close();}
}
An additional benefit of this client-side script (opposed to the C# script) is that you don't have to reload the parent page, which can disturb your user in his/her work.

How to use an ajax modal popup and consume a value from a link?

I've read a few articles regarding getting values back from a modal popup in an ASP .NET page, but they all seem to use JavaScript to accomplish this which isn't really want I want to do if possible.
I have a web user control which has a repeater that I populate from a list into a table. Each row has a link button which has a redirect url with a value as a query string.
Then I have another web user control which is just a link button with a panel and the repeater web user control that once clicked shows the actual modal popup.
Is it possible to get a value from the web user control once the link button on the repeater is clicked without having to redirect to the same page? I basically want to click on the link, show the modal and once closed, want to access the value.
I'm populating the repeater with the links as follows:
string linkUrl = "";
string action = "";
if (Request.QueryString["action"] != null)
{
action = Request.QueryString["action"];
switch (action)
{
case "SetupCompany":
{
linkUrl = "<a href=CreateCompanies.aspx?companyId=";
break;
}
case "ViewCompany":
{
linkUrl = "<a href=ViewCompany.aspx?companyId=";
break;
}
}
}
CompaniesBusinessManager mgr = new CompaniesBusinessManager();
var companies = mgr.GetCompanies(txtCompanyName.Text, txtRegistrationNumber.Text);
if (linkUrl != "")
{
foreach (var c in companies)
{
c.Name = linkUrl + c.Id.ToString() + "&action=" + action + ">" + c.Name + "</a>";
}
}
rptrCompanies.DataSource = companies;
rptrCompanies.DataBind();
if you don't want the page to be redirected, you will need to use javascript.
There is now way you can pass values from different controls without going back to the server.
In case you keep it without the javascript:
I think you need to pass values from one user control to another. I used to accomplish this by firing reachable events between them.
For example:
in your parent view:
<uc:YourUserControl runat="server" ID="UserControl_Test"
OnYourCustomAction="UserControl_YourUserControl_YourCustomAction" />
In your user control:
public event EventHandler<CustomActionEventArgs> YourCustomAction;
also in the same user control create a public trigger method to be access from others usercontrols
public void TriggerCustomActoinEvent(CustomActionEventArgs EventArgs)
{
if (this.YourCustomAction!= null)
{
this.YourCustomAction(this, EventArgs);
}
}
Hope this help, in on my way to home this was from my mind!
Without a page postback or JavaScript it not really possible. If you are using modal popups you are already using JS, so why not just get the value in JS? You could setup an event handler for all repeater buttons and if they are loaded via ajax use something like this to attach the event handler:
$(document).on('click', '.repeaterButton', function(e) {
var valueOfBtnClicked = $(this).val();
// Do something else
});

how to add custom parameters to membership create user wizard?

I am really confused right now. Ive read some solutions to this problem, but every single one is different.... some people say that web applications projects and web site projects need different solutions.
What I have in thoughts:
I have a web application project
I created a table UserProfile in that table where I store additional columns like a link to an image.
My problem right now is that when I call RegisterUser_CreatedUser on submit, I can't find any way to get Guid of the newly created user :
protected void RegisterUser_CreatedUser(object sender, EventArgs e)
{
FormsAuthentication.SetAuthCookie(RegisterUser.UserName, createPersistentCookie: false);
TextBox ImageUrl = RegisterUserWizardStep.ContentTemplateContainer.FindControl("ImageUrl") as TextBox;
UserProfile.SaveNewProfileInformation("place for new created user guid", ImageUrl.Text);
string continueUrl = RegisterUser.ContinueDestinationPageUrl;
if (!OpenAuth.IsLocalUrl(continueUrl))
{
continueUrl = "~/";
}
Response.Redirect(continueUrl);
}
How do I get the guid of new created user? and am i on the right track at all to solve this problem?
You need to get the created user with the UserName
{
FormsAuthentication.SetAuthCookie(RegisterUser.UserName, createPersistentCookie: false);
//If user is created, get it by UserName
MembershipUser createdUser = Membership.GetUser(RegisterUser.UserName);
Guid userID = new Guid(createdUser.ProviderUserKey.ToString());
TextBox ImageUrl = RegisterUserWizardStep.ContentTemplateContainer.FindControl("ImageUrl") as TextBox;
//Use the userID from the above code
UserProfile.SaveNewProfileInformation(userID, ImageUrl.Text);
}

Check permission on every page

in my web site i need to check permission on every page,
i found my self repeating the same code every page.
this is one of my pages
public partial class KitView : AmsBasePage
{
protected void Page_Load(object sender, EventArgs e)
{
IddUser user = new IddUser();
user = (IddUser)Session["user"];
bool isAdmin = user.roles.Where(IddRole => IddRole.R_ID.Equals(3)).First().IsInRole;
bool isIddTeam = user.roles.Where(IddRole => IddRole.R_ID.Equals(2)).First().IsInRole;
bool isProductionTeam = user.roles.Where(IddRole => IddRole.R_ID.Equals(1)).First().IsInRole;
if (isAdmin)
{
hypAddComponent.Visible = true;
hypAddComponent.NavigateUrl = "AddComponent.aspx?CKID=" + Request.QueryString["CKID"];
}
}
}
how is the best practice to have the roles: isAdmin,isIddTeam,isProductionTeam
in every page but not repeating the code below in every page code
IddUser user = new IddUser();
user = (IddUser)Session["user"];
bool isAdmin = user.roles.Where(IddRole => IddRole.R_ID.Equals(3)).First().IsInRole;
bool isIddTeam = user.roles.Where(IddRole => IddRole.R_ID.Equals(2)).First().IsInRole;
bool isProductionTeam = user.roles.Where(IddRole => IddRole.R_ID.Equals(1)).First().IsInRole;
You should put your authorization code in your Master Page (ASP.NET Web Form) or Layout Page (ASP.NET MVC). That way, your authorization logic will only be placed in one location and runs on every page.
If you want to avoid redundant codes, i would suggest to write your authorization logic in a sepearte class or you can even write the aurthorization logic in a MasterPage if you have any. Then inherit them in your webforms.
Note: in your webforms you will have to override your page_load event so that the authorization from your inherited base class runs first.
if you want to avoid repeating authorization code, you should do it at a central location.
there can be many ways for that, but I can suggest you few
Use Master Page - and write the authorization code in Master Page's OnLoad
Create a HttpModule - Insert your own Module in the ASP.NET Page Events PipeLine and handle all the authorization and authentication logic
Now this is what I did in a multi-million $ Project
Create a PageBase.cs being inherited from System.Web.UI.Page - which you are already doing
Create a constructor of the PageBase, in which you can pass current Page permissions i.e.
public void PageBase(AppActivityEnum PageView, AppActivityEnum PageEdit, AppActivityEnum PageDelete)
{
this.pageView = PageView;
this.pageEdit=PageEdit;
this.PageDelete=PageDelete;
VerifyPermission();
}
where VerifyPermission() is:
public void VerifyPermission()
{
var currentUser= SessionHelper.GetCurrentUser();
var permissions = Utility.GetUserPermissions(currentUser.RoleId);
this.CanView=permissions.Contains((int)this.pageView);
this.CanEdit=permissions.Contains((int)this.pageEdit);
this.CanDelete=permissions.Contains((int)this.pageDelete);
}
now these three variables i.e. CanView, CanEdit, CanDelete are public properties in PageBase, hence available to all your pages(wherever you have inherited).
and you can set your controls(add button, delete button), page visibility based on these variables.
so basically, you create an Activity Table for storing ref of each of the Pages. where Activity table looks like
Id
Name
Value
Parent
a typical entry in this table is like:
1 Module-Master MMaster NULL
2 Module-Master-View MMasterView 1
3 Module-Master-Edit MMasterEdit 1
4 Module-Master-Delete MMasterDelete 1
and you maintain RoleAppActivtyMapping (obviously):
Id RoleId AppActivityId
1 1 2
1 1 3
1 1 4
so RoleId one has all the three permissions.
so GetUserPermissions(RoleId) basically gets all the RoleAppActivityMapping entries corresponding to passed Role.
so on every page you call the PageBase' constructor to verify the view permissions. You pass the current Page's AppActivity Id in the constructor.
and if CanView is false: you redirect to "UnAuthorized" page upon hitting the url.
I added in my AmsBasePage class that all pages inherit from .
this code
private bool _isAdmin;
private bool _isIddTeam;
private bool _isProductionTeam;
protected bool isAdmin
{
get { return _isAdmin; }
set { _isAdmin = value; }
}
protected bool isIddTeam
{
get { return _isIddTeam; }
set { _isIddTeam = value; }
}
protected bool isProductionTeam
{
get { return _isProductionTeam; }
set { _isProductionTeam = value; }
}
check your authorization in the master page. That way, I will be checked once and repeatation can be avoided.

Categories