More powerful version of Type.GetType(string)?

G

Guest

I have been looking for a more powerful version of GetType(string) that will
find the Type no matter what, and will work even if only supplied
"{TypeName}", not the full "{TypeName},{AssemblyName}"

As far as I know yet -- hence this question -- there is no 'one solution
fits all', but instead there are several parts that have to be put together
to check.

What I have so far is, and would like as much feedback as possible to ensure
I've done the best job possible is:

Step 1: Find a Type in the calling Assembly, or fallback to mscorlib:
GetType() looks for a class by that name in the calling Assembly, and then
falls back to search in mscorlib.
As you can imagine, this doesn't get us far.

Step 2: Looking in other Assemblies already loaded:
If not in either of these two, you have to fall back to using
AppDomain.GetAssemblies(), and then loop through each individually to test
each one with assembly.GetType(string).

Step 3: Looking in Assemblies not already loaded:
But these only refer to assemblies already loaded -- and will fail if the
assembly has not loaded yet, so you then have to Assembly.Load(pathName) for
each assembly in the bin dir that ends with ".dll" and ".exe".

Step 4: Looking in the GAC:
The above solutions almost get us through everything, except there stilll is
missing one place to look, the GAC. How can I get a list of assemblies in the
GAC, to search through them as well?


Questions:
a) The Assembly.LoadFile() worries me a lot: how does it work, exactly? I
assume it does exactly as implied, and loads it up in memory -- so calling it
at the beginning of a program could mean that *all* assemblies are loaded in
memory right from the start, making for a slower startup... Calling and
loading all files in the GAC to search through them would end up with a lot
of memory for assemblies that have nothing to do with the application, right?

b) This all seems to be a very complex route, that even with caching for
later, could be a massive drain on system resources -- is there any simpler
way?

c) GetAssemblies() is not available on CE...Is there *any* other way to get
a list of assemblies loaded in memory other than AppDomain.GetAssemblies?

Thank you for any and all feedback, and pointers,
Sky

BTW: For anybody that is interested, the code for the above looks a bit like:

private static Type LoadType(string typeName) {
System.Reflection.Assembly assembly;
return LoadType(typeName, out assembly);
}

private static Type LoadType(string typeName, out
System.Reflection.Assembly assemblyTypeFoundIn) {
if (string.IsNullOrEmpty(typeName)) {
throw new System.ArgumentNullException("typeName");
}
//Clear results first:
assemblyTypeFoundIn = null;
System.Reflection.Assembly assembly = null;
System.Type type = null;

type = System.Type.GetType(typeName, false, true);

if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}

#if (!PocketPC) && (!pocketPC) && (!WindowsCE)
foreach (System.Reflection.Assembly tmpAasembly in
System.AppDomain.CurrentDomain.GetAssemblies()) {
type = tmpAasembly.GetType(typeName);
if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}
}
#endif

//Get Path to Bin folder:
string exePath;
//exePath = System.AppDomain.CurrentDomain.BaseDirectory;
//exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().GetModules()[0].FullyQualifiedName);
string[] fileNames = System.IO.Directory.GetFiles(exePath, "*.dll");
foreach (string fileName in fileNames) {
try {
assembly = System.Reflection.Assembly.LoadFrom(fileName);

type = assembly.GetType(typeName);
if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
catch {
assembly = null;
}


}
return null;
}
 
N

Nicholas Paldino [.NET/C# MVP]

Sky,

There is no way around it, you are going to have to provide the assembly
name, and not just the type name. A type's identity is not simply the name
that it has, but also the module/assembly that it is located in.

For what reason are you looking for a particular type across ALL
assemblies? If you have a need for a specific type, why not have a well
known location (like a subdirectory) where you can load the assemblies and
scan for your type?


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Sky said:
I have been looking for a more powerful version of GetType(string) that
will
find the Type no matter what, and will work even if only supplied
"{TypeName}", not the full "{TypeName},{AssemblyName}"

As far as I know yet -- hence this question -- there is no 'one solution
fits all', but instead there are several parts that have to be put
together
to check.

What I have so far is, and would like as much feedback as possible to
ensure
I've done the best job possible is:

Step 1: Find a Type in the calling Assembly, or fallback to mscorlib:
GetType() looks for a class by that name in the calling Assembly, and then
falls back to search in mscorlib.
As you can imagine, this doesn't get us far.

Step 2: Looking in other Assemblies already loaded:
If not in either of these two, you have to fall back to using
AppDomain.GetAssemblies(), and then loop through each individually to test
each one with assembly.GetType(string).

Step 3: Looking in Assemblies not already loaded:
But these only refer to assemblies already loaded -- and will fail if the
assembly has not loaded yet, so you then have to Assembly.Load(pathName)
for
each assembly in the bin dir that ends with ".dll" and ".exe".

Step 4: Looking in the GAC:
The above solutions almost get us through everything, except there stilll
is
missing one place to look, the GAC. How can I get a list of assemblies in
the
GAC, to search through them as well?


Questions:
a) The Assembly.LoadFile() worries me a lot: how does it work, exactly? I
assume it does exactly as implied, and loads it up in memory -- so calling
it
at the beginning of a program could mean that *all* assemblies are loaded
in
memory right from the start, making for a slower startup... Calling and
loading all files in the GAC to search through them would end up with a
lot
of memory for assemblies that have nothing to do with the application,
right?

b) This all seems to be a very complex route, that even with caching for
later, could be a massive drain on system resources -- is there any
simpler
way?

c) GetAssemblies() is not available on CE...Is there *any* other way to
get
a list of assemblies loaded in memory other than AppDomain.GetAssemblies?

Thank you for any and all feedback, and pointers,
Sky

BTW: For anybody that is interested, the code for the above looks a bit
like:

private static Type LoadType(string typeName) {
System.Reflection.Assembly assembly;
return LoadType(typeName, out assembly);
}

private static Type LoadType(string typeName, out
System.Reflection.Assembly assemblyTypeFoundIn) {
if (string.IsNullOrEmpty(typeName)) {
throw new System.ArgumentNullException("typeName");
}
//Clear results first:
assemblyTypeFoundIn = null;
System.Reflection.Assembly assembly = null;
System.Type type = null;

type = System.Type.GetType(typeName, false, true);

if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}

#if (!PocketPC) && (!pocketPC) && (!WindowsCE)
foreach (System.Reflection.Assembly tmpAasembly in
System.AppDomain.CurrentDomain.GetAssemblies()) {
type = tmpAasembly.GetType(typeName);
if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}
}
#endif

//Get Path to Bin folder:
string exePath;
//exePath = System.AppDomain.CurrentDomain.BaseDirectory;
//exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().GetModules()[0].FullyQualifiedName);
string[] fileNames = System.IO.Directory.GetFiles(exePath, "*.dll");
foreach (string fileName in fileNames) {
try {
assembly = System.Reflection.Assembly.LoadFrom(fileName);

type = assembly.GetType(typeName);
if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
catch {
assembly = null;
}


}
return null;
}
 
G

Guest

Hi Nicholas:

Very good question, actually.

Actually it probably comes from my having had really frustrating experiences
with app.configs/ConnectionSettings/Providers in general when I first started
out. Seemed I could never get up and running without a lot of poking around
due to very vague error messages, combined with one little spelling mistake
in an app.Config.

The whole thing set me off right from the start distrusting pointing to
assemblies with version and culture numbers: it seems to be way too specific,
and causes me nothing but frustration.

It also just seems to me that the current assemby full names are *way* too
specific, whereas they should be in *most* cases 'loose', only resorting to
specific culture/version to handle specific clashes.

