GetScriptDescriptors not being called on Script Control - asp.net

I'm building a script control for asp.net Ajax, and while i can get the GetScriptReferences() function to be called, i cannot get the GetScriptDescriptors().
I've tried deriving from ScriptControl, ScriptControlBase, IScriptControl. I'm registering the control with the pages script manager, but i still cannot get the function to be called?
Any ideas on what i might have missed?
public class FilterGroupingControl : CompositeControl, IScriptControl
{
public List<FilterGrouping> Groupings { get; set; }
public FilterGroupingControl()
{
this.Groupings = new List<FilterGrouping>();
}
protected override void OnPreRender(EventArgs e)
{
#region register control with script manager
ScriptManager scriptManager = ScriptManager.GetCurrent(Page);
if (scriptManager == null)
throw new InvalidOperationException("There must be a script manager on the page");
scriptManager.RegisterScriptControl(this);
#endregion
base.OnPreRender(e);
}
public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
throw new InvalidOperationException();
ScriptControlDescriptor d = new ScriptControlDescriptor("Web.UI.Controls.FilterGroupingControl", this.ClientID);
d.AddProperty("Groupings", this.Groupings.ToArray());
return new ScriptDescriptor[] { d };
}
public IEnumerable<ScriptReference> GetScriptReferences()
{
// throw new InvalidOperationException();
return new ScriptReference[0];
}
}

if you use IScriptControl, you must then add this to the render process:
if (!this.DesignMode)
{
ScriptManager.GetCurrent(this.Page).RegisterScriptDescriptors(this);
}
As mentioned here: GetScriptReferences does not get called
RegisterScriptControl notifies the script manager of the scripts, and invokes GetScriptReferences. You need to call RegisterScriptDescriptors to handle the component registration, and subsequent $create method call on the client.

Related

subscribing to Blazor AuthenticationStateChanged

I could not find any example of how to use the AuthenticationStateChanged in blazor.
My intention is that any page where i want to react to user login or logout i will use these
code. I could not find any example on how to implement the event. the one that i tried just keeps on firing for infinite times.
_CustomAuthProvider.AuthenticationStateChanged += AuhtenticationStateChanged;
private async void AuhtenticationStateChanged(Task<AuthenticationState> task)
{
//This keeps on executing in in loop.
}
I know this is old, but I would have liked an answer when I found it...
This is the code I use on a Blazor web assembly (dotnet 6.0). This is part of a scoped service that I can access through dependency injection from anywhere else in my application.
Notice the await(task) to retrieve the state in the event handler:
public AuthenticationService(AuthenticationStateProvider authenticationProvider, IProfileService profileService)
{
_profileService = profileService;
_authenticationProvider = authenticationProvider;
_authenticationProvider.AuthenticationStateChanged += AuthenticationStateChangedHandler;
// perform initial call into the event handler
AuthenticationStateChangedHandler(_authenticationProvider.GetAuthenticationStateAsync());
}
private bool _disposed = false;
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
_authenticationProvider.AuthenticationStateChanged -= AuthenticationStateChangedHandler;
}
}
public event AuthenticationChanged? AuthenticationChanged;
public AuthenticationState? AuthenticationState { get; private set; }
private async void AuthenticationStateChangedHandler(Task<AuthenticationState> task)
{
AuthenticationState = await (task);
if (IsAuthenticated)
{
// first load profile
await _profileService.LoadProfile(UserName!);
}
else
{
await _profileService.EmptyProfile();
}
// then update all listening clients, invoke the event
AuthenticationChanged?.Invoke(AuthenticationState);
}

WF 4 OnUnhandledException not hit

