Get ASP.NET assembly from OnInit of custom server control? - c#

I'm trying to build an ASP.NET custom server control which displays version information about the ASP.NET project containing the page on which the server control is rendered.
What is the C# syntax to get the assembly of that ASP.NET project?
That is, given this OnInit override inside the custom server control's code...
protected override void OnInit(EventArgs e) {
System.Reflection.Assembly assembly = Foo();
}
... what goes in Foo()?
EDIT: The custom server control is defined in a Class Library project/assembly which is not the ASP.NET project/assembly.

public Assembly GetPageAssembly()
{
var pageType = Page.GetType();
return Assembly.GetAssembly(pageType.BaseType == null
|| pageType.BaseType == typeof (Page)
? pageType : pageType.BaseType);
}
No matter where the control's implementation is, a separate dll or the current one, it will be instantiated in a Page class in the end and added to its Controls collection. This Page is accessible via the Page method and, based on this, will find the assembly.
For an .aspx file ( actually a couple of them if more ), ASP .Net creates a dll. If the "Inherit" attribute is set, then the generated class will look something like:
public _Default_aspx : Namespace._Default, IHttpHandler {
}
This dll is different than the one compiled by Visual Studio, the result of an "Web Application Project" and I think you are interested more for the latest. This dll has the "_Default: type, that we see in Visual Studio:
public _Default : System.Web.Page
{
}
So why this short story? When this.Page.GetType() is called from the server control, then, if the Inherit attribute is set, the method will return _Default_aspx type, but is useless for you, since you need the assembly created by Visual Studio and not the one generated by ASP .Net from aspx/ascx files. If the page or the control has Inherit attribute set, then GetType() it suffices.
Knowing the type, a simply call to Assembly.GetAssembly method returns the assembly you need.

Related

How to register custom control in Dotnetnuke 8

I have created a class file with content:
namespace Controls
{
public class RadioButtonListCustomer : RadioButtonList
{
protected override void RenderItem(System.Web.UI.WebControls.ListItemType itemType, int repeatIndex, System.Web.UI.WebControls.RepeatInfo repeatInfo, System.Web.UI.HtmlTextWriter writer)
{
writer.Write("<td>");
base.RenderItem(itemType, repeatIndex, repeatInfo, writer);
writer.Write("</td>");
}
}
}
Register controls.
<%# Register Assembly="DotNetNuke.Web" TagPrefix="ww" Namespace="Controls" %>
Call controls:
<ww:RadioButtonListCustomer ID="irblUsers" runat="server">
</ww:RadioButtonListCustomer>
RadioButtonListCustomer is not found, so when run application, i get a error:
System.Web.HttpParseException: Unknown server tag 'ww:RadioButtonListCustomer'. ---> System.Web.HttpException: Unknown server tag 'ww:RadioButtonListCustomer'.
Starting with DNN 8.x and later, the application is now precompiled, which means that items outside of app_code will not be automatically included inside of the DotNetNuke.Web assembly.
If you change your register tag to be the following
I believe it will be able to find your control. This is assuming that you have your control defined inside of the App_Code folder.
I would recommend for better reusability in the long term that you create your own assembly and deploy in that manner though, it makes things easier later.

How does the method Initialize(); work in the membership.cs class of asp.net?

I'm attempting to understand the membership class and how it works in asp.net, however when looking at the Membership.cs file, I see the following code,
public static MembershipProvider Provider {
get {
Initialize();
if (s_Provider == null) {
throw new InvalidOperationException(SR.GetString(SR.Def_membership_provider_not_found));
}
return s_Provider;
}
}
I don't see a local method, and the class doesn't seem to inherit from any source that would provide code for it. How is it that the Initialize() method is able to give value to the s_Provider variable and where does its code live?
The class is a partial class. There is another file in the assembly that has the same full name, and is also marked as partial, and that contains a definition for that method.
You can use the Visual Studio "Go to Definition" feature in the context menu of Initialize to open up that file and navigate to the definition of that method.