In addition, I'm not sure that I even agree that a config file is the best
location for some of this identity inforation: I suspect that Culture should
be handled via code choices of satellite resource assemblies,

But probably what worries me the most is the issue of version numbers --
hard to update a growing app (ie more assemblies) over the web without having
to update the config file as well. And that is made very complicated when the
app has
a) user settings in it,
b) is a modular type of software where other vendors may need to also have
settings in the app.config for their assemblies.

Talking about that last piece a little bit more: it seems that the
app.config model is more suited for installation packages, and/or ClickOnce
packages. But if I am correct, code is going to be more and more like
services...ie thin+ clients that start off with some modules, and as needed
(or paid for...) or released, more modules are downloaded and installed, that
are updatable much more fluidly (webservice+serializationofdlls+throw out and
restart appdomain)...where the webconfig stuff will need to be updated for
every new module downloaded, and every version shift in the downloaded
modules.... Seems like this is very hard to do with current thinking... I'm
REALLY looking for pointers/thoughts/articles on this type of thinking...

Then again, this point of view of mne all might be caused from to a
completly wrong understanding of how the whole thing fits together, whereas a
fresh look/undestanding of how to use it all would sort it all out! :) For
example, I'm one of the people who just doesn't get User Settings in an
app.config. Oh, sure I get the simplicity of typed access to properties...but
saving user settings in an app.config seems to be exactly the same as keeping
stuff intended for c:\Documents and Settings\Users\YourSettings under
c:\Programs Files\Your app.... Sounds like a very messed up way of doing
things, no?

Hence, my struggling around to find a good solution: currently this leads me
to believe that references to assemblies being 'loose', with short names, and
not always the assembly it belongs in.

Which when I think about it...doesn't answer your question... Hum.

As for why I can't put the assembly name... That was your question. Is the
answer simply because I'm lazy? I know the type, but I don't always know what
assembly it is in? Especially because I sometimes move things around from one
assembly to another as I progress through coding and get clearer as to where
it should go(eg: I start with a simple 1 assembly exe, and later make it into
an multiple assembly application (UIL, BLL, DAL) and have to move code that
was originally placed in UIL to BLL...
Which is not a good reason to not put in the assembly name as well. It just
seems to indicate that I am lazy. Hum. I'll have to think about that.

Sky


BTW: Solution to my question in the last post, about not using up all
resources:
....the solution is to use a secondary AppDomain to load the Assemblies into,
that can then be dumped. I was stunned to find that one can get a Type out of
an Assembly in a secondary domain, and then dump the secondary domain, and
use the Type in the first domain, without it bugging out due to the Type
pointing to a null address...I would have thought (limited experience with
AppDomains...) that it would somehow cause a type of memory exception
pointing into mem that is no longer available...even after a GC.Collect().
So there. It works. On desktop/full.... But not on CE because CE's AppDomain
is stunted and doesn't have a Load(string) feature to do the same trick.
Anybody have an idea?

PPS: I am still looking for answers to find out what is in the GAC? And the
equivalent to LoadAssembly(string) and GetAssemblies(string) on Compact
NET...anyone?


public static Type LoadType(string typeName, out System.Reflection.Assembly
assemblyTypeFoundIn) {
if (string.IsNullOrEmpty(typeName)) {
throw new System.ArgumentNullException("typeName");
}
//Clear results first:
assemblyTypeFoundIn = null;
System.Reflection.Assembly assembly = null;
System.Type type = null;

type = System.Type.GetType(typeName, false, true);

if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}
//Create a second domain:
System.AppDomain currentAppDomain = System.AppDomain.CurrentDomain;
System.Security.Policy.Evidence evidence =
System.AppDomain.CurrentDomain.Evidence;
System.AppDomain secondDomain =
System.AppDomain.CreateDomain("typeLoader", evidence);
System.AppDomain tmpDomain = secondDomain;//currentAppDomain;
int tmpCount = currentAppDomain.ReflectionOnlyGetAssemblies().Length;
//zero?

//Get assemblies in tmp domain:
//suprisingly, this turned out to have same results
//as currentDomain -- I would have thought it would report back
//less assemblies. Nice.
System.Reflection.Assembly[] assemblies = tmpDomain.GetAssemblies();
int foundCount = assemblies.Length;

foreach (System.Reflection.Assembly tmpAasembly in assemblies) {
type = tmpAasembly.GetType(typeName);
if (type != null) {
System.AppDomain.Unload(tmpDomain);
System.GC.Collect();
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
tmpCount = currentAppDomain.ReflectionOnlyGetAssemblies().Length;

//Get Path to Bin folder:
string exePath;
//exePath = System.AppDomain.CurrentDomain.BaseDirectory;
//exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().GetModules()[0].FullyQualifiedName);
string[] fileNames = System.IO.Directory.GetFiles(exePath, "*.dll");
foreach (string fileName in fileNames) {
try {
//Load assembly into tmp domain rather than
//directly into current domain:
//assembly = System.Reflection.Assembly.LoadFrom(fileName);
string assemblyShortName =
System.IO.Path.GetFileNameWithoutExtension(fileName);
assembly = tmpDomain.Load(assemblyShortName);

type = assembly.GetType(typeName);
if (type != null) {
System.AppDomain.Unload(tmpDomain);
System.GC.Collect();
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
catch {
assembly = null;
}


}
return null;
}
















Nicholas Paldino said:
Sky,

There is no way around it, you are going to have to provide the assembly
name, and not just the type name. A type's identity is not simply the name
that it has, but also the module/assembly that it is located in.

For what reason are you looking for a particular type across ALL
assemblies? If you have a need for a specific type, why not have a well
known location (like a subdirectory) where you can load the assemblies and
scan for your type?


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Sky said:
I have been looking for a more powerful version of GetType(string) that
will
find the Type no matter what, and will work even if only supplied
"{TypeName}", not the full "{TypeName},{AssemblyName}"

As far as I know yet -- hence this question -- there is no 'one solution
fits all', but instead there are several parts that have to be put
together
to check.

What I have so far is, and would like as much feedback as possible to
ensure
I've done the best job possible is:

Step 1: Find a Type in the calling Assembly, or fallback to mscorlib:
GetType() looks for a class by that name in the calling Assembly, and then
falls back to search in mscorlib.
As you can imagine, this doesn't get us far.

Step 2: Looking in other Assemblies already loaded:
If not in either of these two, you have to fall back to using
AppDomain.GetAssemblies(), and then loop through each individually to test
each one with assembly.GetType(string).

Step 3: Looking in Assemblies not already loaded:
But these only refer to assemblies already loaded -- and will fail if the
assembly has not loaded yet, so you then have to Assembly.Load(pathName)
for
each assembly in the bin dir that ends with ".dll" and ".exe".

Step 4: Looking in the GAC:
The above solutions almost get us through everything, except there stilll
is
missing one place to look, the GAC. How can I get a list of assemblies in
the
GAC, to search through them as well?


Questions:
a) The Assembly.LoadFile() worries me a lot: how does it work, exactly? I
assume it does exactly as implied, and loads it up in memory -- so calling
it
at the beginning of a program could mean that *all* assemblies are loaded
in
memory right from the start, making for a slower startup... Calling and
loading all files in the GAC to search through them would end up with a
lot
of memory for assemblies that have nothing to do with the application,
right?

b) This all seems to be a very complex route, that even with caching for
later, could be a massive drain on system resources -- is there any
simpler
way?

c) GetAssemblies() is not available on CE...Is there *any* other way to
get
a list of assemblies loaded in memory other than AppDomain.GetAssemblies?

Thank you for any and all feedback, and pointers,
Sky

BTW: For anybody that is interested, the code for the above looks a bit
like:

private static Type LoadType(string typeName) {
System.Reflection.Assembly assembly;
return LoadType(typeName, out assembly);
}

private static Type LoadType(string typeName, out
System.Reflection.Assembly assemblyTypeFoundIn) {
if (string.IsNullOrEmpty(typeName)) {
throw new System.ArgumentNullException("typeName");
}
//Clear results first:
assemblyTypeFoundIn = null;
System.Reflection.Assembly assembly = null;
System.Type type = null;

type = System.Type.GetType(typeName, false, true);

if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}

#if (!PocketPC) && (!pocketPC) && (!WindowsCE)
foreach (System.Reflection.Assembly tmpAasembly in
System.AppDomain.CurrentDomain.GetAssemblies()) {
type = tmpAasembly.GetType(typeName);
if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}
}
#endif

