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.
Related
In my .NET Application (.NET Framework 4.8) I am trying to implement an exchange of the implementation of one of my interfaces.
I have got the following project structure:
MyProgram.Exchange:
public interface IExchange {
void DoSomething();
}
MyProgram.Exchange.V1 (Reference to Some.dll (Version 1.0.0.0))
[Export(typeof(IExchange))]
public class Exchange : IExchange {
public void DoSomething(){}
}
MyProgram.Exchange.V2 (Reference to Some.dll (Version 2.0.0.0))
[Export(typeof(IExchange))]
public class Exchange : IExchange {
public void DoSomething(){}
}
In my Startup.cs of my main program I create a DirectoryCatalog and register the Types inside my V1 as default behaviour:
var catalog = new DirectoryCatalog($".", $"*V1*.dll");
// ...
var builder = new Autofac.ContainerBuilder();
builder.RegisterComposablePartCatalog(catalog);
Initially this works fine. But at some point further inside my Application I need to switch the Reference from V1 to V2.
My Problem now is, that when calling "builder.RegisterComposablePartCatalog(catalog)" I get an exception, because the referenced assembly "Some.dll" is already registered with another version.
Is there a way to completely remove the reference to MyProgram.Exchange.V1 and all its dependencies and register the MyProgram.Exchange.V2 instead.
Sorry if the explanation of the problem isnt the best, but I hope you get my problem.
I think in the case the option is to load assemblies dynamically using custom AppDomain.
You can realize it like:
create AppDomainAppDomain.CreateDomain()
Assembly.Load()
use instance / register your types using reflection
And when you'll need to replace the dll:
remove the types from DI registration
remove the AppDomain AppDomain.Unload() (the way to UNLOAD already loaded assemblies)
load new assemlby and register types using reflection analogically.
Unfortunately, when you reference dll at compile time, there's no option to "unload" it.
I have two projects in my solution. One represents the main project and another one has the name of the first project but adds ".API" at the end, which acts as an assembly for my interfaces.
Since I use an interface for specific classes, I access the interface and not the actual concrete class, this brings a problem when I was to access a file from the main assembly inside a file in the main assembly and that's fine, the problem comes as I need to mention it in the interface file.
Otherwise it wouldn't be accessible as the interface file is our class in this example.
Here is a code example...
namespace App.Classes
{
public class User : IUser
{
public SomeType SomeType { get; set; }
}
}
namespace App.Classes
{
public enum SomeType
{
SpecialType,
GoldType,
SilverType,
Other
}
}
namespace App.API
{
public interface IUser
{
public SomeType SomeType { get; set; }
}
}
The error I am receiving is the type or namespace name 'SomeType' could not be found.
When trying to add
using App to the interface file I receive an error telling me that namespace doesn't exist.
Primary assembly name: App
API assembly name: App.API
If i understand you correctly,
You have referenced your API (App.API), from you main app (App).
You are then trying to call/reference SomeType in your API which actually located back in (App).
Basically this (if it could be done) is called a Circular Reference for which .Net disallows.
Further more
The type or namespace name 'SomeType' could not be found
This error is entirely appropriate, because there is no reference (even check your API project) from the App.API project to App. I know its not there because it cant be done, .Net wont let you. Ergo Circular Reference
You need to make common things common, i.e If your API needs to know about SomeType it has to be placed in your API assembly (or a more common assembly that both App and App.API can reference).
The simple solution is to put SomeType back into App.API (the project, not just the namespace)
namespace App.API.Enums
{
public enum SomeType
{
SpecialType,
GoldType,
SilverType,
Other
}
}
Or to create a 3rd assembly and reference it from both the App and App.Api projects
I know how to get all types that implement an interface such as using this code.
However I have not figured out why I can't make this work in my Asp.Net MVC ApiController. I have two projects (apologies for the naming convention. I created a solution from scratch just to make sure that my existing one was not the cause of the error):
.sln
-WebAPI
-ClassLibrary1
-Interface1
-Class1 : Interface1
WebApi has a project reference to ClassLibrary1.
Calling my ApiController it looks at the dlls in the bin directory. It is able to get ClassLibrary1.dll but when it tries to look at which type is assignable from Interface1 it does not find anything.
Code is just a .net mvc project and class library and hosted here
You don't need to find referenced assembly by its path, you can just use the type to get its assembly as below:
internal class Program
{
private static void Main(string[] args)
{
var type = typeof(Interface1);
Assembly loadedAssembly = type.Assembly;
var types = loadedAssembly.GetTypes().Where(c => type.IsAssignableFrom(c));
foreach (var typeFound in types)
{
Console.WriteLine(typeFound.Name);
}
Console.ReadKey();
}
}
Output:
Interface1
Class1
The problem is that you have the assembly ClassLibrary1 loaded twice and therefore ClassLibrary1.Interface1 from the reference is not the same interface as ClassLibrary1.Interface1 from the loaded assembly.
Move Interface1 to its own shared library and reference this shared library in both ClassLibrary1 and WebAPI to solve your problem.
About Assembly.LoadFile, this is fine if you're planning to make a plugin like system. This is not needed if you are referencing the library because then you can just enumerate the types from the already loaded assembly.
In that case you can use:
typeof(Interface1).Assembly.GetTypes().Where(c => typeof(Interface1).IsAssignableFrom(c));
as suggested by Bhushan Firake.
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.
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