Avoid creating dummy variable to force a .NET assembly to load - c#

Background:
I have a .NET 4.5 application that uses Enterprise Library 6.0 to connect to an Oracle Database. In order to use the Oracle Data Provider for .NET, you have use EntLibContrib.Data.OdpNet.
I have a separate project and solution that contains the business and data access logic to connect to the database and returns domain objects.
Issue:
I went to use the assemblies in another project (in another solution) and ran into issues. I had added references to the following:
The .NET dlls produced by building the other solution
Microsoft.Practices.EnterpriseLibrary.Common
Microsoft.Practices.EnterpriseLibrary.Data
EntLibContrib.Data.OdpNet
After adding the appropriate settings to the configuration it should have worked as it has in other projects -- but when I tried to connect to the database, I received the following error:
The type 'EntLibContrib.Data.OdpNet.OracleDatabase,
EntLibContrib.Data.OdpNet' cannot be resolved. Please verify the
spelling is correct or that the full type name is provided.
I created a question for this error a while back. I never got a response, but the issue seem to "fix itself" when I added additional config information, but I never dug into what was causing the issue.
Since I ran into this issue again, I investigated and narrowed it that I need to have reference to an object that was part of EntLibContrib.Data.OdpNet. It also works if I have a reference an object that references references an object that is part of EntLibContrib.Data.OdpNet.
My solution was simply to do write a dummy variable in a class of my new project:
private static EntLibContrib.Data.OdpNet.OracleDataReaderWrapper dummyVarNotUsed;
Even though dummyVarNotUsed is never used, simply having that line allows the EntLibContrib.Data.OdpNet assembly to be referenced correctly.
This is a hack, could someone shed some light on what is happening and how to better reference the dll?

Based off of this question, and the associated answer, I'd suggest trying to handle the AssemblyResolve event, so something like this:
//in your startup method
AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
//...
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
//not sure if this will be what the name is, you'd have to play with it
if (args.Name == "EntLibContrib.Data.OdpNet.OracleDatabase")
{
return typeof(EntLibContrib.Data.OdpNet.OracleDataReaderWrapper).Assembly;
}
//not sure if this is best practice or not (to return null if truly unknown)
return null;
}
That answer does suggest your current solution as the preferred method, but I agree that it does feel hacky. Not sure if this other method will feel any less hacky to you; it does to me in that at least this way you can clearly document this event handler instead of having a bogus variable somewhere with comments like //DON'T REMOVE, VERY IMPORTANT!!!

Related

How do I use a type with the same name of another type in another referenced assembly?