//Get Path to Bin folder:
string exePath;
//exePath = System.AppDomain.CurrentDomain.BaseDirectory;
//exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().GetModules()[0].FullyQualifiedName);
string[] fileNames = System.IO.Directory.GetFiles(exePath, "*.dll");
foreach (string fileName in fileNames) {
try {
assembly = System.Reflection.Assembly.LoadFrom(fileName);

type = assembly.GetType(typeName);
if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
catch {
assembly = null;
}


}
return null;
}
 
N

Nicholas Paldino [.NET/C# MVP]

Sky,

See inline.
Actually it probably comes from my having had really frustrating
experiences
with app.configs/ConnectionSettings/Providers in general when I first
started
out. Seemed I could never get up and running without a lot of poking
around
due to very vague error messages, combined with one little spelling
mistake
in an app.Config.

Well, yes, but in the end, that's understandable. Computers can't
understand intent.
The whole thing set me off right from the start distrusting pointing to
assemblies with version and culture numbers: it seems to be way too
specific,
and causes me nothing but frustration.

It also just seems to me that the current assemby full names are *way* too
specific, whereas they should be in *most* cases 'loose', only resorting
to
specific culture/version to handle specific clashes.

In addition, I'm not sure that I even agree that a config file is the best
location for some of this identity inforation: I suspect that Culture
should
be handled via code choices of satellite resource assemblies,

How satellite resource assemblies are referenced is clearly laid out in
the framework documentation. This shouldn't be such a big issue. The
static CurrentCulture property on the Thread class is used to make a
decision from which assembly to load resources from.
But probably what worries me the most is the issue of version numbers --
hard to update a growing app (ie more assemblies) over the web without
having
to update the config file as well. And that is made very complicated when
the
app has
a) user settings in it,
b) is a modular type of software where other vendors may need to also have
settings in the app.config for their assemblies.

That's part of your problem there. You shouldn't be storing ^user^
configuration information in a file that is meant for ^application^
configuration. You should be handling user preferences in a separate file.

As for updating the config file, if your user settings aren't in there,
it shouldn't be such a big deal. Have you looked at ClickOnce for your
deployment scenario? It should help with this situation greatly.
Talking about that last piece a little bit more: it seems that the
app.config model is more suited for installation packages, and/or
ClickOnce
packages. But if I am correct, code is going to be more and more like
services...ie thin+ clients that start off with some modules, and as
needed
(or paid for...) or released, more modules are downloaded and installed,
that
are updatable much more fluidly (webservice+serializationofdlls+throw out
and
restart appdomain)...where the webconfig stuff will need to be updated for
every new module downloaded, and every version shift in the downloaded
modules.... Seems like this is very hard to do with current thinking...
I'm
REALLY looking for pointers/thoughts/articles on this type of thinking...

I don't think that your perspective on app.config is correct. It simply
is a way of specifying configuration details for your application, details
which could change over the course of the installation. It's a way of
providing a uniform interface to those configuration details.

ClickOnce has a separate manifest altogether.

The web.config file is for your service side only. If you roll out new
features on that end, then yes, you will have to update it (assuming there
is information in there that you need to access).
Then again, this point of view of mne all might be caused from to a
completly wrong understanding of how the whole thing fits together,
whereas a
fresh look/undestanding of how to use it all would sort it all out! :)
For
example, I'm one of the people who just doesn't get User Settings in an
app.config. Oh, sure I get the simplicity of typed access to
properties...but
saving user settings in an app.config seems to be exactly the same as
keeping
stuff intended for c:\Documents and Settings\Users\YourSettings under
c:\Programs Files\Your app.... Sounds like a very messed up way of doing
things, no?

I agree with you completely on this. The app.config file is not a place
that user settings should be set. Also, the idea of an app changing the
app.config file while it is running is not something I agree with either.
Hence, my struggling around to find a good solution: currently this leads
me
to believe that references to assemblies being 'loose', with short names,
and
not always the assembly it belongs in.

Which when I think about it...doesn't answer your question... Hum.

