Is it possible to use Autofac somehow within Unity3d? - unity3d

I am using Unity3d 2018.1.0f3 which is using .net 4.6x and has netstandard 2.0. Does anyone know if its possible to get Autofac working within Unity? Currently, I get an error saying that it unloaded the assemblies due to possible runtime issues.
Thanks,
MH

I have no idea about the other stuff, and I use an older version of it, but I use AutoFac (3.5.2) with Unity with no issues at all. I think it needs slightly different setup for unity but not really sure. but I have an editor window system that uses ScriptableObjects for databases and seperate pages for different item types, and use autofac to inject the databases, and find all the sub pages and auto load them.
this is how I do it if this helps:
private ISWeaponDatabase weaponDb;
private ISQualityDatabase qualityDb;
private ISArmorDatabase armorDb;
private int currentTab;
private ContainerBuilder builder;
private BasePage[] pages;
DatabaseConfig dbConfig = Resources.Load<DatabaseConfig>("DatabaseConfig");
if (dbConfig == null) return;
if (weaponDb == null) weaponDb = ISWeaponDatabase.GetDatase<ISWeaponDatabase>(dbConfig.DataFolder, dbConfig.WeaponDatabase.DatabaseFile);
if (qualityDb == null) qualityDb = ISWeaponDatabase.GetDatase<ISQualityDatabase>(dbConfig.DataFolder, dbConfig.QualityDatabase.DatabaseFile);
if (armorDb == null) armorDb = ISArmorDatabase.GetDatase<ISArmorDatabase>(dbConfig.DataFolder, dbConfig.ArmorDatabase.DatabaseFile);
builder = new ContainerBuilder();
builder.RegisterInstance(weaponDb).As<ISWeaponDatabase>();
builder.RegisterInstance(armorDb).As<ISArmorDatabase>();
builder.RegisterInstance(qualityDb).As<ISQualityDatabase>();
builder.RegisterAssemblyTypes(Assembly.GetAssembly(GetType())).Where(t => t.Name.EndsWith("EditorPage")).As<BasePage>().SingleInstance();
IContainer container = builder.Build();
pages = container.Resolve<IEnumerable<BasePage>>().ToArray();

Yes, it is possible, would be nice to know what you tried until now, but basically, you'll import it as you'd import every other 3rd party library.
I assume you are on an up-to-date Windows Machine and you are using VS because you didn't give us any information on that.
Fire up your NuGet PM in VS (Tools > NuGet Package Manager > Console)
Command: Install-Package Autofac -Version 4.8.1
Or your preferred way to get a NuGet package https://www.nuget.org/packages/Autofac/ (VS is of course also working)
Now you should have a package in /Packages/Autofac.4.8.1/lib/net45
Copy the DLL over to your assets/lib folder (or wherever you want to have it)
If you go back to Unity it will load Autofac now
Now you can go to any class you have and type "using Autofac" and get started.

Related

issue changing properties on project items using multi-project vstemplate with IWizard

I am creating a VSIX extension that generates a multi-project solution to be distributed. I would like to do several things to individual items I include in each projects vstemplate but think I may be taking the wrong approach. I have created a IWizard and am able to debug the solution. I added a custom item type to the project items in vstemplate in order to identify which ones I want to flag. I am then doing the following to change a ProjectItems Build Action from Content to Compile. I am still learning Visual Studio Extensability and have very limited experience using the objects so I apologize if the below is horrendous!
public void ProjectFinishedGenerating(Project project)
{
var myproject = (VSProject)project.Object;
foreach (ProjectItem pi in myproject.ProjectItems)
{
var myBuildAction = pi.Properties.Item("ItemType").Value;
if myBuildAction == "CompileContent")
pi.Properties.Item("ItemType").Value = "Compile";
}
}
This is my item in vstemplate
<VSTemplate Version="3.0.0" Type="Project" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" xmlns:sdk="http://schemas.microsoft.com/developer/vstemplate-sdkextension/2010">
<ProjectItem TargetFileName="MyFolder\$ext_safeprojectname$\$ext_safeprojectname$_CodeFile.cs" ReplaceParameters="true" ItemType="CompileContent">CodeFile.cs</ProjectItem>

How can I share global configuration fields when writing a jenkins plugin?

