Get all methods that have an attribute - c#

I have a project that I am loading in an Assembly. That assembly has a reference to NUnit.
My project also has a reference for NUnit to find all methods that contain an attribute. However, When I query the methods, it seems like that the two NUnit assemblies are different and thus doesn't successfully get the methods.
Is there a way to just not compare assemblies somehow?
private static IEnumerable<MethodInfo> GetTestMethods()
{
var alc = new TestAssemblyLoadContext("SomeDll.dll");
Assembly assembly = alc.LoadFromAssemblyPath("SomeDll.dll");
// This works but returns all methods
var good = assembly.GetTypes()
.SelectMany(t => t.GetMethods());
// This doesn't work since TestAttribute is from another assembly (seems like it compares all TestAttributes that have this projects Assembly but the actual assembly in the DLL is not the same)
var bad = assembly.GetTypes()
.SelectMany(t => t.GetMethods())
.Where(m => m.GetCustomAttributes(typeof(TestAttribute), false).Length > 0 || m.GetCustomAttributes(typeof(TestCaseAttribute), false).Length > 0);
alc.Unload();
return bad;
}

I won't talk of the overall architecture you seem to be using, but for your specific problem you can just use the m.GetCustomAttributes(false) overload, and then test the attribute's name as a string rather than a type if needed.

Related

Get .NET Class Hierarchy

I am trying to get all the objects in .NET Framework with their hierarchy order with their namespaces and put them into a tree structure or some sort for academic purposes in C#. I am trying to do this for a specific .NET framework platform. Is there a way to do such thing?
Request: List everything (classes, enums, structures etc.) under System.Threading namespace
Result:
Constructors:
AbandonedMutexException
Barrier
Interlocked
blah blah
Structures:
AsyncFlowControl
CancellationToken
blah blah
Delegates:
ContexCallback
ThreadStart
blah blah
Enumerations:
blah blah
Pretty much what I want is something like this. - MSDN
Many thanks.
Edit: I am not asking for a code sample or someone to write the code for me - I just can't figure out where to start so I am looking for tips.
Here are some guidelines on how to get started:
You will need to use Reflection, i.e. the classes from the System.Reflection namespaces (Assembly, ConstructorInfo, PropertyInfo, and similar). As stated on MSDN:
The classes in the System.Reflection namespace, together with System.Type, enable you to obtain information about loaded assemblies and the types defined within them, such as classes, interfaces, and value types. You can also use reflection to create type instances at run time, and to invoke and access them.
Start by getting an instance of the Assembly class. You can either pass a type from the assembly you want to access (any type, like System.Threading.Barrier for example) and use Assembly.GetAssembly:
var assembly = Assembly.GetAssembly(typeof(System.Threading.Barrier));
Or, if you're going to iterate through a folder of .dll files, you can use use Assembly.LoadFrom:
var assembly = Assembly.LoadFrom("path-to-System.Threading.dll");
Once you have the assembly instance, you will want to iterate through the types and do further reflection upon them:
foreach (var type in assembly.GetTypes())
{
var constructors = type.GetConstructors();
var fields = type.GetFields();
var properties = type.GetProperties();
...
}
The Type class, apart from these methods, also contains various properties which allow you to see if the specified type is a class or a struct (Type.IsValueType, Type.IsInterface, Type.IsAbstract, etc.), see if one type inherits or implement a different one (Type.IsAssignableFrom(otherType), Type.GetInterfaces), and similar things.
Someone (Groo) has alread given the "theoretical" reply about how you should do it, but note that there are at least two strong caveats:
any Microsoft assembly could contain a System.Threading class (and technically any assembly could contain it, even yours, but it is bad etiquette to do it)
if you want to explore a "base" assembly (mscorlib, System, ...) from a different version of .NET than the one you are running from then you'll need Mono.Cecil or similar library, because with Reflection you can't load a different version of a base assembly than the one you are running)
And now some very simple code:
var assemblyNames = new[]
{
"mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
};
var nss = new[]
{
"System.Threading"
};
// Begin real code
var assemblies = Array.ConvertAll(assemblyNames, Assembly.Load);
var nss2 = Array.ConvertAll(nss, x => x + ".");
var types = assemblies.SelectMany(x => x.ExportedTypes).Where(x => nss2.Any(y => x.FullName.StartsWith(y))).ToArray();
// The Delegate check is taken from http://mikehadlow.blogspot.it/2010/03/how-to-tell-if-type-is-delegate.html
var classes = types.Where(x => x.IsClass && !typeof(Delegate).IsAssignableFrom(x)).ToArray();
var structs = types.Where(x => x.IsValueType && !x.IsEnum).ToArray();
var enums = types.Where(x => x.IsEnum).ToArray();
var delegates = types.Where(x => x.IsClass && typeof(Delegate).IsAssignableFrom(x)).ToArray();
// There is a DeclaringType property to see what Type is declaring them
var constructors = types.SelectMany(x => x.GetConstructors(BindingFlags.Public | BindingFlags.Instance)).ToArray();
If you can get hold of the corresponding XML file for the framework you're interested in, you might find it easier to transform that than to extract this information directly from the code.

