In ASP.Net Web Forms there are cases that I've come across that create order dependent code. As an obvious code smell I'm looking for solutions to solve this problem.
A pseudo-code example would be:
Calling Code :: Page.aspx
protected void Page_Load(...) {
var control = LoadControl("ControlX.ascx");
// Ugly since the control's constructor is not used by LoadControl
control.SetDependencies(...);
}
Control Code :: ControlX.ascx
public void SetDependencies(...) {
}
protected void Page_Load(...) {
if (dependencies are null)
throw Exception();
else
ContinueProcessing();
}
LoadControl has two signatures, the one used above accepts a string for the control classes physical location and correctly creates the child controls. Whereas the second signature accepts the control class as a class type, and any parameters for the constructor, however the child controls are not created as detailed in TRULY Understanding Dynamic Controls.
So how can I eliminate this order dependency in the cleanest way? My first thought is that if I dynamically created the child controls in ControlX, but then that can be cumbersome for larger controls. Thoughts?
(I hope in understood the problem correctly) You could invert the dependecy like this:
The host of ControlX.ascx (either another control or a page) must implement a certain interface (defined by ControlX). ControlX can then access its dependencies from its host through that interface.
A small example would be this:
public interface IControlXHost
{
// methods, properties that allow ControlX to access its dependencies
int GetStuff();
}
public partial class ControlX : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
var host = (Parent as IControlXHost) ?? (Page as IControlXHost);
if (host == null) {
throw new Exception("ControlX's parent must implement IControlXHost");
}
else {
int number = host.GetStuff();
}
}
}
The host (the page or the control hosting ControlX) would then have to implement that interface, e.g:
public partial class Default4 : System.Web.UI.Page, IControlXHost
{
public int GetStuff() {
return 33;
}
protected void Page_Load(object sender, EventArgs e) {
var control = LoadControl("ControlX.ascx");
}
}
IMO, this approach makes controls easier reusable, since they "automatically" tell you about the requirements that have to be fulfilled to host the control. You don't have to find out which methods of the control you have to call in which order.
Related
I am working on a simple GUI framework, and I faced a problem calling protected virtual methods.
Here is the IKeyboardInputListenerService interface of service I use to receive keyboard events and the Control class that represents a base for all my GUI controls. The internal method is the one being refactored.
public interface IKeyboardInputListenerService
{
event EventHandler<KeyboardEventArgs> KeyPressed;
}
public abstract class Control
{
public IKeyboardInputListenerService KeyboardInputListenerService { get; }
protected Control(IKeyboardInputListenerService keyboardInputListenerService) =>
KeyboardInputListenerService = keyboardInputListenerService;
public event EventHandler<KeyboardEventArgs> KeyPressed;
/* protected */ internal virtual void OnKeyPressed(object sender, KeyboardEventArgs args)
{
if (enabled && visible && focused && !args.Suppressed)
{
KeyPressed?.Invoke(sender, args);
args.Suppressed = true;
}
}
public void Activate() =>
KeyboardInputListenerService.KeyPressed += new EventHandler<KeyboardEventArgs>(OnKeyPressed);
}
I also created a ContainerControl class that is supposed to contain child controls(like the Panel or GroupBox in Windows Forms) and that overrides the virtual method:
public abstract class ContainerControl : Control
{
private readonly ObservableCollection<Control> controls;
protected ContainerControl(IKeyboardInputListenerService keyboardInputListenerService)
: base(keyboardInputListenerService) =>
controls = new ObservableCollection<Control>();
/* protected */ internal override void OnKeyPressed(object sender, KeyboardEventArgs args)
{
foreach (Control control in controls)
control.OnKeyPressed(sender, args);
base.OnKeyPressed(sender, args);
}
}
Problem is, I cannot decide which modifier to use for methods such as OnKeyPressed. I wanted to make them protected, but it causes a compiler error:
Error CS1540 Cannot access protected member 'Control.OnKeyPressed(object, KeyboardEventArgs)' via a qualifier of type 'Control'; the qualifier must be of type 'ContainerControl' (or derived from it)
I can make them public, but I do not really think it is a good idea, because there is no reason for it except for resolving the problem caused by a cross-hierarchy call. I made them internal, but there is also a drawback: if anyone will want to create a user control, they will not be able to receive events, so the control will be useless.
The question is how to get access from a derived class to virtual methods of the base class without making the methods public-accessed.
Use protected internal
protected internal virtual void OnKeyPressed(object sender, KeyboardEventArgs args)
{ ... }
The documentation says:
protected internal The type or member can be accessed by any code in the assembly in which it is declared, or from within a derived class in another assembly.
I'm working on an application that supports various plugins (well, that's planned at least). And I'd love to have it beautiful.
So I want to let the plugin developer send a big control (like a panel or other containers) to my host application and have the user setup their settings for the plugin in the application.
That would take the plugin-developer's effort to somehow implement a settings-panel that runs by in an own window.
Thing is, I'm not sure how to do that.
I can pass variables to my host application but as soon as I try to add the control to my container panel, I get a RemoteException, telling me that the field 'parent' on type 'System.Windows.Forms.Control' can't be found.
I tried to add the plugin-control that way:
panel.Controls.Add(pluginControl);
If I try it the other way around:
pluginControl.Parent = panel;
I get a SerializationException because the class System.Windows.Forms.Control isn't marked Serializable.
Maybe some person ran into the same thing and can help me.
Let me know if you need more information!
Edit: Have a look on my current implementation: https://dl.dropboxusercontent.com/u/62845853/Random%20crap/NotModified_SamplePluginSystem.zip
I tried something which you can adorn to your needs:
First i created a PluginBase class and the proper EventArgs in a ClassLibrary:
public abstract class PluginBase
{
public abstract void Initialize();
protected void showControl(UserControl control)
{
ShowControl(this, new ControlToBeShownEventArgs() { TheControl = control });
}
public event EventHandler<ControlToBeShownEventArgs> ShowControl = delegate { };
}
public class ControlToBeShownEventArgs : EventArgs
{
public UserControl TheControl { get; set; }
}
This library is referenced by every Plugin and by the host application.
The Plugin is in turn also a Class Library (build path set to the one of the host)
inside i made a plugin inheriting this base type:
class SomePlugin : PluginBase
{
public override void Initialize()
{
showControl(new UserControl1());
}
}
The UserControl1 is the Control to be shown.
Done that, I next added the following code to the main window of the host:
List<PluginBase> plugins = new List<PluginBase>();
private void Form1_Load(object sender, EventArgs e) //Hook in the event too
{
DirectoryInfo dir = (new FileInfo(Assembly.GetExecutingAssembly().Location)).Directory;
foreach (var item in dir.GetFiles())
{
if (item.Name.Contains("Plugin") && item.Name.EndsWith(".dll"))
{
Assembly ass = Assembly.LoadFile(item.FullName);
foreach (Type type in ass.GetTypes().Where(t => t.BaseType.Name == "PluginBase"))
{
PluginBase pibase = (PluginBase)Activator.CreateInstance(type,false);
plugins.Add(pibase);
}
}
}
foreach (var item in plugins)
{
item.ShowControl += item_ShowControl;
item.Initialize();
}
}
void item_ShowControl(object sender, ControlToBeShownEventArgs e)
{
this.Controls.Add(e.TheControl);
}
What is the best way to assign security logic to a method in ASP.NET WebForms? Where instead of checking under each method if the user is logged in, can't we use method attributes?
Example, instead of doing this:
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (!UserLoggedIn)
{
Response.Redirect("/login");
}
//Do stuff
}
I would like to do something like below. I've seen it done in ASP.NET MVC apps but I wonder if I can pull it off with webforms. And also what would be the best practice for ensuring only an authenticated user can continue and others get redirected to login page?
Ex: Desired. Where Secure is a method attribute:
[Secure]
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
//Do stuff
}
How do I go about creating such method attribute? And if that is not possible, how would you recommend I do it? I have many usercontrols that need this on page_load or oninit and I am looking for a better way to do it.
Declare your attribute
[AttributeUsage(AttributeTargets.Class)]
public class SecureAttribute: Attribute
{
}
Create custom base page class for all forms
public class PageBase: Page
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
var secureAttr = Attribute.GetCustomAttribute(this.GetType(), typeof (SecureAttribute));
if (secureAttr != null)
{
bool UserLoggedIn = false; // get actual state from DB or Session
if (!UserLoggedIn)
{
Response.Redirect("/login");
}
}
}
}
Inherit all your forms from the PageBase
[Secure]
public partial class Profile: PageBase
{
}
Create similar UserControlBase for user controls.
One possible Solution would be a PageBase helper class to avoid check that condition on every single pages on your ASP.NET web forms and just inherits the page-base in your aspx.cs classes. something like the code below:
for example you want to make sure that some web forms are only accessible by Admin users then you could have a AdminPageBase class to check this condition for all of your web pages.
your base class:
public class AdminPageBase : System.Web.UI.Page
{
protected void Page_Init(object sender, EventArgs e)
{
if (!Context.User.Identity.IsAuthenticated ||
!HttpContext.Current.User.IsInRole(Roles.Admin.ToString()))
{
this.RedirectToLogin();
}
}
protected void RedirectToLogin()
{
//...
Response.Redirect("~/SignIn.aspx");
}
}
Note: Roles.Admin.ToString() is an enum, but you can also use a plain string if you like
and in your web form classes you only inherits this base class like this:
e.g. AdminPage1.aspx.cs
public partial class AdminPage1: AdminPageBase
{
//....
}
e.g. AdminPage2.aspx.cs
public partial class AdminPage2: AdminPageBase
{
//....
}
and you could always do the same for all other pages in your solution.
you could also change Page_Init to Page_Load on your PageBase class but the reason I have chosen the Page_Init is because you may need Page_Load event to check other things on your page so it's a good place to check your website security.
In order to intercept method calls, I would recommend utilizing some AOP framework, e.g. PostSharp, which allows easily inject behaviors before and after method execution by declaring custom aspect:
[Serializable]
public class SecureAttribute : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
bool UserLoggedIn = false; // get from DB
if (!UserLoggedIn)
{
HttpContext.Current.Response.Redirect("/login");
}
}
}
And applying that attribute to any method
public partial class Profile : Page
{
[Secure]
protected void Page_Load(object sender, EventArgs e)
{
}
[Secure]
protected void Button1_Click(object sender, EventArgs e)
{
}
}
As far as I know, PostSharp incures minor performance hit, or incures no performance hit at all, as PostSharp emits MSIL instructions.
I've created some custom textboxes which are inherited from textbox.
For the next step I want to register javascript with a wrapper.
Decorator pattern allow me to do if only I can inherit it from textbox and pass custom textbox as a constructor parameter.
Problem is that how can I use constructor when I add a control to aspx page or basically how can I use decorator pattern for asp.net controls.
EDIT:
Simply this is my validation base class (IField is an validation interface. This can be ignored):
public abstract class ValidationBase : TextBox, IField
{
private readonly IField _field;
protected ValidationBase(IField field)
{
_field = field;
}
public int MinLength
{
get { return _field.MinLength; }
set { _field.MinLength = value; }
}
public bool Required
{
get { return _field.Required; }
set { _field.Required = value; }
}
// other porperties etc...
protected override void OnPreRender(EventArgs e)
{
// DO SOME STUFF...
base.OnPreRender(e);
}
}
And this is my concrete class (EmailField is a concrete impl. of IField ignore...):
public class ValidationEmail : ValidationBase
{
public ValidationEmail()
: base(new EmailField(string.Empty))
{
}
}
And finally I want to implement this (I've made up my mind on wordpad this can't be the exact impl.):
public class JsRegisterDecorator : ValidationBase
{
private readonly ValidationBase _validationObj;
//I am not quite sure about the constructor but i can handle
public JsRegisterDecorator(ValidationBase concreteValidationObj)
: base(concreteValidationObj)
{
_validationObj = concreteValidationObj;
}
//Wrap the properties
protected override void OnPreRender(EventArgs e)
{
//Register JS Files...
_validationObj.OnPreRender(e);
}
}
The problem is that How can I use this decorator? Because asp.net construct controls automatically:
<vc:ValidationEmail ID="ValidationEmail1" runat="server"/>
I don't know can I use this (where can I put the constructor parameter?):
<vc:JsRegisterDecorator ID="ValidationEmailWithJs1" runat="server"/>
I don't think Decorator pattern fits well here. In general I saw more applications of Builder and Factory Method for ASP.NET controls.
To partially solve your task you can use ControlBuilder. It will give you ability to change the type of the control from ValidationBase to JsRegisterDecorator or ValidationEmail. You need to decorate ValidationBase class with ControlBuilderAttribute, inherit builder class from ControlBuilder and override Init method.
[ControlBuilder(typeof(ValidationBaseBuilder))]
public abstract class ValidationBase : TextBox, IField { }
public class ValidationBaseBuilder: ControlBuilder
{
public override void Init(TemplateParser parser, ControlBuilder parentBuilder, Type type, string tagName, string id, System.Collections.IDictionary attribs)
{
var newType = typeof(/*here you can put a JsRegisterDecorator type*/);
base.Init(parser, parentBuilder, t, tagName, id, attribs);
}
}
But I'm not sure about such approach. ControlBuilder cannot give you easy control over constructor. Surely you can override ProcessGeneratedCode in ControlBuilder and David Ebbo has a blog post worth reading but it would not be an easy task to rewrite constructor for control and make solution simple.
As alternative that will work I can suggest to add an abstract (or virtual) method like RegisterScripts inside ValidationBase and call it in OnPreRender. Every control will know what scripts it needs and the process of new validator control creation will be clean and simple. If you want to separate knowledge of JS scripts from concrete implementations then approach as seen in ASP.NET DynamicData (read MetaTable) could be used.
Another thing that I can see is that your idea is close enough to DynamicControl and maybe it would be possible to get more ideas from ASP.NET DynamicData like Field Templates and IFielTemplateFactory.
I solve my problem AlexanderManekovskiy's help and also some other questions:
ASP.NET RenderControl or RenderChildren fail
How to add child nodes to custom asp.net user control derived from System.Web.UI.Control
ASP.NET Custom/User Control With Children
And here is the solution:
I've made JsRegistererForValidationBase as a WebControl and implemented INamingContaier.
For the children elements I've created Children property which accepts olny list of Validation Base.
And finally OnInit method, I've registered the js.
Here is the code:
[ParseChildren(true)]
[PersistChildren(true)]
[ToolboxData(#"<{0}:JsRegistererForVB runat=""server""></{0}:JsRegistererForVB>")]
public class JsRegistererForValidationBase : WebControl, INamingContainer
{
private ValidationFieldCollection _children;
[PersistenceMode(PersistenceMode.InnerProperty)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ValidationFieldCollection Children
{
get
{
if (_children == null)
_children = new ValidationFieldCollection();
return _children;
}
}
protected override void CreateChildControls()
{
Controls.Clear();
foreach (var c in _children)
Controls.Add(c);
}
protected override void OnInit(EventArgs e)
{
//DO THE REGISTER STUFF
base.OnInit(e);
}
protected override void Render(HtmlTextWriter writer)
{
RenderChildren(writer);
}
}
public class ValidationFieldCollection : List<ValidationBase> { }
}
And at the aspx side it becomes like this:
<vc:JsRegisterer ID="JsRegisterer1" runat="server">
<Children>
<vc:ValidationEmail ID="ValidationEmail1" runat="server"/>
<vc:ValidationEmail ID="ValidationEmail2" runat="server"/>,
<!--etc-->
</Children>
</vc:JsRegisterer>
For the detailed imlementation I added the code to codeplex
Take the following scenario. I have multiple ASPX pages. Login, Logout, Main, Messages, etc... They all inherit from System.Web.UI.Page of course. For all the pages, I want to override the Render method from the Page class. I could easily copy and paste the same code into each page like so:
protected override void Render(HtmlTextWriter writer)
{
//Code Logic Here
}
But if I had many pages, lets say 20, maintaining the code in each page could get very time consuming and error prone.
That made me think a bit and I thought okay lets try this...override the function in each page but call a static function. That way changing the static function would result in a change for every page. Which works fine... But its not really nice and clean, having to override like that on every single page. Anybody have any ideas or thoughts on this one? Perhaps something simple I am overlooking? Thanks
EDIT: Some pages use the System.Web.UI.Page class and some pages inherit from another class called ModifiedPage which inherits and overridies other functions of the System.Web.UI.Page class. So its not as simple as inheriting all the pages from one class.
EDIT2: All pages need this behavior, some already derive from another class, and I am unable to change the implementation or inheritance hierarchy of that other class.
Instead of inheriting from System.Web.UI.Page, have them all inherit from MyProject.MyBasePage which inherits from Page:
public abstract class MyBasePage : System.Web.UI.Page
{
protected override void Render(HtmlTextWriter writer)
{
//Code Logic Here
}
}
and...
public partial class MySpecificPage : MyBasePage
{
}
Edit
Clarification added to the question now points out the real puzzle - the pages which all need this common Render logic have different inheritance paths. That is more tricky in c#, and you won't be able to avoid at least a little bit of redundant plumbing code. There's plenty of different ways to handle this - here's one approach I have taken in the past:
1) Create an interface for this common functionality. For example, IOverrideRender:
public interface IOverrideRender
{
void Register(OverrideRender render);
}
public delegate void OverrideRender(HtmlTextWriter writer, Action<HtmlTextWriter> original);
2) Each page which needs this functionality gets the interface and wires it like so:
public partial class MyPage : Page, IOverrideRender
{
void IOverrideRender.Register(OverrideRender render)
{
this.overrideRender = render;
}
private OverrideRender overrideRender;
protected override void Render(HtmlTextWriter writer)
{
if(overrideRender != nul)
{
overrideRender(writer, base.Render);
}
else
{
base.Render(writer);
}
}
}
3) In an HttpModule, check to see if the handler is IOverrideRender and if so, pass in your custom render method:
public class OverrideRenderModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PreRequestHandlerExecute += this.HandlePreRequestExecute;
}
private void HandlePreRequestExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
IOverrideRender overridable = app.Context.CurrentHandler as IOverrideRender;
if(overridable != null)
{
overridable.Register(
(writer, original) => {
writer.Write("Hello world"); //custom write
original(writer); //calls base.Render
});
}
}
}
You should create a BasePage which inherits from System.Web.UI.Page. Within the BasePage you could override the Render method and then have all your pages inherit from BasePage.
Add a level in your hierarchy and make a BasePage and do your override there, then inherit all other page from BasePage