I have the absurd situation (don't blame me, it is third party software) where I need to have two references (Erp.Contracts.BO.Quote and Erp.Contracts.BO.SalesOrder), but the type Erp.Tablesets.QuoteQtyRow is defined in both assemblies!
How do I use them in code?
void Absurdity()
{
Erp.Tablesets.QuoteQtyRow qqr_Quote = null; //<-- my intention is to use the one from the quote assembly here.
Erp.Tablesets.QuoteQtyRow qqr_SO = null; //<-- my intention is to use the one from the sales order assembly here.
}
The compiler throws an error. Namely: "The type 'Erp.Tablesets.QuoteQtyRow' exists in both assemblies."
EDIT: LIMITATIONS:
I do not have the flexibility of using an extern alias as provided in this answer Class with same name in two assemblies (intentionally). I am limited by the environment supplied by the third party software. I essentially need a way to make the distinction within the body of a method.
I understand I can avoid this problem altogether by using the dynamic keyword, but I am looking for a possible strongly typed solution.
There may not be a solution, but I want to exhaust all my resources before I give up on the problem.
Epicor ERP uses a tool to put together tables from the DB into datasets, and then on into Business objects. This business object is described in the contract assembly, but as you have found when you use two business object that references the same table you run into problems. This is more commonly an issue with the SerialNumber tables.
I understand from your notes that you are providing method body code in a Method Directive or data Directive within the Epicor ERP application. This is entered on the client and stored in the database but generates code on the server in the Deployment\Server\BPM\Sources\BO folder and is compiled to the Deployment\Server\Customization\BO folder.
There is no way to specify an alias for the referenced DLL in the "Execute Custom Code" workflow item of the BPM designer. The fix is requested in SCR 148549. There is no project file for you to edit, and even if there was every time the BPM was enabled and disabled it would be regenerated.
However, if you use the "Invoke External Method" workflow item, then you can build your own dll and put it in the Deployment\Server\Customization\Externals folder. To do that:
Click Actions > Create Programming Interfaces for your method in Method Directive Maintenance for your BPM and copy the code.
Create a new Class library project in Visual Studio
Paste the copied code into the .cs file
Add Assemblies - Framework references:
System.Data.Entity
System.ServiceModel
System.Transactions
Add file references to
Bin\Epicor.ServiceModel.dll
Assemblies\Epicor.Ice.dll
Assemblies\Epicor.System.dll
Assemblies\Ice.Data.Model.dll
Assemblies\Erp.Data.910100.dll
And add a reference for the BPM's BO i.e.
Assemblies\Erp.Contracts.BO.Quote.dll
Ensure all the references have Copy Local set to false.
Inherit from Ice.ContextBoundBase<Erp.ErpContext>
Add a constructor that takes a context public MyQuote (Erp.ErpContext ctx) : base(ctx){ }
You can't quite copy and paste the "Execute Custom Code" body in as you won't have access to the tt row variables, these are all in the ds.

Result of "is" expression returns false when run, but true when inspected

I have the following code. The CustomControlHelper generates an instance of an object via reflection. At this stage we don't know what type of object we are dealing with. We do know it will be a CustomControl, but we don't know if it implements any particular interface or if it extends any other classes. The following code is trying to establish whether the loaded control implements the IRichAdminCustomControl interface.
Object obj = CustomControlHelper.GetControl(cc.Id, cc.ControlClass);
if(obj != null)
{
bool isWhatWeWant = (obj is IRichAdminCustomControl);
return isWhatWeWant;
}
That's all fine, but I've noticed that when I know I have an object that implements IRichAdminCustomControl, the expression evaluates to false.
Okay, this is where it gets really weird. If I inspect the code when debugging, the expression evaluates to true, but then if I immediately let the code run and inspect the result, it evaluates to false (I've attached an animated gif below to illustrate).
Has anyone come across weirdness like this before and if so, what on earth is causing it?
Incidentally, I believe the product I'm using uses Spring.NET to provide dependency injection in the CustomControlHelper.
If you are using Visual Studio 2010 SP1, I came across this bug:
Misreporting of variable values when debugging x64 code
There is a workaround on that page, posted by Microsoft:
You can either set all projects to compile to x86, or create an intermediate initialised variable declaration to ensure the debugger reports the correct value of the variable being examined.
Try this as a workaround:
bool isWhatWeWant = true;
isWhatWeWant &= (obj is IRichAdminCustomControl);
bool finalValue = isWhatWeWant; // this line should fix isWhatWeWant too in the debugger
return finalValue;
EDIT: seems like VS2012 also encounters similar problems in specific conditions.
Two possibilities come to mind. The first is that your interface name is generic enough that it could already be in the namespace somewhere. Try fully qualifying the interface in the is clause. The second possibility is that you might be running the code as part of a constructor, or being called indirectly by a constructor. Any reflection like stuff needs to be done after we are certain the application has fully loaded.
So I found the answer. It was because I had two copies of the dll in different locations. I had one copy in the bin of my back-end application and one in a shared external directory that gets dynamically loaded by the backend app.
I should explain; this application consists of two apps running in tandem, a frontend app and a backend app. Ordinarily, you place "Custom Controls" into your frontend app. These controls are then copied on application start to a external directory that is accessible to the backend app.
In this case, I had logic in my Custom Control library that needed to be accessed in the backend app - so I had to make a reference to it... which ended up with the backend app having two references to the same class. D'oh! And OF COURSE that's going to work when you're debugging.
Solution was to split my extra logic into its own project and reference THAT in the backend app.
I'm not going to "accept" my own answer here because, although it solved my specific problem, the solution is a bit TOO specific to the apps I'm working with and would be unlikely to help anyone else.
This happened to me once and even though I never came to a conclusion as to why it was happening I believed the PDB files that were being loaded with the debugging symbols where out of sync. So, by "cleaning" the solution and then rebuilding the solution this weird issue went away.

Unable to cast transparent proxy to type from AppDomain

I'm trying to create an object in an appdomain:
var type = typeof (CompiledTemplate);
var obj = (CompiledTemplate) domain.CreateInstanceAndUnwrap (
type.Assembly.FullName, type.FullName);
However, I always get the following error:
Unable to cast transparent proxy to type 'Mono.TextTemplating.CompiledTemplate'.
I'm running on .NET 4.0, not Mono, despite what the namespace might suggest :)
As far as I know, this error happens when .NET thinks that the Type & Assembly do not exactly match in the two domains. However, when debugging, the FullName and Location are identical. Only the Assembly.Codebase property differs - in the child AppDomain its extension is uppercased to "DLL" for some reason.
I've tried adding an AssemblyResolve handler to the AppDomain, which uses Assembly.LoadFrom to load the filename explicitly, but the CodeBase's extension still gets uppercased. Since the original assembly was also loaded with Assembly.LoadFrom (via Mono.Addins), the difference between the CodeBase values seems very strange.
Any suggestions for fixing or working around this problem?
Could you be running into an issue with assembly load contexts?
(e.g. see here)
You have a type that's clearly in the load context (because you're using typeof(CompiledTemplate)), but you're saying that the type in the secondary AD is loaded into the load-from context...
Did you check with fuslogvw to determine exactly what assemblies are being loaded? The fuslog trace will also tell you if the assemblies are being loaded into different contexts.
Perhaps you can use the dynamic keyword instead of casting it to a specific type:
var type = typeof (CompiledTemplate);
dynamic obj = domain.CreateInstanceAndUnwrap (
type.Assembly.FullName, type.FullName);
That might at least give you a workaround to the problem. Of course, the potential drawbacks will be not having compile time checking and/or slower performance. However, these might be negligible trade-offs depending on your situation.
A second copy of the assembly is, indeed, being loaded into memory as it is.
An instance of a type in the runtime is specific to the instance of the assembly loaded - so even if the same DLL file is loaded in a second time, the types are not considered to match.
This is a typical problem when "DLLHell" is extended into the "GACAndDLLHell". "GACONLYHeaven" is a better place ... :).
That the filenames are subtly different (the .DLL extension has a different case) implies that the same DLL is being loaded from two places (that is: the GAC is case-insensitive/always lower case on filenames IIRC).
An abstract class or, preferably, an interface is what you need here.
If you can't make changes to the code base I would, first, make very sure that the DLL exists in only 1 place on the drive (or 0 places on the drive if it is being loaded from the GAC). A copy of the DLL that contains the type: 'CompiledTemplate' in your app /bin folder would be a real culprit ...?
Is this new code or existing code that is now failing for some reason?
I have a WCF net named pipes application that uses a callback (duplex) architecture.
I got this error because my service interface's [ServiceContract] was annotated with the wrong callback.