I'm currently writing a jenkins plug-in that has multiple builders. I wanted to share the fields in the descriptor/global.jelly across all builders. How can I share this information? Can I use inheritance or encapsulation?
A good place to start is to search the Jenkins github repository
The code you want is
Jenkins.getInstance().getDescriptor( MyPluginWithGlobalConfig.class )
Which will give you the descriptor back you want (as there is only one instance of the descriptor)
Here is one I used in a plugin (in groovy) which fetches a descriptor then calls a method on it source file
#Override
public List<String> rebuild(List<String> list){
SeleniumDynamicCapability.DescriptorImpl sdcd = Jenkins.getInstance().getDescriptor(SeleniumDynamicCapability.class)
List<SeleniumCapabilityRO> sc = sdcd.loadDefaultItems()
if (sc.size() == 0)
throw(new SeleniumException("No selenium capabilities detected"))
setSeleniumCapabilities(sc)
sc.each{list.add(it.toString())}
return list;
}

CM+MEF: registering MEF exports from external assemblies

this post ideally continues my other post on MEF plugins, but my first post was too full of comments and this sample is more complete. Here I summarize my updated scenario, with all my findings up to this point. Hope this can be useful to other CM newbies like me.
You can download a full repro sample scenario: it's an almost do-nothing dumb skeleton for a CM + MEF plugins-based application:
VS2010 repro solution (updated)
This is a minimal stripped down solution representing my issues with CM+MEF.
There are 3 projects:
the main UI (CmRepro).
a core DLL shared among all the addins (AddinCore), with a couple of interfaces and custom attributes used for MEF metadata.
a sample addin DLL (AlphaAddin), with a view and a viewmodel implementing the interfaces.
The core contains 2 interfaces representing a viewmodel and its view, and 2 attributes to be used for decorating the viewmodels and the views. The viewmodel interface describes a class which should compose a greeting message from some person name, so it exposes a couple of properties and a method for this. The view interface just exposes a property returning its DataContext cast to the viewmodel interface.
The sample addin has an implementation for a viewmodel and a view; both are MEF-exports, decorated by the corresponding attribute. In the real-world solution several properties of these attributes are used for filtering; here I just have a dummy Language property which should allow for other plugins for different languages.
The main UI has a MEF bootstrapper which adds code for retrieving MEF exports from an Addins folder. I modified this code to include exports from MEF directories and get a better understanding of some MEF exceptions, but still I cannot figure out how to properly "register" them with CM.
The main viewmodel has 2 methods: one (A) uses a MEF catalog to retrieve a viewmodel and its view, bind them and show them into a window. Another (B) uses the same catalog to get a viewmodel, and then a CM window manager to locate, create, bind and show the corresponding view according to CM naming conventions. These methods represent two alternative ways I should deal with in my real-world code, i.e. instantiating some crucial objects "by myself" just using MEF but then let them work for CM, or letting CM (with a MEF-bootstrapper) do most of the work starting from a viewmodel.
Anyway, it seems that in both cases I am missing something as for registration with CM. Issues:
(A) how do I wire up VM+V for CM so that the conventions for databinding etc are applied? At this time I can build my MEF parts together, but CM ignores them as it was not used to instantiate none of them.
I answer to myself here:
ViewModelBinder.Bind(viewmodel, (UserControl)view, null);
(B) how do I register the exports from MEF in CM so that the CM window manager can find the view? Currently it does not manage to locate the view from the viewmodel.
Addition (21 jun)
I try to explain better for whom cannot access the repro solution. I use a "standard" MEF bootstrapper, changing the Configure override like:
_container = new CompositionContainer(
new AggregateCatalog(AssemblySource.Instance.Select(
x => new AssemblyCatalog(x)).OfType()
.Union(GetAddinDirectoryCatalogs())));
this creates a MEF composition container which aggregates the catalog from AssemblySource, with CM types like event aggregator or window manager, with a catalog from several addin directories, which contain exports for both V and VM's.
In my sample main viewmodel I create a new VM from a plugin, found in the host application directory among others, and I'd like a CM window manager to locate, instantiate and show its view in a dialog, e.g.:
viewmodel = GetMyViewModelFromAddin();
windowmanager.ShowDialog(viewmodel);
CM anyway cannot locate the view. AFAIK, naming conventions are honored: both V and VM are in the same addin assembly, marked as MEF exports, named like SomethingViewModel / SomethingView. Anyway, as Leaf pointed out in his clarification, AssemblySource.Instance is a static IObservableCollection collection of assemblies and I have not added my addins to it. But this is right the point: I would not like to add all of them in advance, because this means loading ALL the addins without yet knowing which (if any) will be ever used. A robust plugin system is the reason for using MEF, after all. I'm new to CM and not sure if it is possible (and where) to find an extension point for CM in this scenario. The window manager does not call my bootstrapper implementation at all, clearly because there is nothing to be instantiated by IoC, as no match was found in assembly source Instance. So, it seems I'm stuck here, the only solution being loading in advance all the assemblies in the Instance, but this seems defeating the whole purpose of using MEF.
The plugin-based application I'm developing loads tons of V+VM CM "pairs" representing user interface widgets, which in turn often use a window manager to popup other V+VM's pairs as dialogs. I can bypass instantiation by CM and use MEF to retrieve V+VM for each widget, but still I'm facing the same view location issue for each widget requiring a window manager. The other alternative (workaround) I can see is avoding the use of window manager and implement my own mechanism for the purpose of showing dialogs from widgets, but this makes feel me a little wrong about CM...:). Usually when I find myself writing much more code than expected I am inclined to think I'm not using the tool in the right way. Any idea?
Caliburn.Micro goes through 3 stages to find a view from a viewmodel instance.
Does text transforms to transform view type name to viewmodel type name. There are a set of standard conventions for these transformations, e.g. SomeNamespace.ViewModels.CustomerViewModel might map to SomeNamespace.Views.CustomerView.
With the view type name(s), Caliburn.Micro then uses AssemblySource.Instance (a static IObservableCollection<Assembly> collection of assemblies) to find the first matching Type.
Caliburn.Micro attempts to create an instance of that Type via one of the IoC.GetInstance() methods (which delegate to your bootstrapper and hence MEF).
I'm guessing (your file share site is blocked here) that the problem with resolving views from viewmodels is due to the second step and the AssemblySource.Instance collection not containing your dynamic assembly.
One solution might be to add each dynamically loaded addin's assembly to AssemblySource.Instance when they are loaded, or if you know all assemblies at startup then you can override the Bootstrapper.SelectAssemblies method to return list of assemblies you expect to contain views and viewmodels.
Update to show how you could pull assemblies from MEF loaded parts
If you are using DirectoryCatalog to load your parts from other assemblies then you could find the assemblies used like this:
var directoryCatalog = new DirectoryCatalog(#"./");
AssemblySource.Instance.AddRange(
directoryCatalog.Parts
.Select(part => ReflectionModelServices.GetPartType(part).Value.Assembly)
.Where(assembly => !AssemblySource.Instance.Contains(assembly)));
If your addins folder changes during the runtime of the application then you would have to DirectoryCatalog.Refresh() the catalog and run the code to add any new assemblies to AssemblySource.Instance
I found a workaround. It's not that pretty, but it allows CM and its window manager to work. I summarize here my findings, hope this will help others or let someone point me to a better solution.
Given that (a) I do not want to load ALL the assemblies including all their dependencies in my plugins folder, to avoid polluting my app domain with unused stuff; and that (b) the only available CM extension point for this seems SelectAssemblies, my goal is add my assemblies there, but only the plugin assemblies requiring to be registered with CM.
So I started looking for a way of loading all the DLL's into a temporary app domain, scan them for some aspect marking them as plugins, and then load into the current app domain only the plugin ones, passing them to SelectAssemblies. This is far from being the optimal solution, as I cannot use a general purpose scanning way like MEF, and I feel I'm duplicating the effort, but at least it's a working solution.
First, to provide at least a way for loading only the plugins, I decorate my plugin assemblies requiring CM registration with a custom attribute marking them as plugins, and further enumerate their types looking for the ones which should be later used by MEF.
The attribute is as simple as:
[AttributeUsage(AttributeTargets.Assembly)]
public class AssemblyRegisteredWithCMAttribute : Attribute {}
I then found this very good piece of code for scanning assemblies into another temporary AppDomain:
http://sachabarber.net/?p=560
I had to modify it a bit, because it was failing when loading assemblies with dependencies, and it was scanning the assemblies types while in my case I just need to check for the attribute.
Here is my code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Policy;
using System.Reflection;
using System.Diagnostics.CodeAnalysis;
namespace CmRepro
{
///
/// Separate AppDomain assembly loader.
///
/// Modified from http://sachabarber.net/?p=560 .
public class SeparateAppDomainAssemblyLoader
{
///
/// Loads an assembly into a new AppDomain returning the names of the files
/// containing assemblies marked with the assembly attribute name matching
/// the specified name. The new AppDomain is then Unloaded.
///
/// list of files to load
/// assemblies directory
/// matching attribute name
/// list of found namespaces
/// null files, assembly directory or matching attribute
public List LoadAssemblies(string[] aFiles, string sAssemblyDirectory, string sMatchingAttribute)
{
if (aFiles == null) throw new ArgumentNullException("aFiles");
if (sAssemblyDirectory == null) throw new ArgumentNullException("sAssemblyDirectory");
if (sMatchingAttribute == null) throw new ArgumentNullException("sMatchingAttribute");
List<String> namespaces = new List<String>();
AppDomain childDomain = BuildChildDomain(AppDomain.CurrentDomain);
try
{
Type loaderType = typeof(AssemblyLoader);
if (loaderType.Assembly != null)
{
AssemblyLoader loader = (AssemblyLoader)childDomain.
CreateInstanceFrom(
loaderType.Assembly.Location,
loaderType.FullName).Unwrap();
namespaces = loader.LoadAssemblies(aFiles, sAssemblyDirectory, sMatchingAttribute);
} //eif
return namespaces;
}
finally
{
AppDomain.Unload(childDomain);
}
}
/// <summary>
/// Creates a new AppDomain based on the parent AppDomains
/// Evidence and AppDomainSetup.
/// </summary>
/// <param name="parentDomain">The parent AppDomain</param>
/// <returns>A newly created AppDomain</returns>
private AppDomain BuildChildDomain(AppDomain parentDomain)
{
Evidence evidence = new Evidence(parentDomain.Evidence);
AppDomainSetup setup = parentDomain.SetupInformation;
return AppDomain.CreateDomain("DiscoveryRegion", evidence, setup);
}
/// <summary>
/// Remotable AssemblyLoader, this class inherits from <c>MarshalByRefObject</c>
/// to allow the CLR to marshall this object by reference across AppDomain boundaries.
/// </summary>
private class AssemblyLoader : MarshalByRefObject
{
private string _sRootAsmDir;
/// <summary>
/// ReflectionOnlyLoad of single Assembly based on the assemblyPath parameter.
/// </summary>
/// <param name="aFiles">files names</param>
/// <param name="sAssemblyDirectory">assemblies directory</param>
/// <param name="sMatchingAttribute">matching attribute name</param>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
internal List<string> LoadAssemblies(string[] aFiles, string sAssemblyDirectory, string sMatchingAttribute)
{
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += OnReflectionOnlyAssemblyResolve;
_sRootAsmDir = sAssemblyDirectory;
List<string> aAssemblies = new List<String>();
try
{
sMatchingAttribute = "." + sMatchingAttribute;
foreach (string sFile in aFiles)
Assembly.ReflectionOnlyLoadFrom(sFile);
aAssemblies.AddRange(from asm in AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies()
let attrs = CustomAttributeData.GetCustomAttributes(asm)
where attrs.Any(a => a.ToString().Contains(sMatchingAttribute))
select asm.FullName);
return aAssemblies;
}
catch (FileNotFoundException)
{
/* Continue loading assemblies even if an assembly
* can not be loaded in the new AppDomain. */
return aAssemblies;
}
}
private Assembly OnReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs e)
{
// http://blogs.msdn.com/b/junfeng/archive/2004/08/24/219691.aspx
System.Diagnostics.Debug.WriteLine(e.Name);
AssemblyName name = new AssemblyName(e.Name);
string sAsmToCheck = Path.GetDirectoryName(_sRootAsmDir) + "\\" + name.Name + ".dll";
return File.Exists(sAsmToCheck)
? Assembly.ReflectionOnlyLoadFrom(sAsmToCheck)
: Assembly.ReflectionOnlyLoad(e.Name);
}
}
}
}
Now in my bootstrapper I override the SelectAssemblies method as follows:
...
protected override IEnumerable SelectAssemblies()
{
string sAddinPath = GetAbsolutePath(ADDIN_PATH);
FileCheckList list = new FileCheckList(sAddinPath);
// check only DLL files which were added or changed since last check
SeparateAppDomainAssemblyLoader loader = new SeparateAppDomainAssemblyLoader();
List<string> aAssembliesToRegister = loader.LoadAssemblies(list.GetFiles(null),
sAddinPath, "AssemblyRegisteredWithCM");
string[] aFilesToRegister = (from s in aAssembliesToRegister
select Path.Combine(sAddinPath, s.Substring(0, s.IndexOf(',')) + ".dll")).ToArray();
// update checklist
foreach (string sFile in aFilesToRegister) list.SetCheck(sFile, true);
list.UncheckAllNull();
list.Save();
// register required files
return (new[]
{
Assembly.GetExecutingAssembly(),
}).Union((from s in list.GetFiles(true)
select Assembly.LoadFrom(s))).ToArray();
}
...
As you can see, I am calling the loader not for all the DLL's in my addins path, but only for the ones which a cached files checklist tells me have been added or modified in that folder since the last full scan. This should speed up things a bit and does not require the checklist file to exist: if not found it will be recreated at startup by scanning all the files, if found only added or changed files will be scanned again (I use a CRC to detect changes). So I get the addins folder, create a files checklist for that folder, get from it a list of new or changed files, and pass it to the assemblies loader. This returns only the names of the DLL files which should be registered (i.e. the ones containing assemblies marked with my attribute); I then update the checklist for the next startup, and register only the required files. This way I can let my addin VM's use window managers and correctly locate the view for each required viewmodel. Somewhat ugly, but working. Thanks again to Leaf, who explained me the working of CM.