As for why I can't put the assembly name... That was your question. Is the
answer simply because I'm lazy? I know the type, but I don't always know
what
assembly it is in? Especially because I sometimes move things around from
one
assembly to another as I progress through coding and get clearer as to
where
it should go(eg: I start with a simple 1 assembly exe, and later make it
into
an multiple assembly application (UIL, BLL, DAL) and have to move code
that
was originally placed in UIL to BLL...
Which is not a good reason to not put in the assembly name as well. It
just
seems to indicate that I am lazy. Hum. I'll have to think about that.

Without it seeming like I am being chastising, you do need to rethink
that approach. A type is not only defined by its code, but by it's
location, which means the assembly name. You say you know the type, but if
you don't always know what assembly it is in, then you CAN'T know the type.

Also, here is a hint. If you use the static Load method on the Assembly
class, you don't have to always specify the complete name. You can specify
the assembly name without the public key, the culture, and the version, and
it will get the best match.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Sky


BTW: Solution to my question in the last post, about not using up all
resources:
...the solution is to use a secondary AppDomain to load the Assemblies
into,
that can then be dumped. I was stunned to find that one can get a Type out
of
an Assembly in a secondary domain, and then dump the secondary domain, and
use the Type in the first domain, without it bugging out due to the Type
pointing to a null address...I would have thought (limited experience with
AppDomains...) that it would somehow cause a type of memory exception
pointing into mem that is no longer available...even after a GC.Collect().
So there. It works. On desktop/full.... But not on CE because CE's
AppDomain
is stunted and doesn't have a Load(string) feature to do the same trick.
Anybody have an idea?

PPS: I am still looking for answers to find out what is in the GAC? And
the
equivalent to LoadAssembly(string) and GetAssemblies(string) on Compact
NET...anyone?


public static Type LoadType(string typeName, out
System.Reflection.Assembly
assemblyTypeFoundIn) {
if (string.IsNullOrEmpty(typeName)) {
throw new System.ArgumentNullException("typeName");
}
//Clear results first:
assemblyTypeFoundIn = null;
System.Reflection.Assembly assembly = null;
System.Type type = null;

type = System.Type.GetType(typeName, false, true);

if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}
//Create a second domain:
System.AppDomain currentAppDomain = System.AppDomain.CurrentDomain;
System.Security.Policy.Evidence evidence =
System.AppDomain.CurrentDomain.Evidence;
System.AppDomain secondDomain =
System.AppDomain.CreateDomain("typeLoader", evidence);
System.AppDomain tmpDomain = secondDomain;//currentAppDomain;
int tmpCount = currentAppDomain.ReflectionOnlyGetAssemblies().Length;
//zero?

//Get assemblies in tmp domain:
//suprisingly, this turned out to have same results
//as currentDomain -- I would have thought it would report back
//less assemblies. Nice.
System.Reflection.Assembly[] assemblies = tmpDomain.GetAssemblies();
int foundCount = assemblies.Length;

foreach (System.Reflection.Assembly tmpAasembly in assemblies) {
type = tmpAasembly.GetType(typeName);
if (type != null) {
System.AppDomain.Unload(tmpDomain);
System.GC.Collect();
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
tmpCount = currentAppDomain.ReflectionOnlyGetAssemblies().Length;

//Get Path to Bin folder:
string exePath;
//exePath = System.AppDomain.CurrentDomain.BaseDirectory;
//exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().GetModules()[0].FullyQualifiedName);
string[] fileNames = System.IO.Directory.GetFiles(exePath, "*.dll");
foreach (string fileName in fileNames) {
try {
//Load assembly into tmp domain rather than
//directly into current domain:
//assembly = System.Reflection.Assembly.LoadFrom(fileName);
string assemblyShortName =
System.IO.Path.GetFileNameWithoutExtension(fileName);
assembly = tmpDomain.Load(assemblyShortName);

type = assembly.GetType(typeName);
if (type != null) {
System.AppDomain.Unload(tmpDomain);
System.GC.Collect();
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
catch {
assembly = null;
}


}
return null;
}
















Nicholas Paldino said:
Sky,

There is no way around it, you are going to have to provide the
assembly
name, and not just the type name. A type's identity is not simply the
name
that it has, but also the module/assembly that it is located in.

For what reason are you looking for a particular type across ALL
assemblies? If you have a need for a specific type, why not have a well
known location (like a subdirectory) where you can load the assemblies
and
scan for your type?


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Sky said:
I have been looking for a more powerful version of GetType(string) that
will
find the Type no matter what, and will work even if only supplied
"{TypeName}", not the full "{TypeName},{AssemblyName}"

As far as I know yet -- hence this question -- there is no 'one
solution
fits all', but instead there are several parts that have to be put
together
to check.

What I have so far is, and would like as much feedback as possible to
ensure
I've done the best job possible is:

Step 1: Find a Type in the calling Assembly, or fallback to mscorlib:
GetType() looks for a class by that name in the calling Assembly, and
then
falls back to search in mscorlib.
As you can imagine, this doesn't get us far.

Step 2: Looking in other Assemblies already loaded:
If not in either of these two, you have to fall back to using
AppDomain.GetAssemblies(), and then loop through each individually to
test
each one with assembly.GetType(string).

Step 3: Looking in Assemblies not already loaded:
But these only refer to assemblies already loaded -- and will fail if
the
assembly has not loaded yet, so you then have to
Assembly.Load(pathName)
for
each assembly in the bin dir that ends with ".dll" and ".exe".

Step 4: Looking in the GAC:
The above solutions almost get us through everything, except there
stilll
is
missing one place to look, the GAC. How can I get a list of assemblies
in
the
GAC, to search through them as well?


Questions:
a) The Assembly.LoadFile() worries me a lot: how does it work, exactly?
I
assume it does exactly as implied, and loads it up in memory -- so
calling
it
at the beginning of a program could mean that *all* assemblies are
loaded
in
memory right from the start, making for a slower startup... Calling and
loading all files in the GAC to search through them would end up with a
lot
of memory for assemblies that have nothing to do with the application,
right?

b) This all seems to be a very complex route, that even with caching
for
later, could be a massive drain on system resources -- is there any
simpler
way?

c) GetAssemblies() is not available on CE...Is there *any* other way to
get
a list of assemblies loaded in memory other than
AppDomain.GetAssemblies?

Thank you for any and all feedback, and pointers,
Sky

BTW: For anybody that is interested, the code for the above looks a bit
like:

private static Type LoadType(string typeName) {
System.Reflection.Assembly assembly;
return LoadType(typeName, out assembly);
}

private static Type LoadType(string typeName, out
System.Reflection.Assembly assemblyTypeFoundIn) {
if (string.IsNullOrEmpty(typeName)) {
throw new System.ArgumentNullException("typeName");
}
//Clear results first:
assemblyTypeFoundIn = null;
System.Reflection.Assembly assembly = null;
System.Type type = null;

type = System.Type.GetType(typeName, false, true);

if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}

#if (!PocketPC) && (!pocketPC) && (!WindowsCE)
foreach (System.Reflection.Assembly tmpAasembly in
System.AppDomain.CurrentDomain.GetAssemblies()) {
type = tmpAasembly.GetType(typeName);
if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}
}
#endif

//Get Path to Bin folder:
string exePath;
//exePath = System.AppDomain.CurrentDomain.BaseDirectory;
//exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().GetModules()[0].FullyQualifiedName);
string[] fileNames = System.IO.Directory.GetFiles(exePath, "*.dll");
foreach (string fileName in fileNames) {
try {
assembly = System.Reflection.Assembly.LoadFrom(fileName);

type = assembly.GetType(typeName);
if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
catch {
assembly = null;
}


}
return null;
}
 
G

Guest

Hi Nicholas:

First:
<<Without it seeming like I am being chastising, you do need to rethink
that approach...>>
True. :) I think i just needed to beat it out in public to see the error of
my ways :) I am seeing more and more that the problem that I was looking for
a solution to is actually a problem that I was causing myself, due to the way
I was coding... Hum. Got it. Give TypeName + AssemblyName. (But don't need
Version or culture...)

Second:
WebConfig. Sorry...that was a typo. I meant App.Config: I clearly understand
that if one rolls out more modules, that the app.config has to be updated on
the clientside, and web.config is another kettle to be sorted out
independantly.

What I don't see clearly is the following way to handle scenarios such as
this one:
Imagine an app for PocketPC/Desktop that has a common plugin arch. You first
ship it with 5 modules. Turns out later than one set of clients pays for the
Extras and downloads 5 more modules, and another group even goes for Platinum
and downloads 5 more modules. Finally, we release 5 more that nobody seems
interested in. So now we have some clients with 5, some with 10, some with 15
assemblies/modules....which means some only need 5 Sections in app.config,
some 10, some 15. What do we do? Update all with the same config with info
about all modules they could be getting (5+5+5+10) or only what they each got
(5, 10, 15)?

It's just that updating the app.config on the fly, adding module
sectionhandlers and sections as being downloaded...freaks me out. What if
there is a write error half way through? I could blow out whole swaths of
users due one stupid update error...On the PocketPC, it would be a nightmare
to ask them to use pocket Notes to look at their app.config to see what
modules are defined...

Third:
You mentioned ClickOnce manifest as a possible way to go: I'm not too up to
speed on ClickOnce (I tried back in beta and it felt good for an app that
needed to be updated in one block, sending all modules in same package), but
not perfect for a download-as-you-need modular approach. Or did I miss
something that I could use here?

Fourth:
User Settings. Not in app.config. Very bad you say, which is very good :).
So why does MS push it? Plus, I'm getting a bit confused as to which one I am
suppossed to use! There was originally app.config in 1.1, now there are
*.settings files, that produce two sections (user/application).
App.settings are read only, and the other two are read/write -- I think.
Are we supposed to still use both? Or is app.settings depracated, and we
should concentrate on the later two?
Am I right that all three point to app.settings, not another file...
You mentioned using external files to save user settings....how with these
files? Or should we be rolling our own storage system?

Fifth:
Better ask before taking this for granted: you say user settings should be
in secondary file...what about application settings? In app.config, or an
exterior one too, and just use app.config for wiring stuff
(sectionhandlers->Providers).


Thanks!
Sky