How can I search the assemblies for all the classes available in a specific namespace with from a controller using c#?

I need to get all available classes in a giving namespace.
Here is what I have done
In my Index method in XyzController.cs I added this line.
var classesList = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.Namespace == "PATH.TO.HAMESPACE").ToList()
Unfortunately, that gave me no records/classes.
However, when I created a new class in the the same namespace i.e. PATH.TO.HAMESPACE. with the same code. Then called this class from the controller, the code returns the correct classes list.
How can I run this code from the controller to get all available classes with in PATH.TO.HAMESPACE?
Instead of GetExecutingAssembly() try GetAssembly(typeof(PATH.TO.HAMESPACE.SampleClass))
The problem is when you are in the controller the executing Assembly in not the same assembly of the classes you need, while when you created that class , the assemble is the correct one.
So you need to get the correct assembly and then filter it
Overkill method, but that could work:
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t => t.Namespace == "Namespace");
Or (and), you should try to load explicitly your assembly.
With Assembly.Load or Assembly.LoadFrom.
Assembly.Load("your assembly fullname")
.GetTypes()
.Where(t => t.Namespace == "Namespace");

Find the Type of a Class using its full name during Design-Time

Edit: I am using DataSourceProviderService.InvokeAddNewDataSource method to display Data Source Configuration Wizard during design-time in Visual Studio. If user choose an object (as explained here), and click finish, I will get a string like "Namespace.ClassName". To display the Properties of the selected object in designer, I need to find the correct Type of the object in an optimized manner.
I have the name of a class and its namespace (Application.Data.Employee). I want to find the type of the class (Employee) with this information. At present I am using the following code to find the type
string classNameWithNameSpace = "Application.Data.Employee";
Type target;
foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
foreach (Type t in assembly.GetTypes())
{
if (t.FullName.Equals(classNameWithNameSpace))
{
target = t;
break;
}
}
Note: Assembly might be present in any dll referenced in the project. My code also supports .Net Framework 2.0
I know this is not the best way because of the following reasons
1) Two or more assemblies might have same namespace name and class name
2) I saw a SO post stating, it will throw NotSupportedException for dynamic assemblies
3) On debugging found that Types in unwanted or unnecessary assemblies are checked in the loop. AppDomain.CurrentDomain.GetAssemblies() method returns 146 assemblies in a simple project during design-time debugging
4) If above code loads an unnecessary assembly into memory, it will present in memory till application domain is present (check unloading an assembly section in this link https://msdn.microsoft.com/en-us/library/mt632258.aspx)
Is there any recommended way or best approach for doing the same?
You can use these services to work with types at design-time:
ITypeResolutionServic helps you to retrieve an assembly or type by name at design time.
ITypeDiscoveryService helps you to get a list of available types at design time.
For example you can write such methods and pass them a suitable IServiceProvider which can get those service:
Type GetTypeByName(IServiceProvider provider, string typeName)
{
var svc= (ITypeResolutionService)provider.GetService(typeof(ITypeResolutionService));
return svc.GetType(typeName);
}
private List<Type> GetAllTypes(IServiceProvider provider)
{
var svc= (ITypeDiscoveryService)provider.GetService(typeof(ITypeDiscoveryService));
return svc.GetTypes(typeof(object), true).Cast<Type>().ToList();
}
I've used these mechanism in TypeConverter, UiTypeEditor, T4 templates and Add-ons. It's the same way that visual studio uses to work with types at design-time.
Here is the exact code you need to get properties:
var svc = ((DataSourceProviderService)Site.GetService(typeof(DataSourceProviderService)));
if (svc != null)
{
var result = svc.InvokeAddNewDataSource(this, FormStartPosition.CenterScreen);
if(result!=null && result.DataSources.Count>0)
{
var type = GetTypeByName(this.Site, result.DataSources[0].TypeName);
var properties = type.GetProperties().ToList();
MessageBox.Show(string.Join(",", properties.Select(x => x.Name)));
}
}
As you said that the search in your algorithm is also scanning unwanted assemblies. In case you plan to search only your own product's assemblies then you can leverage the standard nomenclature of the assemblies in case you have it. This will dramatically reduce the targeted assemblies which are scanned for the target type. Line # XYZ does the initial task of filtering the relevant assemblies assuming all the assemblies to be searched have a some standard prefix MyCompanyName.MyProductName in their name. Also I've replaced most of your calls with LINQ calls which are syntactically lot more cleaner.
string classNameWithNameSpace = "Application.Data.Employee";
Type target;
var assemblyList = AppDomain.CurrentDomain.GetAssemblies();
//line # XYZ
var filteredAssembliesOfMyProduct =
assemblyList.Where(x => x.FullName.StartsWith("MyCompanyName.MyProductName"));
foreach (Assembly assembly in filteredAssembliesOfMyProduct)
if (assembly.GetTypes().Any(x => x.FullName == classNameWithNameSpace))
{
target = assembly.GetTypes().First(x => x.FullName == classNameWithNameSpace);
break;
}
The inner loop is equivalent to calling assembly.GetType(classNameWithNameSpace), so you can skip it completely. This should take care of item 3 from your list.
Item 2 can be solved by ensuring that Assembly does not have IsDynamic flag in .NET 4.0, or checking the namespace prior to 4.0.
This code is suitable for .NET 2.0
IList<Type> matchingTypes = new List<Type>();
foreach(Assembly a in AppDomain.CurrentDomain.GetAssemblies()) {
// Skip dynamic assemblies.
if (a.GetType().StartsWith("System.Reflection.Emit.")) {
continue;
}
Type t = a.GetType(classNameWithNameSpace);
if (t != null) {
matchingTypes.Add(t);
}
}
Rewrite with LINQ and IsDynamic after .NET 4.0:
var matchingTypes = AppDomain
.CurrentDomain
.GetAssemblies()
.Where(a => !a.IsDynamic)
.Select(a => a.GetType(classNameWithNameSpace))
.Where(t => t != null)
.ToList();
The above gives you a list of all types with classNameWithNameSpace.
Dealing with item #1 is something best left to your application. You need to decide what to do with each of the types on the matchingTypes list.
It is useful to remember about type forwarding. The list above will include both types. You can use TypeForwardedToAttribute to decide which type you should actually take.

Get all derived types of an abstract class within all the assemblies

I have a library that has an abstract class inside - suppose MyAbstractClass. This library is being used by some other projects (one app) within one solution.
I want to get all these classes. I created sample Program project and Program.Library that contains MyAbstractClass.
I have created two test classes derived from MyAbstractClass. One in the first project (Test1Class) and one in the second one (Test2Class).
I tried this code to find what I want:
var list = Assembly.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes())
.Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof (MyAbstractClass)));
It worked but found only Test1Class (because - I guess - it's placed in the same assembly as the executed code presented above?). I don't know how to find all the derived classes within all the assemblies in my whole app.
I have also tried:
var list = Assembly.GetAssembly(typeof(MyAbstractClass)).GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof (MyAbstractClass)));
But this one gives me an empty collection...
You need to be sure that all assemblies you want to look in are already loaded when you call Assembly.CurrentDomain.GetAssemblies();
An Assembly get loaded when the runtime need it (i.e. when your program reach a point in your code that "use" that assembly, or if you specifically call a Assembly.Load or Assembly.LoadFile or whatever method you can use to explicitly load it
The problem is CurrentDomain.GetAssemblies() only lists the Assemblies currently loaded in your Application Domain.
If you want to get the types in all assemblies in your application, then you need to ensure (somehow) that all assemblies are loaded.
One way of loading the assemblies manually is to use the following line of code:
var assembly = Assembly.LoadFile(#filename)
You'd need to do the above for each file, and then you can use the method CurrentDomain.GetAssemblies().

Why I can't load classes from other assemblies?

In the web.config of my MVC project, I have this:
<appSettings>
<add key="MembershipRepository" value="Repo.MembershipRepository"/>
</appSettings>
I have a code like this:
var appSettings = ConfigurationManager.AppSettings;
string membershipRepositoryClassName = appSettings["MembershipRepository"];
Type membershipRepositoryType = Type.GetType(membershipRepositoryClassName);
IMembershipRepository repository = (IMembershipRepository)Activator.CreateInstance(membershipRepositoryType);
Suppose, the web application containing the web.config is in assembly Web.
The code segment I gave is in assembly Lib.
The class MembershipRepository is in assembly Repo.
Web has reference to both Lib and Repo.
Lib does not have reference to any other assemblies (Its liekly to be referenced as dll).
Repo may or may not have reference to Lib.
I get the membershipRepositoryType to be null. I understand that perhaps I need to specify the assembly in which MembershipRepository is. One way is to specify the assembly name in configuration (like this). But I think there should be some other ways. Otherwise how the other classes are loaded geting only the class name from the config file?
For instance, MembershipProvider class is loaded just fine from other assemblies.
How can I do the same. Or if I can't, why I can't?
From the MSDN Library documentation for Type.GetType(String):
typeName: The assembly-qualified name of the type to get. See AssemblyQualifiedName. If the type is in the currently executing assembly or in Mscorlib.dll, it is sufficient to supply the type name qualified by its namespace.
That last sentence explains why it sometimes works when you specify only the class name: If the executing code is in Repo, then you can get away with just specifying MembershipProvider. But I recommend that for clarity (and performance too), you avoid this behavior and always specify the assembly name. Alternately, if you already have a reference to the Assembly that contains your type, then you can call Assembly.GetType instead.
Nevertheless, if you truly have a scenario where you don't know which assembly contains your type, then you can search all loaded assemblies in the current AppDomain and try calling Assembly.GetType on each one:
Type type = AppDomain.CurrentDomain.GetAssemblies()
.Select(assembly => assembly.GetType(typeName))
.First(t => t != null);
What you're looking for is something like this:
var types = AppDomain.CurrentDomain.GetAssemblies().ToList()
.SelectMany( s => s.GetTypes() )
.Where( p => p.Name == membershipRepositoryClassName );
That is actually going to return a list, because a type with the same name may be declared in more than one assembly. If you are sure there is only one type, you can add .FirstOrDefault() or .Single() to the end of the chain. Use .FirstOrDefault() if you aren't sure if the type exists and want it to return null if it doesn't. Use .Single() if you are sure the type should exist is and want it to throw an exception if they type isn't found.

Categories