Unable to access class from app_code

I have a web site(not web app) which has a default.aspx and default.aspx.cs.
And it has an App_Code folder with class A in it.
So default.aspx.cs. Looks like:
namespace test1
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
And class A:
namespace test1
{
public class A
{
_Default mDefaultRef;
public pageLogicIndividual(_Default defaultRef)
{
mDefaultRef = defaultRef;
}
}
}
I can use A in _Default. eg. if I type test1., while working in class _Default, the IntelliSense will show both classes.
But if I type test1., while working in class A, the IntelliSense will only show A.
Why can't I use _Default in A?
Error : the type or namespace name does not exist in the namespace
(are you missing an assembly reference )
Edit:
I'll try to clarify.
While working in the .cs file where class _Default resides I can type test1., which is the namespace, and the intellisense will show me class _Default and Class A.
But if I'm working in the .cs file where class A resides and type test1., which is the namespace, then the intellisense will only show me class A.
I have had this challenge in the past.
Open up the App_Code folder node in Visual studio.
Right click on the concerned class, then click properties
In the properties pane, change Build Action to Compile.
It should work fine now.
Your problem is with your misleading namespace that you've added yourself after the new file has been created because ASP.NET Web Sites do not have namespace. The namespaces are available in Web Applications projects. i.e. after a new WebSite is created, namespace doesn't added to the files.
So you don't need to place your class A inside the test1 namespace because you can use A in default.aspx.cs even without namespace but you can not access other WebForm page classes from a Webform page or App_Code classes.
BTW if you want to use the necessary and reusable methods within a class of the Default Web Form, you can move those methods out to A class which is under App_Code and as I said already you can use it within all the Web Form CodeFiles without providing namespace for it.
In a nutshell, you cannot access page classes from App_code classes.
This restriction comes from website project compilation model. Special App_code folder contains shared classes (and possibly other resources) which are available to pages (and to each other). During compilation App_code is compiled first in a single assembly. This is the key point. Pages, controls and MasterPages are compiled in another assembly(ies) which may have references to the App_code assembly (but not vise versa). So this is one-way road.
No namespace declaration should circumvent this behavior.
Pages can see each other in ASP namespace (ASP.default_aspx) but pages usually don't have public properties / methods (user controls .ascx usually have).
Read better explanation on MSDN Code-Behind Pages

How is an assembly resolved at design time?

I have an extensibility library (or the start of one), with a UITypeEditor. I'd now like to decorate the property with the EditorAttribute. I don't want to reference the extensibility library, as it does not need to be deployed, so I'm using this:
[Editor("MyProject.Extensibility.MyUIEditor, MyProject.Extensibility, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", typeof (UITypeEditor))]
MySpecialType SpecialType { get; set; }
This doesn't work. The type editor is for use on enums and when I use this, the standard enum drop down is shown. However, if you copy the type editor into the project and use a direct type reference, all works well. I've tried testing my string using Activator.CreateInstance and I've got that to work. The MyProject.Extensibility.dll is copied into just about every where (all the project's bin/debug folders). Is there some special place to put an extensibility dll so .net can resolve the assembly?
Thanks!
Just enter Regedit.exe and create a key just like:
HKLM\SOFTWARE\Microsoft\.NETFramework\v2.0.50727\AssemblyFoldersEx\StackOverflow
It doesn't really matter what the name of the key is, all folder names listed within AssemblyFoldersEx are searched for Assemblies design-time by Visual Studio.
A folder must be added in Regedit using a (Default) entry having the folder path as value. (See sibling keys for example).
It's interesting that all folders present in the AssemblyFoldersEx registry key will automatically also appear when you click "Add New Reference" on a project context menu on the .NET tab.
Another approach would be to add the desired assembly to Global Access Cache (c:\Windows\Assembly)
I just made the following test: On a resource assembly I put the following code:
public class MyEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
{
MessageBox.Show("Works");
return null;
}
}
On the consumer assembly (Windows forms executable assembly) I created a component that derives from Button just like this:
public class MyButton : Button
{
[Editor("AssemblyReferenceCL.MyEditor, AssemblyReferenceCL", typeof(UITypeEditor))]
public String MyProp { get; set; }
}
There's no reference between the two assemblies. Everything worked just fine.