Nicholas Paldino said:
Sky,

See inline.
Actually it probably comes from my having had really frustrating
experiences
with app.configs/ConnectionSettings/Providers in general when I first
started
out. Seemed I could never get up and running without a lot of poking
around
due to very vague error messages, combined with one little spelling
mistake
in an app.Config.

Well, yes, but in the end, that's understandable. Computers can't
understand intent.
The whole thing set me off right from the start distrusting pointing to
assemblies with version and culture numbers: it seems to be way too
specific,
and causes me nothing but frustration.

It also just seems to me that the current assemby full names are *way* too
specific, whereas they should be in *most* cases 'loose', only resorting
to
specific culture/version to handle specific clashes.

In addition, I'm not sure that I even agree that a config file is the best
location for some of this identity inforation: I suspect that Culture
should
be handled via code choices of satellite resource assemblies,

How satellite resource assemblies are referenced is clearly laid out in
the framework documentation. This shouldn't be such a big issue. The
static CurrentCulture property on the Thread class is used to make a
decision from which assembly to load resources from.
But probably what worries me the most is the issue of version numbers --
hard to update a growing app (ie more assemblies) over the web without
having
to update the config file as well. And that is made very complicated when
the
app has
a) user settings in it,
b) is a modular type of software where other vendors may need to also have
settings in the app.config for their assemblies.

That's part of your problem there. You shouldn't be storing ^user^
configuration information in a file that is meant for ^application^
configuration. You should be handling user preferences in a separate file.

As for updating the config file, if your user settings aren't in there,
it shouldn't be such a big deal. Have you looked at ClickOnce for your
deployment scenario? It should help with this situation greatly.
Talking about that last piece a little bit more: it seems that the
app.config model is more suited for installation packages, and/or
ClickOnce
packages. But if I am correct, code is going to be more and more like
services...ie thin+ clients that start off with some modules, and as
needed
(or paid for...) or released, more modules are downloaded and installed,
that
are updatable much more fluidly (webservice+serializationofdlls+throw out
and
restart appdomain)...where the webconfig stuff will need to be updated for
every new module downloaded, and every version shift in the downloaded
modules.... Seems like this is very hard to do with current thinking...
I'm
REALLY looking for pointers/thoughts/articles on this type of thinking...

I don't think that your perspective on app.config is correct. It simply
is a way of specifying configuration details for your application, details
which could change over the course of the installation. It's a way of
providing a uniform interface to those configuration details.

ClickOnce has a separate manifest altogether.

The web.config file is for your service side only. If you roll out new
features on that end, then yes, you will have to update it (assuming there
is information in there that you need to access).
Then again, this point of view of mne all might be caused from to a
completly wrong understanding of how the whole thing fits together,
whereas a
fresh look/undestanding of how to use it all would sort it all out! :)
For
example, I'm one of the people who just doesn't get User Settings in an
app.config. Oh, sure I get the simplicity of typed access to
properties...but
saving user settings in an app.config seems to be exactly the same as
keeping
stuff intended for c:\Documents and Settings\Users\YourSettings under
c:\Programs Files\Your app.... Sounds like a very messed up way of doing
things, no?

I agree with you completely on this. The app.config file is not a place
that user settings should be set. Also, the idea of an app changing the
app.config file while it is running is not something I agree with either.
Hence, my struggling around to find a good solution: currently this leads
me
to believe that references to assemblies being 'loose', with short names,
and
not always the assembly it belongs in.

Which when I think about it...doesn't answer your question... Hum.

