Loading a plugin into an AppDomain and calling the constructor - c#

As you can tell by the title, I'm trying to load a plugin into a new AppDomain. I want to call the constructor when that happens.
Normally this wouldn't be a problem but the constructor takes a parameter. In this case, it's a class that uses a bunch of other classes too.
AppDomain domain = AppDomain.CreateDomain("Plugins");
object inst = Activator.CreateInstance(domain, "ircplugin", "eIRCBot.Plugin", false, BindingFlags.Default, null, new object[] { ircinstance }, null, null);
If I do this, it gives me the following exception:
An unhandled exception of type 'System.Runtime.Serialization.SerializationException' occurred in mscorlib.dll
Additional information: Type 'IRCLib.IrcInstance' in assembly 'IRCLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
How do I solve this? I can't go and make all of the classes serializable because it also uses some of .net's classes like tcpclient. Am I missing something here?

Related

Unable to create instance of unit test class. Error: System.TypeLoadException

I'm trying to create an instance of a class I need for a unit test. This class depends a "global object", which gets passed into the constructor. It basically gives the object access to our base return class that helps us handle errors and diagnostics (so I can't do without it in this architecture).
The problem is that a constructor a couple layers above the global object constructor relies on an assembly that can't be loaded when I instantiate my test class:
// test constructor
public Tests()
{
State state = new State(GlobalObject);
}
// global object constructor
public Global()
{
string localPath = (System.Web.HttpContext.Current == null)
? string.Empty
: System.Web.HttpContext.Current.Server.MapPath("");
...
}
The error:
Unable to create instance of class Tests. Error: System.TypeLoadException: Could not load type 'System.Web.HttpContext' from assembly 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'..
I've tried rebuilding the solution/cleaning the project. I've also tried adding this assembly manually, but this error persists. Search results for this question are either very old or not helpful. How can I get this assembly to be recognized?

'object' does not contain a definition for '...' within same assembly