I've created a custom activity which contains as a Body another Activity.
[Browsable(false)]
public Activity Body { get; set; }
protected override void Execute(NativeActivityContext context)
{
ActivityInstance res = context.ScheduleActivity(Body, new CompletionCallback(OnExecuteComplete), OnFaulted);
}
private void OnFaulted(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
{
throw new Exception(propagatedException.Message);
}
When an exception is thrown during the execution of the Body, ma handler for the OnFaulted is hit.
My execution starts with a call to static method Run of the WorkflowApplication class. My WorkflowApplication instance has a handler associated for the OnUnhandledException event.
instance.OnUnhandledException +=
delegate(WorkflowApplicationUnhandledExceptionEventArgs args)
{
Console.WriteLine(args.ExceptionSource);
waitEvent.Set();
return UnhandledExceptionAction.Cancel;
};
But regardless of what happens when the Activity hosted in the Body is executed, i never reach the handler defined above. I thought that if i throw an exception from the OnFaulted, i will be able to redirect the flow to the OnUnhandledException but i was wrong. Any ideas ?
I need this in order to centralize my errors, check them and display messages accordingly. Also i need a way to stop the execution and so on and i don't want to define handlers all over the application. Is there any way to accomplish this ?
As Will suggested, i will post what i did to handle my scenario.
Basically, in my custom activity i have hosted an Assign :
[Browsable(false)]
public Activity Body { get; set; }
Activity System.Activities.Presentation.IActivityTemplateFactory.Create(System.Windows.DependencyObject target)
{
return new Assignment()
{
Body = new Assign() { DisplayName = "" }
};
}
I've added this code to my Execute method :
ActivityInstance res = context.ScheduleActivity(Body, new CompletionCallback(OnExecuteComplete), OnFaulted);
I was trying to run this Assignment by giving an array a negative value as index and and an exception was thrown. This, somehow ended my execution but no handler for the events of my WorkflowApplication instance were hit.
Here is the method given as a callback when executing the body ( in our case the Assign activity ) :
private void OnFaulted(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
{
faultContext.HandleFault();
CommunicationExtension ce = faultContext.GetExtension<CommunicationExtension>();
ITextExpression toTextExpression = (propagatedFrom.Activity as Assign).To.Expression as ITextExpression;
string valueTextExpression = string.Empty;
if ((propagatedFrom.Activity as Assign).Value != null)
{
if ((propagatedFrom.Activity as Assign).Value.Expression != null)
valueTextExpression = (propagatedFrom.Activity as Assign).Value.Expression.ToString();
}
if (ce != null)
{
ce.AddData(string.Format("{0} found on Assignment definition [{1} = {2}]", propagatedException.Message, toTextExpression.ExpressionText, valueTextExpression));
}
}
The trick was to call :
faultContext.HandleFault();
and use CommunicationExtension to allow me to to display the erros in the GUI.
The code for this class is trivial :
public class CommunicationExtension
{
public List<string> Messages { get; set; }
public CommunicationExtension()
{
Messages = new List<string>();
}
public void AddData(string message)
{
if (string.IsNullOrEmpty(message))
return;
Messages.Add(message);
}
}
Use this to add the extension:
CommunicationExtension ce = new CommunicationExtension();
instance.Extensions.Add(ce);
where instance is my WorkflowApplication instance.
I understood that for each instance of the workflow application we have one instance of its extension class. So i can send messages like this from all my custom activities in order to display their status.
I hope this scenario can help other people too.

Partial caching of custom WebControls

I need to cache the generated content of custom WebControls. Since build up of control collection hierarchy is very expensive, simple caching of database results is not sufficient. Caching the whole page is not feasible, because there are other dynamic parts inside the page.
My Question: Is there a best practice approach for this problem? I found a lot of solutions caching whole pages or static UserControls, but nothing appropriate for me. I ended up with my own solution, but im quite doubtful if this is a feasible approach.
A custom WebControl which should be cached could look like this:
public class ReportControl : WebControl
{
public string ReportViewModel { get; set; }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
// Fake expensive control hierarchy build up
System.Threading.Thread.Sleep(10000);
this.Controls.Add(new LiteralControl(ReportViewModel));
}
}
The aspx page which includes the content control(s) could look as follows:
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Fake authenticated UserID
int userID = 1;
// Parse ReportID
int reportID = int.Parse(Request.QueryString["ReportID"]);
// Validate if current user is allowed to view report
if (!UserCanAccessReport(userID, reportID))
{
form1.Controls.Add(new LiteralControl("You're not allowed to view this report."));
return;
}
// Get ReportContent from Repository
string reportContent = GetReport(reportID);
// This controls needs to be cached
form1.Controls.Add(new ReportControl() { ReportViewModel = reportContent });
}
private bool UserCanAccessReport(int userID, int reportID)
{
return true;
}
protected string GetReport(int reportID)
{
return "This is Report #" + reportID;
}
}
I ended up writing two wrapper controls, one for capturing generated html and a second one for caching the content - Quite a lot of code for simple caching functionality (see below).
The wrapper control for capturing the output overwrites the function Render and looks like this:
public class CaptureOutputControlWrapper : Control
{
public event EventHandler OutputGenerated = (sender, e) => { };
public string CapturedOutput { get; set; }
public Control ControlToWrap { get; set; }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Controls.Add(ControlToWrap);
}
protected override void Render(HtmlTextWriter writer)
{
StringWriter stringWriter = new StringWriter();
HtmlTextWriter htmlTextWriter = new HtmlTextWriter(stringWriter);
base.RenderChildren(htmlTextWriter);
CapturedOutput = stringWriter.ToString();
OutputGenerated(this, EventArgs.Empty);
writer.Write(CapturedOutput);
}
}
The wrapper control to cache this generated output looks as follows:
public class CachingControlWrapper : WebControl
{
public CreateControlDelegate CreateControl;
public string CachingKey { get; set; }
public delegate Control CreateControlDelegate();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
string content = HttpRuntime.Cache.Get(CachingKey) as string;
if (content != null)
{
// Content is cached, display
this.Controls.Add(new LiteralControl(content));
}
else
{
// Content is not cached, create specified content control and store output in cache
CaptureOutputControlWrapper wrapper = new CaptureOutputControlWrapper();
wrapper.ControlToWrap = CreateControl();
wrapper.OutputGenerated += new EventHandler(WrapperOutputGenerated);
this.Controls.Add(wrapper);
}
}
protected void WrapperOutputGenerated(object sender, EventArgs e)
{
CaptureOutputControlWrapper wrapper = (CaptureOutputControlWrapper)sender;
HttpRuntime.Cache.Insert(CachingKey, wrapper.CapturedOutput);
}
}
In my aspx page i replaced
// This controls needs to be cached
form1.Controls.Add(new ReportControl() { ReportViewModel = reportContent });
with
CachingControlWrapper cachingControlWrapper = new CachingControlWrapper();
// CachingKey - Each Report must be cached independently
cachingControlWrapper.CachingKey = "ReportControl_" + reportID;
// Create Control Delegate - Control to cache, generated only if control does not exist in cache
cachingControlWrapper.CreateControl = () => { return new ReportControl() { ReportViewModel = reportContent }; };
form1.Controls.Add(cachingControlWrapper);
Seems like a good idea, maybe you should pay attention to :
the ClientIdMode of the child controls of your custom control to prevent conflicts if these controls are to be displayed in another context
the LiteralMode of your Literal : it should be PassThrough
the expiration mode of your cached item (absoluteExpiration/slidingExpiration)
disable ViewState of your CustomControl
Recently, I tend to have another approach : my wrapper controls only holds some javascript that performs an AJAX GET request on a page containing only my custom control.
Caching is performed client side through http headers and serverside through OutputCache directive (unless HTTPS, content has to be public though)