Custom attributes in C#

I have a custom attribute for my page like this:
[PageDefinition("My page", "~/Parts/MyPage.aspx")]
My PageDefinition looks like this, where AttributeItemDefinitions is get set for Title, Url, IsPage and IsUserControl
public class PageDefinition : AttributeItemDefinitions
{
public PageDefinition(string title, string url)
: this()
{
Title = title;
Url = Url;
}
public PageDefinition()
{
IsPage = true;
IsUserControl = false;
}
}
But i can't find any good way to add all page with that attribute to a placeholder where all links should be list with the title and url. Do you have any good idea? Thanks for your help.
When I've created such custom attributes that define some metadata on a class I've often built a small routine that scans all classes of an assembly using reflection.
In my current project I'm using a IoC framework (other story) and instead of configuring it in a custom config file I've built myself a ComponentAttribute that defines what interface a class belongs to. (From a bird's eye view: I ask the IoC framework for a interface later on and it knows how to instantiate classes that implement that and how they fit together)
To configure that IoC framework I need to call a member of a certain class and tell it which class to interface mappingts exist.
ioc.ConfigureMapping(classType, interfaceType)
To find all those mappings I use the following two methods in one of my helper classes
internal static void Configure(IoCContainer ioc, Assembly assembly)
{
foreach (var type in assembly.GetTypes())
AddToIoCIfHasComponentAttribute(type, ioc);
}
internal static void AddToIoCIfHasComponentAttribute(Type type, IoC ioc)
{
foreach (ComponentAttribute att in type.GetCustomAttributes(typeof(ComponentAttribute), false))
{
ioc.ConfigureMapping(attribute.InterfaceType, type);
}
}
What I'm doing here is enumerating all of an assemblies' types in the first method, and than evaluting the attribute in the second one.
Back to your problem:
Using a similar approach you could find all the marked classes and record them in a container (ArrayList or similar) along with all the data you have defined in the attribute (Page path, etc.).
Update (Answer to comment)
When you build your program in Visual Studio you normally have one or more projects. For each project you will get a distinct assembly (.dll or .exe file). The code above will examine all the classes within one assembly. Seen that way an assembly is a collection of collected .cs files. So you want to search an assembly, not a directory of .cs files (which are source code and not part of the running application.)
So what's probably is missing: How can you access an assembly from your code when you want to search for classes? You just take ANY class you know (that is in the assembly/project where your other classes are) and obtain the assembly it is in by calling
var assembly = typeof(MyDummyClass).Assembly;
and afterwards you'd call something you derived from the code above
AnalyzeClasses(assembly)
and AnalyzeClasses would look like
internal static void AnalyzeClasses(Assembly assembly)
{
foreach (var type in assembly.GetTypes())
AnalzyeSingleClass(type);
}
internal static void AnalzyeSingleClass(Type type)
{
foreach (MyCustomAttribute att in type.GetCustomAttributes(typeof(MyCustomAttribute), false))
{
Console.WriteLine("Found MyCustomAttribute with property {0} on class {1}",
att.MyCustomAttributeProperty,
type);
}
}
And you'd just call all that before you run your application code, for example right
at the top in main() (for applications) or if it's difficult in advanced you can also
call this on demand when you need the collected data. (For example from an ASP.NET page)
It might be more than you need but...
I run into this pattern all of the time in my projects so I implemented a type loader that can be supplied with user defined delegates for a type search matching.
http://www.codeproject.com/KB/architecture/RuntimeTypeLoader.aspx

Categories