We have a plugin system where the plugin code runs on a separate AppDomain from the main process, using .NET remoting for the objects to communicate.
One class is similar to HttpContext.Current (which also suffers from the problem) (edit, the actual implementation):
public class MyClass
{
public static MyClass Instance
{
get
{
if(HttpContext.Current != null)
return HttpContext.Current.Items["MyClassInstance"];
}
set
{
if(HttpContext.Current != null)
HttpContext.Current.Items["MyClassInstance"] = value;
}
}
}
Then, we have a communicating object which inherits from MarshalByRefObject:
public class CommunicatingClass : MarshalByRefObject, ICommunicatingClass
{
public void DoSomething()
{
MyClass.Instance.DoSomething();
}
}
The CommunicatingClass is created on the main AppDomain, and works fine. Then, there's the plugin class, which is created on its AppDomain, and given an instance of the CommunicatingClass:
public class PluginClass
{
public void DoSomething(ICommunicatingClass communicatingClass)
{
communicatingClass.DoSomething();
}
}
The problem is, that even though CommunicatingClass resides on the main appdomain (verified with the Immediate Window), all of the static data such as MyClass.Instance and HttpContext.Current have disappeared and are null. I have a feeling that MyClass.Instance is somehow being retrieved from the plugin AppDomain, but am unsure how to resolve this.
I saw another question that suggested RemotingServices.Marshal, but that did not seem to help, or I used it incorrectly. Is there a way that the CommunicatingClass can access all static methods and properties like any other class in the main AppDomain?
Edit:
The PluginClass is given an instance like this:
public static PluginClass Create()
{
var appDomain = GetNewAppDomain();
var instance = (PluginClass)appDomain.CreateInstanceAndUnwrap(assembly, type);
instance.Communicator = new CommunicatingClass();
return instance;
}
Edit 2:
Might have found the source of the problem. MyClass.Instance is stored in HttpContext.Current.Items (see above edit).
Is there any way at all that HttpContext.Current can access the correct HttpContext? I'm still wondering why, even though it is being run in HttpContext.Current's AppDomain, CommunicatingClass.DoSomething, when calling MyClass.Instance, retrieves stuff from PluginClass' AppDomain (if that makes any sense).
So my co-worker and I worked this out, finally, with a bunch of help from Reflector.
The main problem is that HttpContext.Current is null when accessed via a remoting call.
The HttpContext.Current property is stored in an interesting manor. A few nested setters down, you reach CallContext.HostContext. This is a static object property on a CallContext.
When the CallContext is set, it first checks if the value is an ILogicalThreadAffinitive.
If it is, it stores the value in the current thread's LogicalCallContext.
If it's not, it stores the value in the current thread's IllogicalCallContext.
HttpContext is not an ILogicalThreadAffinitive, so it is stored in the IllogicalCallContext.
Then, there's remoting.
We didn't dig too far in to its source, but what it does was inferred from some other functions.
When a call is made to a remote object from a different AppDomain, the call is not directly proxied to the original thread, running in the exact same execution context.
First, the ExecutionContext of the original thread (the one containing HttpContext.Current) is captured, via ExecutionContext.Capture (more in this in a bit).
Then, the ExecutionContext returned from Capture is passed as the first argument to ExecutionContext.Run, esentially forming the code:
Delegate myRemoteCall; //Assigned somewhere else in remoting
ExecutionContext.Run(ExecutionContext.Capture(), x => { myRemoteCall() }, null);
Then, completely transparently, your code in the remote object is accessed.
Unfortunately, HttpContext.Current is not captured in ExecutionContext.Capture().
Here lies the essential difference between an IllogicalCallContext and a LogicalCallContext.
Capture creates a brand-new ExecutionContext, essentially copying all of the members (such as the LogicalCallContext) in to the new object. But, it does not copy the IllogicalCallContext.
So, since HttpContext is not an ILogicalThreadAffinative, it cannot be captured by ExecutionContext.Capture.
The solution?
HttpContext is not a MarshalByRefObject or [Serializable] (probably for good reason), so it cannot be passed in to the new AppDomain.
But, it can cross ExecutionContexts without problem.
So, in the main AppDomain's MarshalByRefObject which is given as a proxy to the other AppDomain, in the constructor give it the instance of HttpContext.Current.
Then, in each method call of the new object (unfortunately), run:
private HttpContext _context;
private void SyncContext()
{
if(HttpContext.Current == null)
HttpContext.Current = _context;
}
And it will be set without problem. Since HttpContext.Current is tied to the IllogicalCallContext of the ExecutionContext, it will not bleed in to any other threads that ASP.NET might create, and will be cleaned up when the copy ExecutionContext is disposed.
(I could be wrong about much of this, though. It's all speculation and reflection)
I believe you'll need to derive MyClass from MarshalByRefObject as well.
Related
With StructureMap, I am registering an interface as follow:
public class PersistenceRegistry : Registry
{
public PersistenceRegistry()
{
For<IClearableSessionProvider>().HybridHttpOrThreadLocalScoped().Use<FirebirdSessionProvider>();
}
}
I would like to know if it is possible to detect by any ways if the resolved instance is resolved from an HttpContext or a "ThreadContext". I don't even need to have access to the context. I just want to know whether I am in the context of an HttpRequest or from a background thread.
I tried several things consisting of injecting the HttpContext if it exists, but whatever I tried, I always get a null reference.
I tried to register an IHttpContextProvider:
public interface IHttpProvider
{
HttpContext GetHttpContext();
}
public class HttpProvider
{
HttpContext GetHttpContext()
{
return HttpContext.Current;
}
}
But HttpContext.Current is always null (even once I get into the controller call).
I tried to register an HttpContextWrapper but also always null:
For<HttpContextBase>().Use(() => new HttpContextWrapper(HttpContext.Current))
I am using StructureMap-2.6.3.0. I am not sure how to formulate correctly but the app is built upon OWIN (which explains why my attempts always return null if I understand correctly what I have been reading before coming here).
I have a helper-class for my unit-tests that share a reference to an COM-object in memory:
public class UnitTestGeometryProvider
{
public static readonly IGeometry Geometry = Deserialize();
}
The geometry is deserialized from a Xml file which is stored as a resource file and appended to the project. Afterwards it is wrapped into a COM object:
public static IGeometry Deserialize()
{
return (IGeometry) new XMLSerializerClass().LoadFromString(myXDoc.OuterXml, null, null);
}
Now I have two test-methods that use the geometry stored within this class:
[TestClass()]
public class MyTest
{
[TestMethod()]
public void FirstTest()
{
var p = UnitTestGeometryProvider.Geometry;
}
[TestMethod()]
public void SecondTest()
{
var p = UnitTestGeometryProvider.Geometry;
}
}
When running the second one I get a COMException:
COM object that has been separated from its underlying RCW cannot be used
I wonder why the reference to the COM-object is released as it is marked static in UnitTestGeometryProvider and I do not explicitly release it. So even if the managed resource to the instance would go out of scope (which is does not at it is static), the underlying COM object should go away only when all my tests finished or more general when the application terminates, or do I miss anything?
I am using ArcObjects and Visual NUnit.
Due to the comments by Hans Passant I found the actual problem.
Obviously the Visual-NUnit-Framework decides to create a separate thread for every test. Thus whenever I create a COM-object - be it static or not - this object lives on this single thread and cannot be used in another one. If the thread dies also does the COM-object or to be more precise the reference to it. This leads to the GC kicking in throwing the COM-object away as no more managed references to it exist within that thread.
The solution is pretty straitforward: I changed the static field to instance-members and created an instance-member of type UnitTestGeometryProvider within my test-class. Thus a new provider is generated by every test.
However this solution is quite annoying because the Geometry-property has to be initialized and therefor the Deserialize-method runs for every test instead of only once for all tests.
I donĀ“t know if there is a thread-safe solution to not kill the reference to the COM-object when the first thread that intialized it dies.
I have a property whose instance I want to be in other domain.
public ModuleLoader Loader
{
get
{
if(_loader == null)
_loader = (ModuleLoader)myDomain.CreateInstanceAndUnwrap(
this.GetType().Assembly.FullName,
"ModuleLoader",
false,
System.Reflection.BindingFlags.CreateInstance,
null,
null,
null,
null);
System.Diagnostics.Debug.WriteLine("Is proxy={0}",
RemotingServices.IsTransparentProxy(_loader));
//writes false
_loader.Session = this;
return _loader;
}
}
This works fine. But I assume all the method calls on _loader instance will invoke in other domain (myDomain). But when I run following code, it still writes main app domain.
public void LoadModule(string moduleAssembly)
{
System.Diagnostics.Debug.WriteLine("Is proxy={0}",
RemotingServices.IsTransparentProxy(this));
System.Diagnostics.Debug.WriteLine(
AppDomain.CurrentDomain.FriendlyName);
System.Diagnostics.Debug.WriteLine("-----------");
}
Is it because of Unwrap()? Where I am doing wrong?
I understand AppDomain creates seperate memory. What I need is my main application runs, it loads modules in different AppDomain. Since main app also wants to watch some activity of modules and interfare with objects running in seperate domain, what is the best way to achieve it.
If you want to actually run the code in the other assembly, you need to make your ModuleLoader class inherit from MarshalByRefObject. If you do that, CreateInstanceAndUnwrap() will actually return a proxy and the call will be executed in the other appdomain.
If you don't do that, and instead mark the class as Serializable (as an exception message suggests), CreateInstanceAndUnwrap() will create the object in the other appdomain, serialize it, transfer the serialized form to the original appdomain, deserialize it there and call the method on the deserialized instance.
I am currently writing a framework for MutationTesting. The code is almost complete, but there is a tiny bit which (after spending half a day on it) I cannot figure out:
via reflection I would like to execute a method "TestMethod" which is inside the class "TestClass". The project in which "TestClass" is, references an essembly, lets call it "Proband.dll".
The "TestMethod" creates an object of some type inside the Proband.dll and executes a method on that object.
In order to clarify a little:
TestClass - is a class that contains unit tests.
TestMethod - is a single unit test.
Proband.dll - contains the method/class to test.
Before executing the TestMethod, I already successfully disassembled, mutated and reassembled the Proband.dll. So now I have a new "Proband.dll" which shall be taken by the TestClass instead!
The problem is that the TestClass is already in the middle of execution. I was thinking whether it is possible to create an AppDomain in which the new Proband.dll will be loaded and inside the fresh AppDomain, the TestMethod will be executed.
I have already created this AppDomain and successfully loaded the new Proband.dll into it, however, I do not know how to execute the TestMethod inside this new AppDomain. Also I do not know whether this is going to "replace" the old Proband.dll for the test method.
Here is my TestClass:
[TestClass()]
public class TestClass
{
[TestInitialize()]
public void MyTestInitialize()
{
// code to perform the mutation.
// From here, the "TestMethod" should be called with
// the new "Proband.dll".
}
[TestMethod()]
public void TestMethod()
{
// test code calling into Proband.dll
}
}
Does anyone have an idea of how this could be achieved? Or any clue or keyword?
Thanks,
Christian
This is probably overkill, but check out my answer:
Static Fields in AppDomain
You'll want to create the TestClass in the AppDomain that you've loaded the Proband.dll in, and use a MarshalByRef to keep the class in the AppDomain (so you can unload it later).
Here is more about what I did (not exact, as i'm changing it to match more what you need).
// Create Domain
_RemoteDomain = AppDomain.CreateDomain(_RemoteDomainName,
AppDomain.CurrentDomain.Evidence,
AppDomain.CurrentDomain.BaseDirectory,
AppDomain.CurrentDomain.BaseDirectory,
true);
// Load an Assembly Loader in the domain (mine was Builder)
// This loads the Builder which is in the current domain into the remote domain
_Builder = (Builder)_RemoteDomain.CreateInstanceAndUnwrap(
Assembly.GetExecutingAssembly().FullName, "<namespace>.Builder");
_Builder.Execute(pathToTestDll)
public class Builder : MarshalByRefObject
{
public void Execute(string pathToTestDll)
{
// I used this so the DLL could be deleted while
// the domain was still using the version that
// exists at this moment.
Assembly newAssembly = Assembly.Load(
System.IO.File.ReadAllBytes(pathToTestDll));
Type testClass = newAssembly.GetType("<namespace>.TestClass",
false, true);
if (testClass != null)
{
// Here is where you use reflection and/or an interface
// to execute your method(s).
}
}
}
This should provide you the solution you need.
I'm using Castle Core to create a custom attribute and interceptor to inject security checks into our code using attributes.
e.g. [Security("Role1")]
In the implementation of the interceptor:
public class SecurityInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
object o;
MethodInfo mi = invocation.Method;
SecurityAttribute[] atts = (SecurityAttribute[])mi.GetCustomAttributes(typeof(SecurityAttribute), true);
// if method not marked with Security attribute, then pass on call
if (atts.Length == 0)
{
invocation.Proceed();
}
else
{
//for now assume that there is only one security attribute on the method
//do some security test
{
invocation.Proceed();
}
}
}
In the "do some security test" section above, I need access to the HttpContext.Session object in order to retrieve some saved objects to do the security test.
Assume the method that this attribute is on, is a code-behind asp.net page, i.e an instance of the Page class)
I can't just use this.Context in the attribute like this [Security("Role1", this.Context)]
as attributes don't allow that.
So how do I get access to the httpContext inside the Intercept method?
Or is there a better way of doing this in an aspect-like way?
The InvocationTarget property of the IInvocation instance has the target object. So in your case, if you are certain that the interception happens on a Page object, you should be able to do this:
var page = (Page)invocation.InvocationTarget;
If that is not always the case, you should gain access to the HTTP context in another way.
It's true, as Ben points out, that HttpContext.Current gives you access to the current HttpContext from anywhere, but accessing that static property is just icky. There's a better way, however, and that is by registering a factory method that allows for injection of the session state:
container.Register(
Component.For<ISessionState>()
.UsingFactoryMethod(k => new SessionWrapper(HttpContext.Current.Session)
.Lifestyle.PerWebRequest));
assuming that you have created the ISessionState interface and an appropriate wrapper that has the API you wish to use when interacting with the ASP.NET HttpSessionState object.
Now, since the interceptor is pulled from the container like everything else, it can depend on ISessionState:
public class SecurityInterceptor : IInterceptor
{
public SecurityInterceptor(ISessionState sessionState)
{
//...
}
}
which makes your interceptor nice and testable.
There's probably many other ways to do this, and possible better ways as well. This is just an idea on how you can get on with your project :)
You can use HttpContext.Current.Session from anywhere so long as the code is being called from an ASP.net process.