ASP.NET + NUnit : Good unit testing strategy for HttpModule using .NET 4

I have the following HttpModule that I wanted to unit test. Problem is I am not allowed to change the access modifiers/static as they need to be as it is. I was wondering what would be the best method to test the following module. I am still pretty new in testing stuff and mainly looking for tips on testing strategy and in general testing HttpModules. Just for clarification, I am just trying to grab each requested URL(only .aspx pages) and checking if the requested url has permission (for specific users in our Intranet). So far it feels like I can't really test this module(from productive perspective).
public class PageAccessPermissionCheckerModule : IHttpModule
{
[Inject]
public IIntranetSitemapProvider SitemapProvider { get; set; }
[Inject]
public IIntranetSitemapPermissionProvider PermissionProvider { get; set; }
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += ValidatePage;
}
private void EnsureInjected()
{
if (PermissionProvider == null)
KernelContainer.Inject(this);
}
private void ValidatePage(object sender, EventArgs e)
{
EnsureInjected();
var context = HttpContext.Current ?? ((HttpApplication)sender).Context;
var pageExtension = VirtualPathUtility.GetExtension(context.Request.Url.AbsolutePath);
if (context.Session == null || pageExtension != ".aspx") return;
if (!UserHasPermission(context))
{
KernelContainer.Get<UrlProvider>().RedirectToPageDenied("Access denied: " + context.Request.Url);
}
}
private bool UserHasPermission(HttpContext context)
{
var permissionCode = FindPermissionCode(SitemapProvider.GetNodes(), context.Request.Url.PathAndQuery);
return PermissionProvider.UserHasPermission(permissionCode);
}
private static string FindPermissionCode(IEnumerable<SitemapNode> nodes, string requestedUrl)
{
var matchingNode = nodes.FirstOrDefault(x => ComparePaths(x.SiteURL, requestedUrl));
if (matchingNode != null)
return matchingNode.PermissionCode;
foreach(var node in nodes)
{
var code = FindPermissionCode(node.ChildNodes, requestedUrl);
if (!string.IsNullOrEmpty(code))
return code;
}
return null;
}
public void Dispose() { }
}
For other people still looking there is this post which explains a way to do it
Original page was deleted, you can get to the article here:
https://web.archive.org/web/20151219105430/http://weblogs.asp.net/rashid/unit-testable-httpmodule-and-httphandler
Testing HttpHandlers can be tricky. I would recommend you create a second library and place the functionality you want to test there. This would also get you a better separation of concerns.

