I am using the PartialCaching attribute on the base class of a user control.
I would like the cached controls to vary based on the properties set on the control instance.
For example:
<mycontrols:control1 runat="server" param1="10" param2="20" />
...output would be cached separately from a control instance with different properties:
<mycontrols:control1 runat="server" param1="15" param2="20" />
...and this control would be cached separately as well:
<mycontrols:control1 runat="server" param1="10" param2="25" />
However, if two control instances on two separate pages had identical param1 and param2 properties, I'd like them to cache as one object (so that cached control would be shared).
Can the above use case be achieved with PartialCaching attribute? What settings would I use? varyByControl?
Also, is it possible to make the cache duration variable at runtime?
Thanks.
To answer your first Q, let me first tell you that your question itself has the answer ;). 'Shared' ... yes that's the keyword :) To have a single instance in cache for the user control across all the pages, set Shared='true' in the #OutputCache directive. This should be set at the user control level i.e. in the ascx page.
To cache the user control based on user control properties, you should specify the fully qualified name of the properties in the varyByControls section of the PartialCachingAttribute. Multiple properties if any should be separated by semi-colons.
<%# Control Language="C#" AutoEventWireup="true"
CodeFile="WebUserControl.ascx.cs"
Inherits="UC_WebUserControl" %>
<%# OutputCache Duration="60"
VaryByControl="UC_WebUserControl.param1;UC_WebUserControl.param2"
VaryByParam="none" Shared="true" %>
or you can also include the PartialCache attribute for the user control:
[PartialCaching(60, null, "UC_WebUserControl.param1;UC_WebUserControl.param2", null, true)]
public partial class UC_WebUserControl : System.Web.UI.UserControl
{
public string param1 { get; set; }
public string param2 { get; set; }
}
OR another way to cache the control on the combination of both values would be:
[PartialCaching(60, null, "UC_WebUserControl.BothParams", null, true)]
public partial class UC_WebUserControl : System.Web.UI.UserControl
{
public string param1 { get; set; }
public string param2 { get; set; }
public string BothParams
{
get { return String.Concat(param1, param2); }
}
}
The last parameter (true) specifies shared. Duration is specified by 60. Refer to the link How to: Cache Multiple Versions of a User Control Based on Parameters
To answer your second Q, to make the cache duration for the user control variable at run time, you can do it in two ways:
Assign it in the user control code behind:
[PartialCaching(60, null, "UC_WebUserControl.BothParams", null, true)]
public partial class WebUserControl1 : System.Web.UI.UserControl
{
...
protected void Page_Load(object sender, EventArgs e)
{
this.CachePolicy.Duration = new TimeSpan(0, 0, 60);
}
}
You can assign it in the code behind of the page where user control is referenced using the ID of the user control.
e.g. If the user control on the aspx is:
<mycontrols:control1 ID="ucControl1" runat="server" param1="15" param2="20" />
then in the code behind of aspx, you should write:
this.ucControl1.CachePolicy.Duration = new TimeSpan(0, 0, 60);
FYI, if both the user control and page are cached: If the page output cache duration is less than that of a user control, the user control will be cached until its duration has expired, even after the remainder of the page is regenerated for a request. For example, if page output caching is set to 50 seconds and the user control's output caching is set to 100 seconds, the user control expires once for every two times the rest of the page expires.
What you need is Caching Multiple Versions of a User Control by Using Declarative Attributes.
I'm posting a new answer to this very old question because the accepted answer is woefully inaccurate.
This correct answer is:
There is no built-in way to vary by control property value. VaryByControl only works for controls.
When a cached version is served, your control field will be null. You can't change the cache duration in code - you would get a NullReferenceException.
There is a bug that varies cache by control ID and NamingContainer ID(s) if VaryByControl is set to any value. That is why it appears to work sometimes. The bug is right here: http://referencesource.microsoft.com/#System.Web/xsp/system/Web/UI/PartialCachingControl.cs#530
I blogged about this recently right here: http://tabeokatech.blogspot.be/2014/09/outputcache-on-user-controls.html .
A way you could make this work is call the builder method for the PartialCachingControl yourself in code-behind, and embed the property value you want to vary by in the guid parameter:
// PhControls is an asp:PlaceHolder
protected void Page_Init(object sender, EventArgs e)
{
for (int i = 0; i < 3; i++)
{
System.Web.UI.StaticPartialCachingControl.BuildCachedControl(PhControls, String.Format("Test{0}", i), String.Format("1234567{0}", i), 180, null, null, null, null, new System.Web.UI.BuildMethod(GetBuilderDelegate(i)), null);
}
}
public Func<Control> GetBuilderDelegate(int number)
{
return delegate()
{
var control = (UserControls.Test)LoadControl("~/UserControls/Test.ascx");
control.Number = number;
return control;
};
}
That neatly takes care of varying caching duration in code-behind as well. Make sure you remove the OutputCache directive from the markup in the ascx though when you do this. Otherwise the LoadControl gets you another PartialCachingControl and the cast fails.
Related
I have a user control that is being integrated to the page on run time, now here on the page i have a few HTML control whose values need to save too, as i have written the save method logic to the user control because it has many content their to save in DB, but what i am missing is how to save the page content to the DB using the same Save Method?
If my understanding is correct, you just need to pass the values to the UC, just declare public properties in your UC and assign the values at runtime:
Check the following example:
Public properties approach
In the UC code behind
public string Street
{
get
{
return this.ViewState["c"] as string;
}
set
{
this.ViewState["c"] = value;
}
}
public void Save()
{
...
// your logic to save the UC data
// save the custom properties too, this.Street
{
In the ASPX code behind
this.myAddressTemplateControl.Street = myOtherControl.Text;
Accessing directly the page object
In the UC code behind
this.Page.FindControl("myPageControlID");
in my project i need to upload files so i decided to use the uploader provided by asp.net ajax controls AsyncFileUPloader control.
there are four blocks. every block contains two such uploaders
so i decided to utilize the power of asp.net web user controls.
i wrapped the required form fields in my user control called DesignUploader.ascx
now i have to put the four instances of this control on my aspx page
please refer the snap below
my problem starts here i have to insert the fileurl to the database and each of the block generates unique id and id value changes after uploading the file to the server. i noticed that viewstate does not work for me in case of asyncfileuploader it clears the viewstate because it does the secret postback to the server behind the scenes. now only option left for me is to use session but when user uploads the files in two blocks one after another then filepath from second/third consecutive blocks overwrite my session. i don't know how many blocks user can use to upload the designs he may use 1 only or he may use all four.
There would be a final submit button in the bottom of the page on click of which i have to insert data to database.
so when i tried to save the data to database the session stores the value of the recently uploaded file path for all the records my problem lies here
i don't know if i was able to describe my problem in correct manner or not please excuse me if it is not clear and post comment if required.
Note: I can not change the UI because client insists for this only :(
any quick work around would be appreciated much
Thanks
Devjosh
I believe you saving file path to session in a wrong way and it's impossible to recognize where is an error without code.
All the way, in my opinion better don't persist file path in session but use client side for that purpose instead. You can add two hidden fields to DesignUploader.ascx control and set their values in UploadedComplete event handler.
public partial class DesignUploader : System.Web.UI.UserControl
{
private static readonly string AppDataPath = HttpContext.Current.Server.MapPath("~/App_Data/");
public string FirstFilePath
{
get
{
return Server.UrlDecode( FirstFilePathHiddenField.Value);
}
}
public string SecondFilePath
{
get
{
return Server.UrlDecode(SecondFilePathHiddenField.Value);
}
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
FirstFileUpload.UploadedComplete += FirstFileUpload_UploadedComplete;
SecondileUpload.UploadedComplete += SecondileUpload_UploadedComplete;
}
void FirstFileUpload_UploadedComplete(object sender, AjaxControlToolkit.AsyncFileUploadEventArgs e)
{
var fullPath = Path.Combine(AppDataPath, Path.GetFileName(e.FileName));
FirstFileUpload.SaveAs(fullPath);
SaveFilePathToHiddenField(FirstFilePathHiddenField.ClientID, fullPath);
}
void SecondileUpload_UploadedComplete(object sender, AjaxControlToolkit.AsyncFileUploadEventArgs e)
{
var fullPath = Path.Combine(AppDataPath, Path.GetFileName(e.FileName));
SecondileUpload.SaveAs(fullPath);
SaveFilePathToHiddenField(SecondFilePathHiddenField.ClientID, fullPath);
}
private void SaveFilePathToHiddenField(string fieldId, string pathValue)
{
var script = string.Format("top.$get('{0}').value = '{1}';", fieldId, Server.UrlEncode(pathValue));
ScriptManager.RegisterStartupScript(this, this.GetType(), "setPath", script, true);
}
}
I'm trying to create my calendar control with databinding.
public partial class Calendar : UserControl
{
public static readonly DependencyProperty DateProperty =
DependencyProperty.Register("Date", typeof(DateTime),
typeof(Calendar), null);
public object Date
{
get { return GetValue(DateProperty); }
set
{
SetValue(DateProperty, value);
OnPropertyChanged("Date");
}
}
public Calendar()
{
// Required to initialize variables
InitializeComponent();
DayText.Text = ((DateTime)Date).ToString("dd");
MonthText.Text = ((DateTime)Date).ToString("MMM");
this.Loaded += new RoutedEventHandler(Calendar_Loaded);
this.GotFocus += new RoutedEventHandler(Calendar_Loaded);
}
void Calendar_Loaded(object sender, RoutedEventArgs e)
{
DayText.Text = ((DateTime)Date).ToString("dd");
MonthText.Text = ((DateTime)Date).ToString("MMM");
}
}
But When I create the listbox with this control, same calndar have the wrong date. I'm sure that the Date passed thorough databinding is correct but I don't understand why same calender show a different day (I'm noticed that is the day of a previous calendar control intance)
Thank you for supporting!
Hmm ... where do we start? Here's a few things I've noticed:
If you're using a dependency property, there's no need to call OnPropertyChanged from the Date property setter.
The dependency property declares the type as DateTime, but your public exposed property is of type object, which then requires you to cast it elsewhere.
If Calendar_Loaded is to be called in more situations than in response to the Loaded event (such as the GotFocus event, then I'd recommend that you call it something else, or create a method with a relevant name (e.g. UpdateDateParts) and call it from properly named separate event handlers.
Using fixed format specifiers when processing date strings does not localize well.
In addition to that, I'd suggest that you could implement the user interface in a manner that supports databinding (and re-templating) by using bindings and exposing the date parts of the Date dependency property instead of manually updating the Text property of some text blocks/boxes in event handlers. In fact, if you derive from Control instead of UserControl then you can create and actuall lookless control that has it's user interface defined by a style in themes\generic.xaml that can be re-defined by users of your control.
As for why the date is incorrect in different instances of your calendar control, we'd need to see some of your XAML/code to see how the control is being used and initialized to be able to provide a better answer. However, I thought the above was worth putting in an Answer, instead of trying to say it in a Comment.
I'm looking for some ideas to best persist an object over multiple page requests without the use of session variables, cookies, context cache, view state or a session database.
I have a web form (ASPX) that will contain several user controls that will act as "pages", and these controls will be displayed in a one-at-a-time-manner:
<uc1:UserControl1 id="controlStep1" runat="server" visible="true" />
<uc2:UserControl2 id="controlStep2" runat="server" visible="false" />
<uc3:UserControl3 id="controlStep3" runat="server" visible="false" />
I have an object that I use to contain various parameters. These parameters come into the application as query string values and this object lazy loads them. For example:
public class Situation
{
private string _jobId;
private JobType _jobType;
public string JobId
{
get
{
if (!String.IsNullOrWhitespace(_jobId))
return _jobId;
_jobId = GetFromQueryString("jid");
return _jobId;
}
}
public JobType JobType
{
get
{
if (_jobType != JobType.Default)
return _jobType;
_jobType = GetEnumFromQueryString("jtype", typeof(JobType));
return _jobType;
}
}
}
I'd like to persist this Situation object while the customer is in the web application, proceeding through a wizard-style interface as the user controls' visibilities are toggled. At the moment, I'm creating an instance of the Situation object in an HttpModule during the customer's first request and storing it in HttpContext.Current.Items, but I'd like to use lazy loading so that the various Situation object properties are only loaded as needed. However, if for example a property is accessed on controlStep1, I'd like to persist the state of the object so that if that same property is accessed on controlStep2, the getter doesn't have to go back to the query string to get the requested property's value. I could serialize the object and deserialize in an HttpModule, but if a property is accessed and loaded, it wouldn't be remembered on the next request.
Any suggestions?
File system is sometimes a useful persistance mechanism.
I have a listview called "lvQuestions" which has a label (called lblMissingField) on it and a couple of other fields. When the user tries to postback the page, I call a routine that first sets ALL lvlMissingField.Text = string.empty and then validates that "the other" fields in the ListViewDataItem have been filled in. If they haven't been filled I set the particular listview's lvlMissingField.Text = "*" to visually mark them with a "*" (Note: standard asp.net validators are not used).
So, what happens? Every lblMissingField.Visible is displayed, what am I missing here?
foreach(ListViewDataItem question in unansweredQuestions)
{
((Label)question.FindControl("lblMissingField")).Text = "*";
}
I've made that the unansweredQuestions are only those ListViewDataItems that I want, and still am suprised to see the output HTML sets all of them to "*"...?
I've also tried Setting Visible = true instead of setting the Text to '*' (which was my original preference) without any luck.
Going a little mad here...
When I have needed to do functionality similar to what you are doing and the updates / checks were done server side, I have implemented a DataBinding method for each control I want to customize. When something changes, I rebind to my model with the bits and pieces updated that need to be changed.
So for example in your case assume I am binding to an list of objects (could be DataTable or whatever) that has the following details:
public class MyData
{
public string Question { get; set; }
public string Answer { get; set; }
}
public List<MyData> listOfMyData = GetListOfMyData();
Then my databinding event on the label would be implemented
<asp:Label ID="lblMissingField" runat="server" OnDataBinding="lblMissingField_DataBinding" />
Then the implementation of the databinding:
protected void lblMissingField_DataBinding(object sender, System.EventArgs e)
{
Label lbl = (Label)(sender);
lbl.Text = (Eval("Answer").ToString() == string.Empty) ? "*" : string.Empty;
}
This gives you a lot of control on every single item in your template. Instead of dealing with all your controls after your data is loaded, this lets you have the data modified as the data is being bound.
I know it's not a solution to your exact question but maybe just a suggestion of another way to approach the problem.