I have a bit of a head-scratcher here that I wonder if someone may know the answer to.
The setup is basically this:
//in Visual Studio plug-in application
SpinUpProgramWithDebuggerAttached();
//in spun up program
void Start()
{
StaticClass.StaticVariable = "I want to use this.";
XmlSerializer.Deserialize(typeof(MyThingie), "xml");
}
class MyThingie : IXmlSerializable
{
ReadXml()
{
//why the heck is this null?!?
var thingIWantToUse = StaticClass.StaticVariable;
}
}
The problem that has me pulling my hair out is that StaticClass.StaticVariable is null in the IXmlSerializable.ReadXml() method, even though it's called RIGHT AFTER the variable is set.
Of note is that breakpoints aren't hit and Debugger.Launch() is ignored in the precise spot the problem occurs.
Mysteriously, I determined through raising exceptions that the AppDomain.CurrentDomain.FriendlyName property is the same for the place the static variable is populated vs. null!
Why the heck is the static variable out of scope?!? What's going on?!? How can I share my variable?
EDIT:
I added a static constructor, per a suggestion in the responses, and had it do a Debug.WriteLine. I noticed it was called twice, even though all the code appears to be running in the same AppDomain. Here is what I see in the output window, which I'm hoping will be a useful clue:
Static constructor called at: 2015-01-26T13:18:03.2852782-07:00
...Loaded 'C:...\GAC_MSIL\System.Numerics\v4.0_4.0.0.0__b77a5c561934e089\System.Numerics.dll'...
...Loaded 'Microsoft.GeneratedCode'...
...Loaded 'C:...\GAC_MSIL\System.Xml.Linq\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.Linq.dll'....
...Loaded 'C:\USERS...\APPDATA\LOCAL\MICROSOFT\VISUALSTUDIO\12.0EXP\EXTENSIONS...SharePointAdapter.dll'. Symbols loaded.
...Loaded 'Microsoft.GeneratedCode'.
Static constructor called at: 2015-01-26T13:18:03.5196524-07:00
ADDITIONAL DETAIL:
Here is the actual code, since a couple of commenters thought it might help:
//this starts a process called "Emulator.exe"
var testDebugInfo = new VsDebugTargetInfo4
{
fSendToOutputWindow = 1,
dlo = (uint)DEBUG_LAUNCH_OPERATION.DLO_CreateProcess,
bstrArg = "\"" + paramPath + "\"",
bstrExe = EmulatorPath,
LaunchFlags = grfLaunch | (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_StopDebuggingOnEnd | (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_WaitForAttachComplete,
dwDebugEngineCount = 0,
guidLaunchDebugEngine = VSConstants.CLSID_ComPlusOnlyDebugEngine,
};
var debugger = Project.GetService(typeof(SVsShellDebugger)) as IVsDebugger4;
var targets = new[] { testDebugInfo };
var processInfos = new[] { new VsDebugTargetProcessInfo() };
debugger.LaunchDebugTargets4(1, targets, processInfos);
//this is in the emulator program that spins up
public partial class App : Application
{
//***NOTE***: static constructors added to static classes.
//Problem still occurs and output is as follows (with some load messages in between):
//
//MefInitializer static constructor called at: 2015-01-26T15:34:19.8696427-07:00
//ContainerSingleton static constructor called at: 2015-01-26T15:34:21.0609845-07:00. Type: SystemTypes.ContainerSingleton, SystemTypes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=...
//ContainerSingleton static constructor called at: 2015-01-26T15:34:21.3399330-07:00. Type: SystemTypes.ContainerSingleton, SystemTypes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=...
protected override void OnStartup(StartupEventArgs e)
{
//...
//initializes a MEF container singleton (stored as static variable)
MefInitilizer.Run();
//here's where it blows up. the important details are that
//FullSelection implements IXmlSerializable, and its implemention
//ends up referencing the MEF container singleton, which ends up
//null, even though it was initialized in the previous line.
//NOTE: the approach works perfectly under a different context
//so the problem is not the code itself, per se, but a problem
//with the code in the environment it's running in.
var systems = XmlSerialization.FromXml<List<FullSelection>>(systemsXml);
}
}
public static class MefInitilizer
{
static MefInitilizer() { Debug.WriteLine("MefInitializer static constructor called at: " + DateTime.Now.ToString("o")); }
public static void Run()
{
var catalog = new AggregateCatalog();
//this directory should have all the defaults
var dirCatalog = new DirectoryCatalog(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
//add system type plug-ins, too
catalog.Catalogs.Add(dirCatalog);
var container = new CompositionContainer(catalog);
ContainerSingleton.Initialize(container);
}
}
public class ContainerSingleton
{
static ContainerSingleton()
{
Debug.WriteLine("ContainerSingleton static constructor called at: " + DateTime.Now.ToString("o") + ". Type: " + typeof(ContainerSingleton).AssemblyQualifiedName);
}
private static CompositionContainer compositionContainer;
public static CompositionContainer ContainerInstance
{
get
{
if (compositionContainer == null)
{
var appDomainName = AppDomain.CurrentDomain.FriendlyName;
throw new Exception("Composition container is null and must be initialized through the ContainerSingleton.Initialize()" + appDomainName);
}
return compositionContainer;
}
}
public static void Initialize(CompositionContainer container)
{
compositionContainer = container;
}
}
Bear in mind I've just copied your code to try to replicate your problem.
When running this code, I get a NullReferenceException on Debug.Write, AnotherClass hasn't properly initialized before the call is resolved.
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
MefInitilizer.Run();
Debug.Write(AnotherClass.Test);
}
}
public class AnotherClass
{
public static String Test = ContainerSingleton.ContainerInstance;
}
public static class MefInitilizer
{
public static void Run()
{
ContainerSingleton.Initialize("A string");
}
}
public class ContainerSingleton
{
private static String compositionContainer;
public static String ContainerInstance
{
get
{
if (compositionContainer != null) return compositionContainer;
var appDomainName = AppDomain.CurrentDomain.FriendlyName;
throw new Exception("Composition container is null and must be initialized through the ContainerSingleton.Initialize()" + appDomainName);
}
}
public static void Initialize(String container)
{
compositionContainer = container;
}
}
}
However, when I add static constructors to all classes with static fields it works as expected:
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
MefInitilizer.Run();
Debug.Write(AnotherClass.Test);
}
}
public class AnotherClass
{
static AnotherClass()
{
}
public static String Test = ContainerSingleton.ContainerInstance;
}
public static class MefInitilizer
{
static MefInitilizer()
{
}
public static void Run()
{
ContainerSingleton.Initialize("A string");
}
}
public class ContainerSingleton
{
static ContainerSingleton()
{
}
private static String compositionContainer;
public static String ContainerInstance
{
get
{
if (compositionContainer != null) return compositionContainer;
var appDomainName = AppDomain.CurrentDomain.FriendlyName;
throw new Exception("Composition container is null and must be initialized through the ContainerSingleton.Initialize()" + appDomainName);
}
}
public static void Initialize(String container)
{
compositionContainer = container;
}
}
}
I'd say this could definitely be a BeforeFieldInit problem.
As I understood, your code is an plug-in for a Visual Studio, and the main problem of your application is that your class is being instantiated twice, once for a normal AppDomain, and once for some other reason you can't really find out.
First of all, I see here a potential sandboxing from a Visual studio - it wants to test your code in various sets of rights to ensure your code won't harm any other parts of the Visual Studio or end user work. In this case your code could be loaded into another AppDomain, without some rights (You can find a good article at the MSDN), so you can understand why is your code called twice per application.
Second, I want to point out that you are misunderstanding the idea of static constructor and static method:
public static void Initialize(CompositionContainer container)
{
compositionContainer = container;
}
is not the same as
public static ContainerSingleton()
{
compositionContainer = container;
}
So, I suggest you to move the all initialization logic into a static container, something like this:
public class ContainerSingleton
{
private static CompositionContainer compositionContainer;
public static CompositionContainer ContainerInstance
{
get
{
if (compositionContainer == null)
{
var appDomainName = AppDomain.CurrentDomain.FriendlyName;
throw new Exception("Composition container is null and must be initialized through the ContainerSingleton.Initialize()" + appDomainName);
}
return compositionContainer;
}
}
public static ContainerSingleton()
{
var catalog = new AggregateCatalog();
//this directory should have all the defaults
var dirCatalog = new DirectoryCatalog(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
//add system type plug-ins, too
catalog.Catalogs.Add(dirCatalog);
compositionContainer = new CompositionContainer(catalog);
}
}
Second approach: I want to point out that the pattern you use for getting the singleton is outdated, try to use the Lazy<T> class, something like this:
public class ContainerSingleton
{
private static Lazy<CompositionContainer> compositionContainer;
public static CompositionContainer ContainerInstance
{
get
{
return compositionContainer.Value;
}
}
public static ContainerSingleton()
{
compositionContainer = new Lazy<CompositionContainer>(() => Initialize());
}
public static void Initialize()
{
// Full initialization logic here
}
}
Also, you should remember that simply adding the empty static constructors isn't enough - you should move all assignments to it, so you should replace such code:
public class AnotherClass
{
static AnotherClass()
{
}
public static String Test = ContainerSingleton.ContainerInstance;
}
with this one:
public class AnotherClass
{
static AnotherClass()
{
Test = ContainerSingleton.ContainerInstance;
}
public static String Test;
}
Update:
#Colin You can even use [LazyTask type][https://msdn.microsoft.com/en-us/magazine/dn683795.aspx] - simply pass a Func to your constructor, and it will be a thread-safe approach, see more in the article. The same Id of the AppDomain means nothing - the sandbox could run your code via AppDomain.ExecuteAssembly method (it's obsolete in 4.5, but still could be a possible variant) to see how it behaves in various set of permissions.
May be there is another technique for this in .NET 4.5, but can't find an article related right now.
Update 2:
As I can see in your code, you are reading some information from disk. Try to add a Code Access Security rule for this to see, if your code is being ran under restricted permissions, like this:
FileIOPermission f2 = new FileIOPermission(FileIOPermissionAccess.Read, Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
//f2.AddPathList(FileIOPermissionAccess.Write | FileIOPermissionAccess.Read, "C:\\example\\out.txt");
try
{
f2.Demand();
}
catch (SecurityException s)
{
Console.WriteLine(s.Message);
}
More about FileIOPermission Class on MSDN.
Try adding a static constructor to ContainerSingleton. I believe this is BeforeFieldInit raising its ugly head again.
Thanks to all who offered suggestions! I never did figure out exactly what was going on (and will continue to investigate/post updates if I ever do), but I did end up coming up with a workaround.
A few suggested that initialization of the static class be internalized, but due to the nature of the actual problem, initialization logic had to be externalized (I was basically loading a DI/service location container whose composition varied from environment to environment).
Also, I suspect it wouldn't have helped, since I could observe that the static constructor was called twice (thus, whatever initialization logic there was would just be called twice, which didn't directly address the problem).
However, the suggestion got me on the right track.
In my case, none of the services I loaded needed to be stateful, so it didn't really matter that the initialization happened twice aside from the performance hit.
Therefore, I simply had the static class check if the MEF container was loaded, and if it wasn't I'd read a configuration file which specified a class that handled initialization.
By doing so, I could still vary the composition of the MEF container from environment to environment, which is currently working pretty well, even if it's not an ideal solution.
I'd like to split the bounty between all who helped, but since that doesn't appear to be possible, I will probably reward OakNinja since he was a hero in spitting out as many good ideas as could realistically be expected given the information I provided. Thanks again!
Related
So I'm trying to access a running program from which I've injected an external DLL at runtime. I have two projects: console app and class library.
Console app:
using System;
using System.Reflection;
namespace SharpConsole
{
public class GameEvent
{
private bool _cancelled;
public bool Cancelled { get => _cancelled; }
public void SetCancelled(bool cancelled)
{
_cancelled = cancelled;
}
public override string ToString()
{
return $"Event (Cancelled={Cancelled})";
}
}
public class API
{
public static void Print(string text)
{
Console.WriteLine($"[API] {text}");
}
}
public class Program
{
public static string TestValue = "some initial test value";
static void Main(string[] args)
{
var evt = new GameEvent();
Console.WriteLine("[Console] " + evt.ToString());
Console.WriteLine("[Console] Loading dll...");
// var asm = Assembly.LoadFile(#"C:\Users\tinytengu\Desktop\SharpTest\SharpExternal\bin\Release\netcoreapp3.1\SharpExternal.dll");
var asm = Assembly.LoadFile(#"C:\Users\tinytengu\SharpExternal\SharpExternal.dll");
var classType = asm.GetType("SharpExternal.Class1");
Console.WriteLine("[Console] Invoking SharpExternal.Class1.Main method...");
var methodInfo = classType.GetMethod("Main", BindingFlags.Static | BindingFlags.Public);
methodInfo.Invoke(null, new object[] { evt });
Console.WriteLine("[Console] After changes: " + evt.ToString());
Console.WriteLine();
methodInfo = classType.GetMethod("ShowTestValue", BindingFlags.Static | BindingFlags.Public);
methodInfo.Invoke(null, null);
Console.ReadLine();
}
}
}
And a class library:
using System;
namespace SharpExternal
{
public class Class1
{
public static void Main(ref SharpConsole.GameEvent evt)
{
Console.WriteLine("[DLL] " + evt.ToString());
Console.WriteLine("[DLL] Cancelling an event");
evt.SetCancelled(true);
}
public static void ShowTestValue()
{
SharpConsole.API.Print(SharpConsole.Program.TestValue);
}
}
}
What is going on:
The console app creates GameEvent class instance
It injects the ExternalSharp.dll
ExternalSharp.Class1.Main gets called passing previously created GameEvent instance reference to it.
ExternalSharp changes GameEvent's Cancelled state and SharpConsole outputs an instance of a GameEvent that has been successfully modified from ExternalSharp
ExternalSharp's ShowTestValue gets called so it outputs a Console's TestValue field value.
The thing is, I can compile SharpExternal (which uses SharpConsole project as a dependency so it can use its classes. I can also compile SharpConsole and use its DLL file as a dependency, the result is the same) once and move its DLL file on the Desktop so it can't access any files, be recompiled and etc., i.e. in a completely empty folder. BUT I can change Console's TestValue at any moment, even from Console.ReadLine in runtime, then recompile only the Console app and the SharpExternal will output new value when the ShowTestValue method is called. I can add more static properties, methods, etc. before and after TestValue, change the file as I want, but unchanged from the first time SharpExternal.dll file which is still located on the Desktop manages to find TestValue (and other static fields, methods, ...) every time and output the correct value.
I'd like to know how this whole thing works, how ExternalSharp finds the correct TestValue address every time. I suppose it's because of the static modifier, but I haven't found any information beyond the fact that it allows type members to refer to the type itself, and not just to its instance, which I already knew.
I was wondering if there is anyway, how to do a parameter existence check with Ninject?
What I am referring to: let's have a theoretical class and an interface:
public interface IFileExistenceCheck
{
bool FileExist();
}
public class FileExistenceChecker : IFileExistenceCheck
{
private readonly string filePath;
private readonly IFileSystem fileSystem;
public FileExistenceChecker(IFileSystem fileSystem, string filePath)
{
this.fileSystem = fileSystem;
this.filePath = filePath;
}
public bool FileExist()
{
return this.fileSystem.File.Exists(this.filePath);
}
}
Then somewhere in the code, I will be getting an instance of IFIleExistenceCheck interface via kernel like so:
public class Foo()
{
public void Bar()
{
// do something
var filePath = SomeMagicString;
var filePathArgument = new ConstructorArgument("filePath", filePath); // <- This part I do not really like
var checker = Kernel.Get<IFileExistenceCheck>(filePathArgument);
var fileExist = checker.FileExist();
// do the rest of code
}
}
This will work just fine, problem is, that it will only work as long as the name of file path argument stays the same. Let's say one day somebody will decide, that filePath is unnecessary and renames it just to path. The code itself will still compile, but it will not cause any error until somebody will actually do the call to the Bar() method.
Is there any way how to prevent this from happening?
I do not really want to expose the filePath. I still want it to be passed as an argument of a constructor. I do not want to change signature of FileCheck() to accept filePath as an argument and I do not even want to change filePath to publicly accessible field.
This is abusing the DI container as a service locator, and yet another reason why it service locator is considered to be anti-pattern.
Your contrived example would not happen if you were using the dependency injection pattern. Refactoring your example to use dependency injection, it would look like this:
public interface IFileExistanceCheck
{
bool FileExist(string filePath);
}
public class FileExistanceChecker : IFileExistanceCheck
{
private readonly IFileSystem fileSystem;
public FileExistanceChecker(IFileSystem fileSystem)
{
if (fileSystem == null)
throw new ArgumentNullException(nameof(fileSystem));
this.fileSystem = fileSystem;
}
// Pass runtime data through the method parameters!
public bool FileExist(string filePath)
{
// Prevent an empty file path from being used
if (string.IsNullOrEmpty(filePath))
throw new ArgumentNullException(nameof(filePath));
return this.fileSystem.File.Exists(filePath);
}
}
Foo Class
public class Foo
{
private readonly IFileExistanceCheck fileExistanceCheck;
public Foo(IFileExistanceCheck fileExistanceCheck)
{
if (fileExistanceCheck == null)
throw new ArgumentNullException(nameof(fileExistanceCheck));
this.fileExistanceCheck = fileExistanceCheck;
}
public void Bar()
{
// do something
var filePath = SomeMagicString;
var fileExist = fileExistanceCheck.FileExist(filePath);
// do the rest of code
}
}
At the composition root, Ninject would tie it all together and get Foo running.
class Program
{
static void Main(string[] args)
{
// Begin composition root
var kernel = new StandardKernel();
kernel.Bind<IFileSystem>().To<FileSystem>();
kernel.Bind<IFileExistanceCheck>().To<FileExistanceChecker>();
var app = kernel.Get<Foo>();
// End composition root
app.Bar();
}
}
If there is a need to check for the existence of the filePath parameter, this check can be done using a guard clause. You only need to use the dependency injection pattern, and pass the runtime data (the file path) through a method parameter.
If you want the filePath to be a configuration value that is passed through the constructor at application startup, then having a service to check for file existence seems rather pointless. In that case, you should check whether the file exists before allowing your application to run.
class Program
{
static void Main(string[] args)
{
var filePath = "SomeFileThatShouldExist.txt";
// Check configuration
if (!File.Exists(filePath))
throw new InvalidOperationException("Invalid configuration");
// Begin composition root
var kernel = new StandardKernel();
kernel.Bind<Foo>().To(new Bar(filePath));
// Register other services...
var app = kernel.Get<Foo>();
// End composition root
app.Bar();
}
}
If your parameter is known only in runtime you can inject factory and create your FileExistenceChecker with it:
public interface IFileExistenceCheckerFactory
{
IFileExistenceCheck Create(string path);
}
...
var kernel = new StandardKernel();
kernel.Bind<IFileExistenceCheck>().To<FileExistenceChecker>();
kernel.Bind<IFileSystem>().To<FileSystem>();
kernel.Bind<IFileExistenceCheckerFactory>().ToFactory(() => new TypeMatchingArgumentInheritanceInstanceProvider());
var factory = kernel.Get<IFileExistenceCheckerFactory>();
var checker = factory.Create("SomeMagicString");
var fileExist = checker.FileExist();
Then even if your parameter names don't match, TypeMatchingArgumentInheritanceInstanceProvider ensure that parameters will be matched by its type.
I'd like to be able to specify an Action<string> at the app level that my library could then use for progress reporting. ConfigurationManager.AppSettings only allows XmlSerializeables, and Actions are not that.
The motivation is that console apps might just write to the console, webapps perhaps to a trace, and forms perhaps to files or a particular field, the point is the app should be able to configure it imo.
My approach currently is to have in the library a LibSettings class that has a static settable Action<string>. That means anyone can set it elsewhere too, which poses potential for bugs.
At first I thought maybe a static constructor (with parameters) would do but it turns out you can't call static constructors explicitly and you certainly can't give them parameters.
Is there any way to achieve my goal of being able to specify the Feedback action once and only onc in some sort of custom app settings, and not throw a runtime exception on second setting, or swallow the second setting? That is essentially like a singleton property of my design when I design it. Thanks in advance.
Serializing and deserializing a delegate usually isn't a good idea, as it easily leads to pretty serious security concerns (see arbitrary code execution).
Instead I would recommend having a enum or similar serializable type that identifies a number of statically defined functions and convert between them. Something like this:
public enum FeedbackAction
{
Console,
Trace,
...
}
public static class FeedbackActions
{
public static void Console(string text) { ... }
public static void Trace(string text) { ... }
public static Action<string> GetAction(FeedbackAction action)
{
switch (action)
{
case FeedbackAction.Console:
return Console;
case FeedbackAction.Trace:
return Trace;
default:
throw new ArgumentException("Invalid feedback action.", nameof(action));
}
}
}
Now whenever you're trying to use the app setting, just call FeedbackActions.GetAction to convert between your enum values and the appropriate Action<string>.
For example:
public static class Feedback
{
public static Action<string> feedbackAction;
public static object syncLock = new object();
public static void ProvideFeedback(string text)
{
if (feedbackAction == null)
{
// synchronize to avoid duplicate calls
lock (syncLock)
{
if (feedbackAction == null)
{
var value = ConfigurationManager.AppSettings["FeedbackAction"];
feedbackAction = FeedbackActions.GetAction(value);
}
}
}
feedbackAction(text);
}
}
This way you can safely call Feedback.ProvideFeedback, and its behavior will be driven by the app/web.config file.
If you need to make a solution that's flexible enough to handle almost any feedback action, I'd strongly recommend reading up on inversion of control in general and the Managed Extensibility Framework (MEF) in particular. A full implementation would be a bit too complex to provide here, but in general it would look a bit like this:
public interface IFeedbackAction
{
void ProvideFeedback(string text);
}
public interface IFeedbackMetadata
{
string Name { get; }
}
[Export(typeof(IFeedbackAction)), ExportMetadata("Name", "Console")]
public interface ConsoleFeedbackAction : IFeedbackAction { ... }
[Export(typeof(IFeedbackAction)), ExportMetadata("Name", "Trace")]
public interface TraceFeedbackAction : IFeedbackAction { ... }
public static class Feedback
{
[ImportMany]
public IEnumerable<Lazy<IFeedbackAction, IFeedbackMetadata>> FeedbackActions { get; set; }
private IFeedbackAction feedbackAction;
public static void ProvideFeedback(string text)
{
if (feedbackAction == null)
{
// synchronize to avoid duplicate calls
lock (syncLock)
{
if (feedbackAction == null)
{
var value = ConfigurationManager.AppSettings["FeedbackAction"];
feedbackAction = GetFeedbackAction(value);
}
}
}
feedbackAction.ProvideFeedback(text);
}
private static IFeedbackAction GetFeedbackAction(string name)
{
return FeedbackActions
.First(l => l.Metadata.Name.Equals(name)).Value;
}
}
With this method, consumers would be able to provide their own implementation of IFeedbackAction, decorated with the appropriate [Export] and [ExportMetadata] attributes, and simply specify use of their custom actions in the app/web.config file.
Ok, let's see if I inderstood all right.
Let's suppose this is your config class:
public static class LibSettings
{
public static readonly Action<string> TheAction{ get; private set; }
static LibSettings()
{
var action = ConfigurationManager.AppSettings["libAction"];
switch(action)
{
case "console":
TheAction = ConsoleAction;
break;
case "web":
TheAction = WebAction;
break;
//And as many as you need...
}
}
private static void ConsoleAction(string Parameter)
{
//Whatever it does...
}
private static void WebAction(string Parameter)
{
//Whatever it does...
}
}
Is this what you meant? it will be only set once whenever you access any property of the class, it cannot be modified externally and will change the Action upon an AppSeting record.
Ok, let's go with another approach. Now we will have two classes a temporal holder where you will set the action you want and the current settings class.
public static class TemporalHolder
{
public static Action<string> HeldAction{ get; set; }
}
public static class LibSettings
{
public static readonly Action<string> TheAction;
static LibSettings()
{
TheAction = TemporalHolder.HeldAction;
}
public static void Init()
{
/*Just do nothing as we will use it to fire the constructor*/
}
}
And now, to use it, just seth the action to the temporal holder and call anithing static on LibSettings:
TemporalHolder.Action = (your function);
LibSettings.Init();
And voila! no errors on second settings, it cannot be changed on runtime and cannot be reasigned. are all the conditions met?
I have a service that creates an app domain and starts it:
this._appDomain = AppDomain.CreateDomain(this._appName, AppDomain.CurrentDomain.Evidence, appDomainSetup);
this._startStopControllerToRun = (IStartStop)this._appDomain.CreateInstanceFromAndUnwrap(assemblyName, this._fullyQualifiedClassName);
this._startStopControllerToRun.Start();
That has been running well for a long time now. The issue is when the controller, started within this app domain, calls a framework logging class. The logger gets the entry assembly name and records that as the source in the event log. This is how the logger gets the source (caller) name:
private static string GetSource()
{
try
{
var assembly = Assembly.GetEntryAssembly();
// GetEntryAssembly() can return null when called in the context of a unit test project.
// That can also happen when called from an app hosted in IIS, or even a windows service.
if (assembly == null)
{
// From http://stackoverflow.com/a/14165787/279516:
assembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;
}
return assembly.GetName().Name;
}
catch
{
return "Unknown";
}
}
Before adding that if check, the logger would record "Unknown" for the source. After some research, I added that attempt in the if block. Now the logger records "mscorlib" as the source (entry assembly name).
This is the overview:
Host -> Controller (running within app domain)
How can I get the name of the assembly (that has the controller) running within the domain?
Note: I also tried this (below), but it gives me the name of the framework where the logging class exists (not the name of the assembly in which the controller is running within the app domain):
assembly = Assembly.GetExecutingAssembly();
This is perhaps one way to do what you want. What I'm demonstrating here is passing and receiving metadata to the created AppDomain via SetData and GetData methods so disregard how I am creating the actual remote type.
using System;
using System.Reflection;
namespace ConsoleApplication13
{
class Program
{
static void Main(string[] args)
{
AppDomain appDomain = AppDomain.CreateDomain("foo");
appDomain.SetData(FooUtility.SourceKey, FooUtility.SourceValue);
IFoo foo = (IFoo)appDomain.CreateInstanceFromAndUnwrap(Assembly.GetEntryAssembly().Location, typeof(Foo).FullName);
foo.DoSomething();
}
}
public static class FooUtility
{
public const string SourceKey = "Source";
public const string SourceValue = "Foo Host";
}
public interface IFoo
{
void DoSomething();
}
public class Foo : MarshalByRefObject, IFoo
{
public void DoSomething()
{
string source = AppDomain.CurrentDomain.GetData(FooUtility.SourceKey) as string;
if (String.IsNullOrWhiteSpace(source))
source = "some default";
Console.WriteLine(source);
}
}
}
Which outputs:
Foo Host
Press any key to continue ...
So in your case, you could pass whatever source metadata to the AppDomain:
this._appDomain = AppDomain.CreateDomain(this._appName, AppDomain.CurrentDomain.Evidence, appDomainSetup);
this._appDomain.SetData("Source", "MyController");
this._startStopControllerToRun = (IStartStop)this._appDomain.CreateInstanceFromAndUnwrap(assemblyName, this._fullyQualifiedClassName);
this._startStopControllerToRun.Start();
and in your GetSource method check for its existence.
private static string GetSource()
{
try
{
string source = AppDomain.CurrentDomain.GetData("Source") as string;
if (!String.IsNullOrWhiteSpace(source))
return source;
var assembly = Assembly.GetEntryAssembly();
// GetEntryAssembly() can return null when called in the context of a unit test project.
// That can also happen when called from an app hosted in IIS, or even a windows service.
if (assembly == null)
{
// From http://stackoverflow.com/a/14165787/279516:
assembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;
}
return assembly.GetName().Name;
}
catch
{
return "Unknown";
}
}
UPDATE ALTERNATIVE
You could also declare a public interface method for setting the source on a static location in the target domain as well.
using System;
using System.Reflection;
namespace ConsoleApplication13
{
class Program
{
static void Main(string[] args)
{
AppDomain appDomain = AppDomain.CreateDomain("foo");
IFoo foo = (IFoo)appDomain.CreateInstanceFromAndUnwrap(Assembly.GetEntryAssembly().Location, typeof(Foo).FullName);
foo.SetSource("Foo Host");
foo.DoSomething();
}
}
public interface IFoo
{
void DoSomething();
void SetSource(string source);
}
public class Foo : MarshalByRefObject, IFoo
{
public void DoSomething()
{
string source = Foo.Source;
if (String.IsNullOrWhiteSpace(source))
source = "some default";
Console.WriteLine(source);
}
public static string Source{get; private set;}
public void SetSource(string source)
{
Foo.Source = source;
}
}
}
I ran into a situation where somewhere buried in the .net code, it was relying on Assembly.GetEntryAssembly(). It would take the returned assembly and inspect it for an assembly level attribute. Which is going to fail if code is in an app domain.
Long story short, I had to work around this same issue. The solution is ugly, I hate that I needed to do this but it worked...
If you read the docs here - Assembly.GetEntryAssembly() Method
It contains this section:
Return Value
Type: System.Reflection.Assembly
The assembly that is the process executable in the default application domain, or the
first executable that was executed by AppDomain.ExecuteAssembly. Can
return null when called from unmanaged code.
To get around this, I added some code to my exe which makes the process exit if "/initializingappdomain" is passed as an argument.
Here is some code to do this...
// 1. Create your new app domain...
var newDomain = AppDomain.CreateDomain(...);
// 2. call domain.ExecuteAssembly, passing in this process and the "/initializingappdomain" argument which will cause the process to exit right away
newDomain.ExecuteAssembly(GetProcessName(), new[] { "/initializingappdomain" });
private static string GetProcessName()
{
return System.IO.Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName.Replace(".vshost", ""));
}
// 3. Use your app domain as you see fit, Assembly.GetEntryAssembly will now return this hosting .net exe.
Again, this is far from ideal. There are better solutions if you can avoid this but if you find yourself in a situation where you don't own the code relying on Assembly.GetEntryAssembly(), this will get you by in a pinch.
So here's a sample singleton:
public class Singleton
{
private static Singleton _instance = new Singleton();
private Singleton()
{
//do stuff
}
public static Singleton Get()
{
return _instance;
}
}
I'm trying to run multiple tests on this singleton class, and the first one always works, naturally. Basically, I'm able to see that when I create a new Singleton() the constructor is called. Or, when I call Singleton.Get() the constructor is called. The tests work fine individually, but then the static field has been set, and even if I manually set it to null, it still won't reset itself for the next test. This is a problem for me as automated testing is important here. So how can I get it to reset itself?
So far, I researched AppDomain and found that I could put each test into its own assembly and run it manually. But of course, VS already uses a unique AppDomain for each assembly which would allow for that to happen automatically, but that's a LOT of test assemblies and seems a bit silly. I'm looking for an alternative to that.
Note: I don't want advice about code quality. This is code I'm testing, not that I've written. I will not change the code until I've tested it first. I researched this thoroughly and did not find a question about this that was not answered with "You shouldn't use singletons". I am not interested in code advice. This is StackOverflow, not CodeReview.
Additional Information Requested
I am running tests currently using Visual Studio's Built-In Testing framework. I would prefer that this still worked when it is moved to MSBuild. That doesn't rule out manually triggering an external application for the tests, but it does make it harder to do.
You're misunderstanding AppDomains.
You can simply create a new AppDomain for each test, load all of the test assemblies into it, and invoke only one test method in each domain.
It would probably be rather slow, though.
You can design your Singleton class like this:
public class Singleton
{
private static Singleton _instance;
private static object _instanceLock = new object();
private Singleton()
{
//do stuff
}
public static Singleton Get()
{
if (_instance == null)
{
lock(_instanceLock)
{
if (_instance == null)
{
_instance = new Singleton();
}
}
}
return _instance;
}
public static void Clear()
{
if (_instance != null)
{
lock(_instanceLock)
{
if (_instance != null)
{
_instance = null;
}
}
}
}
}
Then you'd have to call Singleton.Clear() before you begin each test, like this:
[TestInitialize]
public void Initialize()
{
Singleton.Clear();
}
Write a console program that has a command-line argument to determine which of those singleton tests to run. Then call that multiple time from a batch file (or bash or powershell, whichever you prefer). It's extra work but it will let you test this code in a fresh new environment every time. Or you could try to figure out whether there's some option in your current tool to do that.
Perhaps something like that:
static int Main(string[] args) {
try {
int testcase = (Int32.Parse(args[0]));
RunTest(testcase);
} catch (Exception x) {
Console.WriteLine("test failed: "+x.Message);
return 1;
}
Console.WriteLine("test passed.");
return 0;
}
After numerous bits of advice from #redtuna #drch and #SLaks and lots of googling we have determined a way to do this.
Step 1: Your test class needs to inherit from MarshalByRefObject
public class UnitTest1 : MarshalByRefObject
Step 2: This next piece is a custom method to run each test in it's own AppDomain.
private void RunTestInCustomDomain(string methodName)
{
// selecting the dll from the debug directory using relative directories
var testDll = #"..\..\..\UnitTests\bin\Debug\UnitTests.dll";
// first verify the dll exists
Assert.IsTrue(File.Exists(testDll));
var assemblyName = AssemblyName.GetAssemblyName(testDll).FullName;
var domain = AppDomain.CreateDomain(methodName, null, new AppDomainSetup()
{
// This is important, you need the debug directory as your application base
ApplicationBase = Path.GetDirectoryName(testDll)
});
// create an instance of your test class
var tests = domain.CreateInstanceAndUnwrap(assemblyName, typeof(UnitTest1).FullName) as UnitTest1;
var type = tests.GetType();
var method = type.GetMethod(methodName);
// invoke the method inside custom AppDomain
method.Invoke(tests, new object[]{});
// Unload the Domain
AppDomain.Unload(domain);
}
Step 3: The next trick is that your Test Method is calling this custom method, so your tests are written into public methods without the [TestMethod] attribute.
[TestMethod]
[DeploymentItem("UnitTests.dll")]
public void TestMethod1()
{
RunTestInCustomDomain("actual_TestMethod1");
}
public void actual_TestMethod1()
{
// Assert Stuff
}
For Completeness: If you need to run initialization or cleanup for each test, they need to be called manually because the TestMethod is running in a different AppDomain from the actual_TestMethod
public void actual_TestMethod1()
{
// Assert Stuff
TestCleanup();
}
Edit: I should note that because these methods are being run under a separate AppDomain, Code Coverage doesn't work :(. If anyone finds a way to get that functionality in addition to this feature, please let me know.
Just an idea:
Setting it to null just sets your variable to null instead of what it's pointing at, of course. But what if you could get the Singleton itself... Well, it seems that that can be done using Expressions. See Getting the object out of a MemberExpression?