Per my boss' recommendations, we are using Dapper to access our database. As this is just a quick, throwaway project, he would like to forego creating POCO objects for the results of these calls to the database, so we're just having Dapper return dynamic objects.
So I have several Web API controllers, all deriving from the same BaseController. In the base class, we have a method like this:
protected dynamic ExecuteSingle(string sql, object parameters = null, bool useStoredProcedure = true) {
using (var db = ObjectFactory.GetInstance<IDbManager>())
{
var cmd = useStoredProcedure ? db.SetSpCommand(sql) : db.SetCommand(sql);
if (parameters != null)
{
cmd = cmd.SetParameters(parameters);
}
return cmd.ExecuteObject<dynamic>();
}
}
We are able to use that successfully when taking that result and passing it as the parameter to a Ok() return value. All of the properties of the object get successfully parsed into the JSON object.
I'm now working on another section of code where instead of immediately spitting it back out, I need to use the data coming back to hit another set of functionality.
var sql = " SELECT Id FROM Table WHERE ReviewId = :ReviewId ";
dynamic dbSurvey = ExecuteSingle(sql, new {ReviewId = reviewId}, false);
var survey = sg.GetSurvey(new GetSurveyRequest(dbSurvey.Id));
It then fails on that last line where calling dbSurvey.Id with an exception saying
'object' does not contain a definition for 'Id'
Checking the object, there is a property on there named "Id".
I have checked several questions already, and even though they are all dealing with Anonymous objects (which I could understand dynamic being under that heading), I am staying within the same assembly, so those points about Anonymous objects being declared as "internal" wouldn't apply in this case.
I also tried changing the return type of ExecuteSingle to an ExpandoObject, but am getting the same results.
EDIT
Here is a screenshot of how it is being called, versus the object and properties that it contains. Perhaps the format of the properties could help determine why it's not being found.
I also used the Watch menu to try various ways to access the property, and none of the following worked:
dbSurvey["Id"]
(dbSurvey as IDictionary<string, long>)["Id"]
((IDictionary<string, int>)dbSurvey)["Id"]
Here is the result of dbSurvey.GetType() from the immediate window while running:
{<>f__AnonymousType2`1[System.Int64]}
base: {Name = "<>f__AnonymousType2`1" FullName = "<>f__AnonymousType2`1[[System.Int64, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"}
Assembly: {***.Web.Test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null}
AssemblyQualifiedName: "<>f__AnonymousType2`1[[System.Int64, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], ***.Web.Test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
BaseType: {Name = "Object" FullName = "System.Object"}
ContainsGenericParameters: false
DeclaringMethod: 'dbSurvey.GetType().DeclaringMethod' threw an exception of type 'System.InvalidOperationException'
DeclaringType: null
FullName: "<>f__AnonymousType2`1[[System.Int64, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"
GenericParameterAttributes: 'dbSurvey.GetType().GenericParameterAttributes' threw an exception of type 'System.InvalidOperationException'
GenericParameterPosition: 'dbSurvey.GetType().GenericParameterPosition' threw an exception of type 'System.InvalidOperationException'
GUID: {382c0269-d631-3c89-a105-38a1be8a3db7}
IsConstructedGenericType: true
IsEnum: false
IsGenericParameter: false
IsGenericType: true
IsGenericTypeDefinition: false
IsSecurityCritical: true
IsSecuritySafeCritical: false
IsSecurityTransparent: false
MemberType: TypeInfo
MetadataToken: 33554480
Module: {***.Web.Test.dll}
Name: "<>f__AnonymousType2`1"
Namespace: null
ReflectedType: null
StructLayoutAttribute: {System.Runtime.InteropServices.StructLayoutAttribute}
TypeHandle: {System.RuntimeTypeHandle}
UnderlyingSystemType: {Name = "<>f__AnonymousType2`1" FullName = "<>f__AnonymousType2`1[[System.Int64, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"}
OK, so I resolved this issue. I just figured out what the problem was. Thanks to xanatos' help in the comments, we noticed that the Module property of the type was coming from the Test harness. That didn't make any sense to me at the time, because the dynamic object was being created in the same assembly.
However, what I didn't think about until I came back to the issue this morning was that the source of the object being "created" in the base API Controller was an anonymous object that I was creating in the Test harness. So that Module was correct.
If I went and created a separate POCO object for the Mock object in the Test DLL, then it was no longer creating a dynamic off of an anonymous object created in another assembly.

InvalidCastException, wrong context?

I have an exe that does data dumps. The exe will dynamically pick up DLL's based on configuration and pass a class object into it. The DLL has a copy of this class compiled with it and can see the data, under debug, without a problem as an object. However, when I try to cast that to the class, it tells me it can't because of the context. I'm sure I've overlooked something as I do that at times.
Error:
[A]MyClass cannot be cast to [B]MyClass. Type A originates from
'MyExe, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the
context 'Default' at location 'C:\MyPath\MyExe.exe'. Type B originates
from 'MyDLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in
the context 'LoadNeither' at location 'C:\MyPath\MyDLL.dll'.
EXE Code:
Object[] param = new Object[] { MyClass };
MethodInfo m = type.GetMethod("MyMethod");
reader = (SqlDataReader)m.Invoke(obj, param);
DLL Code:
public SqlDataReader MyMethod(Object param)
{
SqlDataReader reader = new SqlDataReader();
Type t = param.GetType(); //Returns MyClass
if (param is MyClass) //Returns false
reportItem = (MyClass)param; //Never executes
MyClass reportItem = (MyClass)param; //InvalidCastException
//other code here, pulling data
return reader;
}
The DLL has a copy of this class compiled with it
Don't do that, basically. You should have the type in one assembly, and only one assembly. As far as the CLR is concerned, these are entirely different types.
You probably want to have a common library which both the plugins and your application can refer to. Or you could make your plugins refer to the application executable and keep the type within there.

Why am I getting assembly version as 0.0.0.0? Will that make any issues if real DLL has some version number and using Type class to retrieve values?

I have a project named "Test.LiveModel" (Test.LiveModel.dll) and its version is 8.0.7.0 in my solution which contains 25 projects. I can see the information of Test.LiveModel in AssemblyInfo.cs. I have two category of objects named 'base class category' and 'user-defined class category' which are displaying in my application UI. I am displaying this through a property which is of class Type
Now I am considering one base class category object named "Server" and one user-defined class category object RoundedTree. When I set value as "Server" in Property in Grid after saving it when I restart my application I can see the saved value, but for "RoundedTree" which is not happening due to type becomes null. So I did a thorough analysis and came to know that issue is in ToType() method shown below
This is ToType() metho
For base class Server xmlSerializableType.Name, I am getting as Test.LiveModel.Server and AssemblyName I am getting as Test.LiveModel, Version=8.0.7.0, Culture=neutral, PublicKeyToken=23bd062a94e26d58 and type I am getting by using Type.GetType as type = {Name = "Server" FullName = "Test.LiveModel.Server"}
But for user defined class xmlSerializableType.Name I am getting as _Rounded_Tree. 'type' I am getting as null by using Type.GetType. AssemblyName I am getting as _Rounded_TreeTest-Machine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null, but even assembly.GetType I am getting as null. What is the reason behind it? Why am I getting assembly version 0.0.0.0? I mean full assembly _Rounded_TreeTest-Machine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null.
This is the method CreateType() which will create assembly and type as myTypeBuilder for userdefined class:
public Type CreateType()
{
// Create the assembly name by appending the machine name to the typename.
myAsmName.Name = this.TypeName + Environment.MachineName;
// Define assembly that can be executed but not saved
this.UserClassAssemblyBuilder = myDomain.DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.Run);
// Create dynamic module with symbol information
this.UserClassModuleBuilder = this.UserClassAssemblyBuilder.DefineDynamicModule("userdefinedmodule", true);
So here is my question: if real Dll has some version number, and user defined class assembly has version 0.0.0.0, is that the reason why I am getting type as null after using Type.GetType and assembly.GetType method?
Here are some suggestions which may solve the problems.
Define a assembly version
new AssemblyName(this.TypeName + Environment.MachineName)
{
Version = new Version("1.0.0.0")
};
Use full qualified names for the serialization
myObject.GetType().FullName

Loading services from other .dll and run them isolated

I'd like to run several services from different .dll's in a isolated way. Basically, all services are derived from RoleEntryPoint , and I want to load each one in a separated AppDomain and run it there in a different thread.
So far, I can locate the service and get its type:
String pathToDll = #"C:\....\bin\Debug\ChildWorkerRole.dll";
Assembly assembly = Assembly.LoadFrom(pathToDll);
Type serviceType = assembly.GetTypes().SingleOrDefault(t => t.BaseType == typeof(RoleEntryPoint));
And also run it in the current AppDomain and Thread:
RoleEntryPoint myRole2 = (RoleEntryPoint)Activator.CreateInstance(serviceType);
if (myRole2.OnStart())
myRole2.Run();
But when I try to run it in a separate in different AppDomain I get an exception:
AppDomain domain = AppDomain.CreateDomain("MyNewDomain");
RoleEntryPoint myRole = (RoleEntryPoint)domain.CreateInstanceFromAndUnwrap(pathToDll, serviceType.FullName);
if (myRole.OnStart())
myRole.Run();
This exception:
System.Runtime.Serialization.SerializationException was unhandled
Message=Type 'ChildWorkerRole.WorkerRole' in assembly 'ChildWorkerRole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
Source=mscorlib
StackTrace:
at System.AppDomain.CreateInstanceFromAndUnwrap(String assemblyName, String typeName)
.......
The funny thing is that ChildWorkerRole is actually marked with the SerializableAttribute ... but may be because RoleEntryPoint is not, it cannot be done.
Any idea or workaround?
thanks!
In order to work with a type in a separate AppDomain, it, and all of the types you directly use, need to be either marked as [Serializable], or you need to derive them from MarshalByRefObject.
If this is not possible, the only real option is to make a proxy class that you can use which does follow the above criteria, and allow it to manage your types internally.

Categories

Resources