As for why I can't put the assembly name... That was your question. Is the
answer simply because I'm lazy? I know the type, but I don't always know
what
assembly it is in? Especially because I sometimes move things around from
one
assembly to another as I progress through coding and get clearer as to
where
it should go(eg: I start with a simple 1 assembly exe, and later make it
into
an multiple assembly application (UIL, BLL, DAL) and have to move code
that
was originally placed in UIL to BLL...
Which is not a good reason to not put in the assembly name as well. It
just
seems to indicate that I am lazy. Hum. I'll have to think about that.

Without it seeming like I am being chastising, you do need to rethink
that approach. A type is not only defined by its code, but by it's
location, which means the assembly name. You say you know the type, but if
you don't always know what assembly it is in, then you CAN'T know the type.

Also, here is a hint. If you use the static Load method on the Assembly
class, you don't have to always specify the complete name. You can specify
the assembly name without the public key, the culture, and the version, and
it will get the best match.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Sky


BTW: Solution to my question in the last post, about not using up all
resources:
...the solution is to use a secondary AppDomain to load the Assemblies
into,
that can then be dumped. I was stunned to find that one can get a Type out
of
an Assembly in a secondary domain, and then dump the secondary domain, and
use the Type in the first domain, without it bugging out due to the Type
pointing to a null address...I would have thought (limited experience with
AppDomains...) that it would somehow cause a type of memory exception
pointing into mem that is no longer available...even after a GC.Collect().
So there. It works. On desktop/full.... But not on CE because CE's
AppDomain
is stunted and doesn't have a Load(string) feature to do the same trick.
Anybody have an idea?

PPS: I am still looking for answers to find out what is in the GAC? And
the
equivalent to LoadAssembly(string) and GetAssemblies(string) on Compact
NET...anyone?


public static Type LoadType(string typeName, out
System.Reflection.Assembly
assemblyTypeFoundIn) {
if (string.IsNullOrEmpty(typeName)) {
throw new System.ArgumentNullException("typeName");
}
//Clear results first:
assemblyTypeFoundIn = null;
System.Reflection.Assembly assembly = null;
System.Type type = null;

type = System.Type.GetType(typeName, false, true);

if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}
//Create a second domain:
System.AppDomain currentAppDomain = System.AppDomain.CurrentDomain;
System.Security.Policy.Evidence evidence =
System.AppDomain.CurrentDomain.Evidence;
System.AppDomain secondDomain =
System.AppDomain.CreateDomain("typeLoader", evidence);
System.AppDomain tmpDomain = secondDomain;//currentAppDomain;
int tmpCount = currentAppDomain.ReflectionOnlyGetAssemblies().Length;
//zero?

//Get assemblies in tmp domain:
//suprisingly, this turned out to have same results
//as currentDomain -- I would have thought it would report back
//less assemblies. Nice.
System.Reflection.Assembly[] assemblies = tmpDomain.GetAssemblies();
int foundCount = assemblies.Length;

foreach (System.Reflection.Assembly tmpAasembly in assemblies) {
type = tmpAasembly.GetType(typeName);
if (type != null) {
System.AppDomain.Unload(tmpDomain);
System.GC.Collect();
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
tmpCount = currentAppDomain.ReflectionOnlyGetAssemblies().Length;

//Get Path to Bin folder:
string exePath;
//exePath = System.AppDomain.CurrentDomain.BaseDirectory;
//exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().GetModules()[0].FullyQualifiedName);
string[] fileNames = System.IO.Directory.GetFiles(exePath, "*.dll");
foreach (string fileName in fileNames) {
try {
//Load assembly into tmp domain rather than
//directly into current domain:
//assembly = System.Reflection.Assembly.LoadFrom(fileName);
string assemblyShortName =
System.IO.Path.GetFileNameWithoutExtension(fileName);
assembly = tmpDomain.Load(assemblyShortName);

type = assembly.GetType(typeName);
if (type != null) {
System.AppDomain.Unload(tmpDomain);
System.GC.Collect();
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
catch {
assembly = null;
}


}
return null;
}
















Nicholas Paldino said:
Sky,

There is no way around it, you are going to have to provide the
assembly
name, and not just the type name. A type's identity is not simply the
name
that it has, but also the module/assembly that it is located in.

For what reason are you looking for a particular type across ALL
assemblies? If you have a need for a specific type, why not have a well
known location (like a subdirectory) where you can load the assemblies
and
scan for your type?


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

"Sky" <public.skysigal{*AT*}xact-solutions.com> wrote in message
I have been looking for a more powerful version of GetType(string) that
will
find the Type no matter what, and will work even if only supplied
"{TypeName}", not the full "{TypeName},{AssemblyName}"

As far as I know yet -- hence this question -- there is no 'one
solution
fits all', but instead there are several parts that have to be put
together
to check.

What I have so far is, and would like as much feedback as possible to
ensure
I've done the best job possible is:
 
G

Guest

While I'm searching for answers to the questions I posted in last post,
concerning
app.settings, user and application settings, where to save them, how to
access them, etc, I came across the following blog which seems to cover the
most terrain:

http://geekswithblogs.net/akraus1/articles/64871.aspx

I'm still digesting it, trying to see how best to put it to use, but in the
mean-time I'm posting it anycase it is of use to anybody else as well who is
looking for info on settings....


Nicholas Paldino said:
Sky,

See inline.
Actually it probably comes from my having had really frustrating
experiences
with app.configs/ConnectionSettings/Providers in general when I first
started
out. Seemed I could never get up and running without a lot of poking
around
due to very vague error messages, combined with one little spelling
mistake
in an app.Config.

Well, yes, but in the end, that's understandable. Computers can't
understand intent.
The whole thing set me off right from the start distrusting pointing to
assemblies with version and culture numbers: it seems to be way too
specific,
and causes me nothing but frustration.

It also just seems to me that the current assemby full names are *way* too
specific, whereas they should be in *most* cases 'loose', only resorting
to
specific culture/version to handle specific clashes.

In addition, I'm not sure that I even agree that a config file is the best
location for some of this identity inforation: I suspect that Culture
should
be handled via code choices of satellite resource assemblies,

How satellite resource assemblies are referenced is clearly laid out in
the framework documentation. This shouldn't be such a big issue. The
static CurrentCulture property on the Thread class is used to make a
decision from which assembly to load resources from.
But probably what worries me the most is the issue of version numbers --
hard to update a growing app (ie more assemblies) over the web without
having
to update the config file as well. And that is made very complicated when
the
app has
a) user settings in it,
b) is a modular type of software where other vendors may need to also have
settings in the app.config for their assemblies.

That's part of your problem there. You shouldn't be storing ^user^
configuration information in a file that is meant for ^application^
configuration. You should be handling user preferences in a separate file.

As for updating the config file, if your user settings aren't in there,
it shouldn't be such a big deal. Have you looked at ClickOnce for your
deployment scenario? It should help with this situation greatly.
Talking about that last piece a little bit more: it seems that the
app.config model is more suited for installation packages, and/or
ClickOnce
packages. But if I am correct, code is going to be more and more like
services...ie thin+ clients that start off with some modules, and as
needed
(or paid for...) or released, more modules are downloaded and installed,
that
are updatable much more fluidly (webservice+serializationofdlls+throw out
and
restart appdomain)...where the webconfig stuff will need to be updated for
every new module downloaded, and every version shift in the downloaded
modules.... Seems like this is very hard to do with current thinking...
I'm
REALLY looking for pointers/thoughts/articles on this type of thinking...

I don't think that your perspective on app.config is correct. It simply
is a way of specifying configuration details for your application, details
which could change over the course of the installation. It's a way of
providing a uniform interface to those configuration details.

ClickOnce has a separate manifest altogether.

The web.config file is for your service side only. If you roll out new
features on that end, then yes, you will have to update it (assuming there
is information in there that you need to access).
Then again, this point of view of mne all might be caused from to a
completly wrong understanding of how the whole thing fits together,
whereas a
fresh look/undestanding of how to use it all would sort it all out! :)
For
example, I'm one of the people who just doesn't get User Settings in an
app.config. Oh, sure I get the simplicity of typed access to
properties...but
saving user settings in an app.config seems to be exactly the same as
keeping
stuff intended for c:\Documents and Settings\Users\YourSettings under
c:\Programs Files\Your app.... Sounds like a very messed up way of doing
things, no?

I agree with you completely on this. The app.config file is not a place
that user settings should be set. Also, the idea of an app changing the
app.config file while it is running is not something I agree with either.
Hence, my struggling around to find a good solution: currently this leads
me
to believe that references to assemblies being 'loose', with short names,
and
not always the assembly it belongs in.

Which when I think about it...doesn't answer your question... Hum.

As for why I can't put the assembly name... That was your question. Is the
answer simply because I'm lazy? I know the type, but I don't always know
what
assembly it is in? Especially because I sometimes move things around from
one
assembly to another as I progress through coding and get clearer as to
where
it should go(eg: I start with a simple 1 assembly exe, and later make it
into
an multiple assembly application (UIL, BLL, DAL) and have to move code
that
was originally placed in UIL to BLL...
Which is not a good reason to not put in the assembly name as well. It
just
seems to indicate that I am lazy. Hum. I'll have to think about that.

Without it seeming like I am being chastising, you do need to rethink
that approach. A type is not only defined by its code, but by it's
location, which means the assembly name. You say you know the type, but if
you don't always know what assembly it is in, then you CAN'T know the type.

Also, here is a hint. If you use the static Load method on the Assembly
class, you don't have to always specify the complete name. You can specify
the assembly name without the public key, the culture, and the version, and
it will get the best match.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Sky


BTW: Solution to my question in the last post, about not using up all
resources:
...the solution is to use a secondary AppDomain to load the Assemblies
into,
that can then be dumped. I was stunned to find that one can get a Type out
of
an Assembly in a secondary domain, and then dump the secondary domain, and
use the Type in the first domain, without it bugging out due to the Type
pointing to a null address...I would have thought (limited experience with
AppDomains...) that it would somehow cause a type of memory exception
pointing into mem that is no longer available...even after a GC.Collect().
So there. It works. On desktop/full.... But not on CE because CE's
AppDomain
is stunted and doesn't have a Load(string) feature to do the same trick.
Anybody have an idea?

PPS: I am still looking for answers to find out what is in the GAC? And
the
equivalent to LoadAssembly(string) and GetAssemblies(string) on Compact
NET...anyone?


public static Type LoadType(string typeName, out
System.Reflection.Assembly
assemblyTypeFoundIn) {
if (string.IsNullOrEmpty(typeName)) {
throw new System.ArgumentNullException("typeName");
}
//Clear results first:
assemblyTypeFoundIn = null;
System.Reflection.Assembly assembly = null;
System.Type type = null;

type = System.Type.GetType(typeName, false, true);

if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}
//Create a second domain:
System.AppDomain currentAppDomain = System.AppDomain.CurrentDomain;
System.Security.Policy.Evidence evidence =
System.AppDomain.CurrentDomain.Evidence;
System.AppDomain secondDomain =
System.AppDomain.CreateDomain("typeLoader", evidence);
System.AppDomain tmpDomain = secondDomain;//currentAppDomain;
int tmpCount = currentAppDomain.ReflectionOnlyGetAssemblies().Length;
//zero?

//Get assemblies in tmp domain:
//suprisingly, this turned out to have same results
//as currentDomain -- I would have thought it would report back
//less assemblies. Nice.
System.Reflection.Assembly[] assemblies = tmpDomain.GetAssemblies();
int foundCount = assemblies.Length;

foreach (System.Reflection.Assembly tmpAasembly in assemblies) {
type = tmpAasembly.GetType(typeName);
if (type != null) {
System.AppDomain.Unload(tmpDomain);
System.GC.Collect();
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
tmpCount = currentAppDomain.ReflectionOnlyGetAssemblies().Length;

//Get Path to Bin folder:
string exePath;
//exePath = System.AppDomain.CurrentDomain.BaseDirectory;
//exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().GetModules()[0].FullyQualifiedName);
string[] fileNames = System.IO.Directory.GetFiles(exePath, "*.dll");
foreach (string fileName in fileNames) {
try {
//Load assembly into tmp domain rather than
//directly into current domain:
//assembly = System.Reflection.Assembly.LoadFrom(fileName);
string assemblyShortName =
System.IO.Path.GetFileNameWithoutExtension(fileName);
assembly = tmpDomain.Load(assemblyShortName);

type = assembly.GetType(typeName);
if (type != null) {
System.AppDomain.Unload(tmpDomain);
System.GC.Collect();
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
catch {
assembly = null;
}


}
return null;
}
















Nicholas Paldino said:
Sky,

There is no way around it, you are going to have to provide the
assembly
name, and not just the type name. A type's identity is not simply the
name
that it has, but also the module/assembly that it is located in.

For what reason are you looking for a particular type across ALL
assemblies? If you have a need for a specific type, why not have a well
known location (like a subdirectory) where you can load the assemblies
and
scan for your type?


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

"Sky" <public.skysigal{*AT*}xact-solutions.com> wrote in message
I have been looking for a more powerful version of GetType(string) that
will
find the Type no matter what, and will work even if only supplied
"{TypeName}", not the full "{TypeName},{AssemblyName}"

As far as I know yet -- hence this question -- there is no 'one
solution
fits all', but instead there are several parts that have to be put
together
to check.

What I have so far is, and would like as much feedback as possible to
ensure
I've done the best job possible is:
 
G

Guest

Another quick post about good info I've found while trying to clear this all
up:

An essential blog entry to make things clear about how appSettings,
userSettings, and applicationSettings work in NET 2.0

http://blogs.msdn.com/rprabhu/articles/433979.aspx




Nicholas Paldino said:
Sky,

See inline.
Actually it probably comes from my having had really frustrating
experiences
with app.configs/ConnectionSettings/Providers in general when I first
started
out. Seemed I could never get up and running without a lot of poking
around
due to very vague error messages, combined with one little spelling
mistake
in an app.Config.

Well, yes, but in the end, that's understandable. Computers can't
understand intent.
The whole thing set me off right from the start distrusting pointing to
assemblies with version and culture numbers: it seems to be way too
specific,
and causes me nothing but frustration.

It also just seems to me that the current assemby full names are *way* too
specific, whereas they should be in *most* cases 'loose', only resorting
to
specific culture/version to handle specific clashes.

In addition, I'm not sure that I even agree that a config file is the best
location for some of this identity inforation: I suspect that Culture
should
be handled via code choices of satellite resource assemblies,

How satellite resource assemblies are referenced is clearly laid out in
the framework documentation. This shouldn't be such a big issue. The
static CurrentCulture property on the Thread class is used to make a
decision from which assembly to load resources from.
But probably what worries me the most is the issue of version numbers --
hard to update a growing app (ie more assemblies) over the web without
having
to update the config file as well. And that is made very complicated when
the
app has
a) user settings in it,
b) is a modular type of software where other vendors may need to also have
settings in the app.config for their assemblies.

That's part of your problem there. You shouldn't be storing ^user^
configuration information in a file that is meant for ^application^
configuration. You should be handling user preferences in a separate file.

As for updating the config file, if your user settings aren't in there,
it shouldn't be such a big deal. Have you looked at ClickOnce for your
deployment scenario? It should help with this situation greatly.
Talking about that last piece a little bit more: it seems that the
app.config model is more suited for installation packages, and/or
ClickOnce
packages. But if I am correct, code is going to be more and more like
services...ie thin+ clients that start off with some modules, and as
needed
(or paid for...) or released, more modules are downloaded and installed,
that
are updatable much more fluidly (webservice+serializationofdlls+throw out
and
restart appdomain)...where the webconfig stuff will need to be updated for
every new module downloaded, and every version shift in the downloaded
modules.... Seems like this is very hard to do with current thinking...
I'm
REALLY looking for pointers/thoughts/articles on this type of thinking...

I don't think that your perspective on app.config is correct. It simply
is a way of specifying configuration details for your application, details
which could change over the course of the installation. It's a way of
providing a uniform interface to those configuration details.

ClickOnce has a separate manifest altogether.

The web.config file is for your service side only. If you roll out new
features on that end, then yes, you will have to update it (assuming there
is information in there that you need to access).
Then again, this point of view of mne all might be caused from to a
completly wrong understanding of how the whole thing fits together,
whereas a
fresh look/undestanding of how to use it all would sort it all out! :)
For
example, I'm one of the people who just doesn't get User Settings in an
app.config. Oh, sure I get the simplicity of typed access to
properties...but
saving user settings in an app.config seems to be exactly the same as
keeping
stuff intended for c:\Documents and Settings\Users\YourSettings under
c:\Programs Files\Your app.... Sounds like a very messed up way of doing
things, no?

I agree with you completely on this. The app.config file is not a place
that user settings should be set. Also, the idea of an app changing the
app.config file while it is running is not something I agree with either.
Hence, my struggling around to find a good solution: currently this leads
me
to believe that references to assemblies being 'loose', with short names,
and
not always the assembly it belongs in.

Which when I think about it...doesn't answer your question... Hum.

As for why I can't put the assembly name... That was your question. Is the
answer simply because I'm lazy? I know the type, but I don't always know
what
assembly it is in? Especially because I sometimes move things around from
one
assembly to another as I progress through coding and get clearer as to
where
it should go(eg: I start with a simple 1 assembly exe, and later make it
into
an multiple assembly application (UIL, BLL, DAL) and have to move code
that
was originally placed in UIL to BLL...
Which is not a good reason to not put in the assembly name as well. It
just
seems to indicate that I am lazy. Hum. I'll have to think about that.

Without it seeming like I am being chastising, you do need to rethink
that approach. A type is not only defined by its code, but by it's
location, which means the assembly name. You say you know the type, but if
you don't always know what assembly it is in, then you CAN'T know the type.

Also, here is a hint. If you use the static Load method on the Assembly
class, you don't have to always specify the complete name. You can specify
the assembly name without the public key, the culture, and the version, and
it will get the best match.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Sky


BTW: Solution to my question in the last post, about not using up all
resources:
...the solution is to use a secondary AppDomain to load the Assemblies
into,
that can then be dumped. I was stunned to find that one can get a Type out
of
an Assembly in a secondary domain, and then dump the secondary domain, and
use the Type in the first domain, without it bugging out due to the Type
pointing to a null address...I would have thought (limited experience with
AppDomains...) that it would somehow cause a type of memory exception
pointing into mem that is no longer available...even after a GC.Collect().
So there. It works. On desktop/full.... But not on CE because CE's
AppDomain
is stunted and doesn't have a Load(string) feature to do the same trick.
Anybody have an idea?

PPS: I am still looking for answers to find out what is in the GAC? And
the
equivalent to LoadAssembly(string) and GetAssemblies(string) on Compact
NET...anyone?


public static Type LoadType(string typeName, out
System.Reflection.Assembly
assemblyTypeFoundIn) {
if (string.IsNullOrEmpty(typeName)) {
throw new System.ArgumentNullException("typeName");
}
//Clear results first:
assemblyTypeFoundIn = null;
System.Reflection.Assembly assembly = null;
System.Type type = null;

type = System.Type.GetType(typeName, false, true);

if (type != null) {
assemblyTypeFoundIn = type.Assembly;
return type;
}
//Create a second domain:
System.AppDomain currentAppDomain = System.AppDomain.CurrentDomain;
System.Security.Policy.Evidence evidence =
System.AppDomain.CurrentDomain.Evidence;
System.AppDomain secondDomain =
System.AppDomain.CreateDomain("typeLoader", evidence);
System.AppDomain tmpDomain = secondDomain;//currentAppDomain;
int tmpCount = currentAppDomain.ReflectionOnlyGetAssemblies().Length;
//zero?

//Get assemblies in tmp domain:
//suprisingly, this turned out to have same results
//as currentDomain -- I would have thought it would report back
//less assemblies. Nice.
System.Reflection.Assembly[] assemblies = tmpDomain.GetAssemblies();
int foundCount = assemblies.Length;

foreach (System.Reflection.Assembly tmpAasembly in assemblies) {
type = tmpAasembly.GetType(typeName);
if (type != null) {
System.AppDomain.Unload(tmpDomain);
System.GC.Collect();
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
tmpCount = currentAppDomain.ReflectionOnlyGetAssemblies().Length;

//Get Path to Bin folder:
string exePath;
//exePath = System.AppDomain.CurrentDomain.BaseDirectory;
//exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
exePath =
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().GetModules()[0].FullyQualifiedName);
string[] fileNames = System.IO.Directory.GetFiles(exePath, "*.dll");
foreach (string fileName in fileNames) {
try {
//Load assembly into tmp domain rather than
//directly into current domain:
//assembly = System.Reflection.Assembly.LoadFrom(fileName);
string assemblyShortName =
System.IO.Path.GetFileNameWithoutExtension(fileName);
assembly = tmpDomain.Load(assemblyShortName);

type = assembly.GetType(typeName);
if (type != null) {
System.AppDomain.Unload(tmpDomain);
System.GC.Collect();
assemblyTypeFoundIn = type.Assembly;
return type;
}

}
catch {
assembly = null;
}


}
return null;
}
















Nicholas Paldino said:
Sky,

There is no way around it, you are going to have to provide the
assembly
name, and not just the type name. A type's identity is not simply the
name
that it has, but also the module/assembly that it is located in.

For what reason are you looking for a particular type across ALL
assemblies? If you have a need for a specific type, why not have a well
known location (like a subdirectory) where you can load the assemblies
and
scan for your type?


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

"Sky" <public.skysigal{*AT*}xact-solutions.com> wrote in message
I have been looking for a more powerful version of GetType(string) that
will
find the Type no matter what, and will work even if only supplied
"{TypeName}", not the full "{TypeName},{AssemblyName}"

As far as I know yet -- hence this question -- there is no 'one
solution
fits all', but instead there are several parts that have to be put
together
to check.

What I have so far is, and would like as much feedback as possible to
ensure
I've done the best job possible is:
 
J

Jay B. Harlow [MVP - Outlook]

Sky,
PMFJI

|I have been looking for a more powerful version of GetType(string) that
will
| find the Type no matter what, and will work even if only supplied
| "{TypeName}", not the full "{TypeName},{AssemblyName}"

You mean something like the BuildManager.GetType in ASP.NET 2.0?

http://msdn2.microsoft.com/en-us/library/a0z5yws8.aspx


--
Hope this helps
Jay B. Harlow [MVP - Outlook]
..NET Application Architect, Enthusiast, & Evangelist
T.S. Bradley - http://www.tsbradley.net


"Sky" <public.skysigal{*AT*}xact-solutions.com> wrote in message
|I have been looking for a more powerful version of GetType(string) that
will
| find the Type no matter what, and will work even if only supplied
| "{TypeName}", not the full "{TypeName},{AssemblyName}"
|
| As far as I know yet -- hence this question -- there is no 'one solution
| fits all', but instead there are several parts that have to be put
together
| to check.
|
| What I have so far is, and would like as much feedback as possible to
ensure
| I've done the best job possible is:
|
| Step 1: Find a Type in the calling Assembly, or fallback to mscorlib:
| GetType() looks for a class by that name in the calling Assembly, and then
| falls back to search in mscorlib.
| As you can imagine, this doesn't get us far.
|
| Step 2: Looking in other Assemblies already loaded:
| If not in either of these two, you have to fall back to using
| AppDomain.GetAssemblies(), and then loop through each individually to test
| each one with assembly.GetType(string).
|
| Step 3: Looking in Assemblies not already loaded:
| But these only refer to assemblies already loaded -- and will fail if the
| assembly has not loaded yet, so you then have to Assembly.Load(pathName)
for
| each assembly in the bin dir that ends with ".dll" and ".exe".
|
| Step 4: Looking in the GAC:
| The above solutions almost get us through everything, except there stilll
is
| missing one place to look, the GAC. How can I get a list of assemblies in
the
| GAC, to search through them as well?
|
|
| Questions:
| a) The Assembly.LoadFile() worries me a lot: how does it work, exactly? I
| assume it does exactly as implied, and loads it up in memory -- so calling
it
| at the beginning of a program could mean that *all* assemblies are loaded
in
| memory right from the start, making for a slower startup... Calling and
| loading all files in the GAC to search through them would end up with a
lot
| of memory for assemblies that have nothing to do with the application,
right?
|
| b) This all seems to be a very complex route, that even with caching for
| later, could be a massive drain on system resources -- is there any
simpler
| way?
|
| c) GetAssemblies() is not available on CE...Is there *any* other way to
get
| a list of assemblies loaded in memory other than AppDomain.GetAssemblies?
|
| Thank you for any and all feedback, and pointers,
| Sky
|
| BTW: For anybody that is interested, the code for the above looks a bit
like:
|
| private static Type LoadType(string typeName) {
| System.Reflection.Assembly assembly;
| return LoadType(typeName, out assembly);
| }
|
| private static Type LoadType(string typeName, out
| System.Reflection.Assembly assemblyTypeFoundIn) {
| if (string.IsNullOrEmpty(typeName)) {
| throw new System.ArgumentNullException("typeName");
| }
| //Clear results first:
| assemblyTypeFoundIn = null;
| System.Reflection.Assembly assembly = null;
| System.Type type = null;
|
| type = System.Type.GetType(typeName, false, true);
|
| if (type != null) {
| assemblyTypeFoundIn = type.Assembly;
| return type;
| }
|
| #if (!PocketPC) && (!pocketPC) && (!WindowsCE)
| foreach (System.Reflection.Assembly tmpAasembly in
| System.AppDomain.CurrentDomain.GetAssemblies()) {
| type = tmpAasembly.GetType(typeName);
| if (type != null) {
| assemblyTypeFoundIn = type.Assembly;
| return type;
| }
| }
| #endif
|
| //Get Path to Bin folder:
| string exePath;
| //exePath = System.AppDomain.CurrentDomain.BaseDirectory;
| //exePath =
|
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
| exePath =
|
System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetCallingAssembly().GetModules()[0].FullyQualifiedName);
| string[] fileNames = System.IO.Directory.GetFiles(exePath, "*.dll");
| foreach (string fileName in fileNames) {
| try {
| assembly = System.Reflection.Assembly.LoadFrom(fileName);
|
| type = assembly.GetType(typeName);
| if (type != null) {
| assemblyTypeFoundIn = type.Assembly;
| return type;
| }
|
| }
| catch {
| assembly = null;
| }
|
|
| }
| return null;
| }
|
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top