How do I convert an ASP.NET page using Ajax webmethods to an Ajax-enabled servercontrol?

In this tutorial I am reading, Dave Ward creates a page that shows the server date in a label without using the update panel.
I am trying to learn how to create servercontrols that use ajax for partial postbacks where methods within the control are called from clientscript generated by the same control, and I think that learning how to convert this page to a server control would be a help me understand what servercontrols use instead of webmethods to expose their methods to clientscript.
I created the page, codebehind, and javascript exactly as the article indicated and got the sample to work.
So, to start trying to convert this to a servercontrol, I moved Dave's Javascript for the page into a file, ~tests/JScript.js:
function UpdateTime() {
PageMethods.GetCurrentDate(OnSucceeded, OnFailed);
}
function OnSucceeded(result, userContext, methodName) {
$get('Literal1').innerHTML = result;
}
function OnFailed(error, userContext, methodName) {
$get('Literal1').innerHTML = "An error occured.";
}
And put the following class in my App_Code:
namespace foo
{
/// <summary>
/// Summary description for ServerControlTest
/// </summary>
public class ServerControlTest : CompositeControl, IScriptControl
{
ScriptManager sm;
protected override void OnPreRender(EventArgs e)
{
if (!this.DesignMode)
{
// Test for ScriptManager and register if it exists
sm = ScriptManager.GetCurrent(Page);
if (sm == null)
throw new HttpException("A ScriptManager control must exist on the current page.");
sm.RegisterScriptControl(this);
sm.EnablePageMethods = true;
}
base.OnPreRender(e);
}
protected override void OnLoad(EventArgs e)
{
Literal lit = new Literal();
lit.Text = "<span ID=\"Literal1\" runat=\"server\">test</span><input id=\"Button1\" type=\"button\" value=\"button\" onclick=\"UpdateTime();\" />";
this.Controls.Add(lit);
}
protected override void Render(HtmlTextWriter writer)
{
if (!this.DesignMode)
sm.RegisterScriptDescriptors(this);
base.Render(writer);
}
[WebMethod]
public static string GetCurrentDate()
{
return DateTime.Now.ToString();
}
#region IScriptControl Members
IEnumerable<ScriptDescriptor> IScriptControl.GetScriptDescriptors()
{
return null;
}
IEnumerable<ScriptReference> IScriptControl.GetScriptReferences()
{
ScriptReference reference = new ScriptReference();
reference.Path = ResolveClientUrl("~/tests/JScript.js");
return new ScriptReference[] { reference };
}
#endregion
}
}
Now, in my sample page, when I click the button, I get this error:
PageMethods is not defined
[Break on this error] PageMethods.GetCurrentDate(OnSucceeded, OnFailed);
How do I call GetCurrentDate from the clientscript that my control registers?
There is actually no fully encapsulated method for implementing AJAX callbacks against methods of a server control yet, as of v3.5. It's a very frustrating limitation.
The most common solution is to create an HttpHandler in your server control's assembly, then require that the handler be registered in the web.config. Look at how ASP.NET AJAX's ScriptResource.axd is wired up in the web.config in ASP.NET AJAX 1.0, for example.

Resources