New cast exception with VS2010/.Net 4

[ Updated 25 May 2010 ]
I've recently upgraded from VS2008 to VS2010, and at the same time upgraded to .Net 4.
I've recompiled an existing solution of mine and I'm encountering a Cast exception I did not have before.
The structure of the code is simple (although the actual implementation somewhat more complicated).
Basically I have:
public class SomeClass : ISomeClass
{
// Stuff
}
public static class ClassFactory
{
public static IInterface GetClassInstance<IInterface>(Type classType)
{
return (IInterface)Activator.CreateInstance(classType); // This throws a cast exception
}
}
// Call the factory with:
ISomeClass anInstance = ClassFactory.GetClassInstance<ISomeClass>(typeof(SomeClass));
Ignore the 'sensibleness' of the above - its provides just a representation of the issue rather than the specifics of what I'm doing (e.g. constructor parameters have been removed).
The marked line throws the exception:
Unable to cast object of type
'Namespace.SomeClass' to type
'Namespace.ISomeClass'.
I suspect it may have something to do with the additional DotNet security (and in particular, explicit loading of assemblies, as this is something my app does).
The reason I suspect this is that I have had to add to the config file the setting:
<runtime>
<loadFromRemoteSources enabled="true" />
</runtime>
.. but I'm unsure if this is related.
Update
I see (from comments) that my basic code does not reproduce the issue by itself. Not surprising I suppose. It's going to be tricky to identify which part of a largish 3-tier CQS system is relevant to this problem.
One issue might be that there are multiple assemblies involved. My static class is actually a factory provider, and the 'SomeClass' is a class factory (relevant in that the factories are 'registered' within the app via explicit assembly/type loading - see below) .
Upfront I use reflection to 'register' all factories (i.e. classes that implement a particular interface) and that I do this when the app starts by identifying the relevant assemblies, loading them and adding them to a cache using (in essence):
Loop over (file in files)
{
Assembly assembly = Assembly.LoadFile(file);
baseAssemblyList.Add(assembly);
}
Then I cache the available types in these assemblies with:
foreach (Assembly assembly in _loadedAssemblyList)
{
Type[] assemblyTypes = assembly.GetTypes();
_loadedTypesCache.AddRange(assemblyTypes);
}
And then I use this cache to do a variety of reflection operations, including 'registering' of factories, which involves looping through all loaded (cached) types and finding those that implement the (base) Factory interface.
I've experienced what may be a similar problem in the past (.Net 3.5, so not exactly the same) with an architecture that involved dynamically creating classes on the server and streaming the compiled binary of those classes to the client app. The problem came when trying to deserialize an instance of the dynamic class on the client from a remote call: the exception said the class type was not know, even though the source and destination types were exactly the same name (including namespace). Basically the cross boundry versions of the class were not recognised as being the same. I solved that by intercepting the deserialization process and explicitly defining the deseriazation class type in the context of the local assemblies.
This experience is what makes me think the types are considered mismatched because (somehow) the interface of the actual SomeClass object, and the interface of passed into the Generic method are not considered the same type.
So (possibly) my question for those more knowledgable about C#/DotNet is: How does the class loading work that somehow my app thinks there are two versions/types of the interface type and how can I fix that (keeping in mind its a DotNet 3.5 vs 4 issue as it worked before my upgrade) ?
[ whew ... anyone who got here is quite patient .. thanks ]
I would say yes that it has something to do either with the runtime loading of assemblies, or with the upgrade conversion, I used this code in a new project and had no issues. Can you provide more code to replicate the error?
The 'quick' (ITO implementation, not ITO finding it) solution was to stop the shadow copy of my app's DLLs.
This is done by modifying the ASP.Net app's Web.Config file as follows:
In section 'configuration/web.settings', add setting:
<hostingEnvironment shadowCopyBinAssemblies="false" />

