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)));
}
}
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.
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 technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.