EventLogInstaller Full Setup with Categories?

It appears the MSDN docs are broken concerning creating an Event Log completely along with a definitions file for messages. I am also lost on how to setup Categories (I have custom numbers in the 3000's for messages).
Can anyone point me to a link or show sample code on how to make this right?
You should start (if you haven't done so already) here:
EventLogInstaller Class (System.Diagnostics)
The sample provided there is the foundation for what you want to do. To sum it up, build a public class inheriting from System.Configuration.Install.Installer in an assembly (could be the same DLL where you have the rest of your application, a separate DLL, or an EXE file), decorate it with the RunInstaller attribute, and add your setup code in the constructor:
using System;
using System.Configuration.Install;
using System.Diagnostics;
using System.ComponentModel;
[RunInstaller(true)]
public class MyEventLogInstaller: Installer
{
private EventLogInstaller myEventLogInstaller;
public MyEventLogInstaller()
{
// Create an instance of an EventLogInstaller.
myEventLogInstaller = new EventLogInstaller();
// Set the source name of the event log.
myEventLogInstaller.Source = "NewLogSource";
// Set the event log that the source writes entries to.
myEventLogInstaller.Log = "MyNewLog";
// Add myEventLogInstaller to the Installer collection.
Installers.Add(myEventLogInstaller);
}
}
When you have your assembly compiled, you may use the InstallUtil tool available through the Visual Studio Command Prompt to run the installer code.
Regarding the message definition file (which includes category definitions), the MSDN documentation for EventLogInstaller.MessageResourceFile mentions that you should create an .mc file, compile it, and add it as a resource to your assembly. Digging around, I found an excellent post which should guide you to the end, here:
C# with .NET - Event Logging (Wayback Machine)

Eclipse RCP: How to access internal classes of plugins?

I want to use the default XML editor (org.eclipse.wst.xml.ui) of Eclipse in an RCP application. I need to read the DOM of the xml file currently open. The plugin doesn't offer any extension point, so I'm trying to access the internal classes. I am aware that the I should not access the internal classes, but I don't have another option.
My approach is to create a fragment and an extension point to be able to read data from the plugin. I'm trying not to recompile the plugin, that's why I thought that a fragment was necessary. I just want to load it and extract the data at runtime.
So, my question is: is there another way to access the classes of a plugin? if yes, how?
Any tutorial, doc page or useful link for any of the methods is welcome.
Since nobody answered my question and I found the answer after long searches, I will post the answer for others to use if they bump into this problem.
To access a plugin at runtime you must create and extension point and an extension attached to it into the plugin that you are trying to access.
Adding classes to a plugin using a fragment is not recommended if you want to access those classes from outside of the plugin.
So, the best solution for this is to get the plugin source from the CVS Repository and make the modifications directly into the source of the plugin. Add extension points, extensions and the code for functionality.
Tutorials:
Getting the plugin from the CVS Repository:
http://www.eclipse.org/webtools/community/tutorials/DevelopingWTP/DevelopingWTP.html
Creating extensions and extension points and accessing them:
http://www.vogella.de/articles/EclipseExtensionPoint/article.html
http://www.eclipsezone.com/eclipse/forums/t97608.rhtml
I ended up extending XMLMultiPageEditorPart like this:
public class MultiPageEditor extends XMLMultiPageEditorPart implements
IResourceChangeListener {
#Override
public void resourceChanged(IResourceChangeEvent event) {
// TODO Auto-generated method stub
setActivePage(3);
}
public Document getDOM() {
int activePageIndex = getActivePage();
setActivePage(1);
StructuredTextEditor fTextEditor = (StructuredTextEditor) getSelectedPage();
IDocument document = fTextEditor.getDocumentProvider().getDocument(
fTextEditor.getEditorInput());
IStructuredModel model = StructuredModelManager.getModelManager()
.getExistingModelForRead(document);
Document modelDocument = null;
try {
if (model instanceof IDOMModel) {
// cast the structured model to a DOM Model
modelDocument = (Document) (((IDOMModel) model).getDocument());
}
} finally {
if (model != null) {
model.releaseFromRead();
}
}
setActivePage(activePageIndex);
return modelDocument;
}
}
This is not a clean implementation, but it gets the job done.