Loading Assemblies into separate AppDomain, getting InvalidCastException

I'm trying to load assemblies in a separate app domain, but am running into a very strange problem. Here's some code:
public static void LoadAssembly(string assemblyPath)
{
string pathToDll = Assembly.GetCallingAssembly().CodeBase;
AppDomainSetup domainSetup = new AppDomainSetup
{
PrivateBinPath = pathToDll
};
AppDomain newDomain = AppDomain.CreateDomain("AssemblyLoader",null,domainSetup);
AssemblyLoader loader = (AssemblyLoader)newDomain.CreateInstanceFromAndUnwrap(
pathToDll,
typeof(AssemblyLoader).FullName);
}
AssemblyLoader is another class in the same assembly as this one, and it inherits from MarshalByRef, however for some strange reason, I get a cast exception every time I try to run this. I even hardcoded the path to the DLL instead of using GetCallingAssembly().CodeBase yet I keep getting this exception.
I understand it's hard to answer a question like this without actually seeing it and having more information, but maybe someone has run into a similar situation and would know the common "gotchas" and what I should look out for.
EDIT: The reason I don't want to load it directly is because this is just part of the code. The ultimate goal is that this class will have a method that load assemblies, gets their GUID and some other info about them and stores them in a database for a project I'm working on. Therefore, if I load this assembly in a separate app domain, I can load the others there too and then unload the app domain. No point in having all these assemblies loaded for the duration of the app, if I only need that data.
(EDIT: after reading the exception given, changing answer completely)
It appears the problem is the CreateInstanceFromAndUnwrap call, which uses the LoadFrom semantics of 'pathToDll'. Suzanne Cook detailed the possible sticking point on her blog where your original AppDomain tries to call Load("SomeAssembly, [...]") as opposed to LoadFrom("pathToDll") when trying to resolve the type in question.
Her advice was to hook the AssemblyResolve event on the current domain to do the correct LoadFrom in order to get the type. A little bit of targetted googling brings up a possible solution to the problem based on Suzanne's suggestion.
I don't believe the PrivateBinPath configuration is necessary, beyond that you don't need to use the Path to the DLL, but rather the Assembly's fully qualified name for the first parameter; try:
AssemblyLoader loader = (AssemblyLoader)newDomain.CreateInstanceFromAndUnwrap(
typeof(AssemblyLoader).Assembly.FullName,
typeof(AssemblyLoader).FullName);
There's a lot of good information for what you're trying to do here: How to load a .NET assembly for reflection operations and subsequently unload it?
Check out this article.
Using the code in that article I got a cross app-domain object. I abstracted things a bit with generics and have three assemblies. (i.e. 1 defining the interface, 1 defining the plugin implementation, and the main program which tells the generic what to load.) The original articles code is easy to follow.

Categories