Identifying the assembly of a composable part - mef

Suppose I define an export in AssemblyA:
[Export(typeof(Foo))]
public class Foo
{
...
}
Is there a way to determine the export of Foo is from AssemblyA in the CompositionContainer?
What I am trying to do, is for any given assembly that provides MEF exports, it must also include one MEF export of a specific interface. And I want to be able to flag any assemblies that do not satisfy this rule.

There's no way to do this from in a CompositionContainer. I would suggest creating an AssemblyCatalog for each assembly and checking whether any of its Parts have an ExportDefinition matching the interface you want.

You can identify the type and assembly of a ComposablePartDefinition using the static ReflectionModelServices.GetPartType method. The following example creates a list of distinct assemblies that contain types included in the container's catalog:
var assemblies = container.Catalog.Parts
.Select(part => ReflectionModelServices.GetPartType(part).Value.Assembly)
.Distinct()
.ToList();

Related

Excluding classes instantiated via IOC container from NDepend warning

My NDepend reports warn that 'Non-static classes should be instantiated or turned to static.' Most of the classes in the returned list are registered via my IOC container (Unity) and instantiated by my IOC framework at runtime.
Given that NDepend is performing static analysis, it's not going to be aware of the runtime instantiation. I'm hoping that it's possible to tweak the CQL to make it aware of my container.
This is the boilerplate query that NDepend executes:
warnif count > 0
from t in JustMyCode.Types
where t.IsClass &&
//!t.IsPublic && // if you are developping a framework,
// you might not want to match public classes
!t.IsStatic &&
!t.IsAttributeClass && // Attributes class are never seen as instantiated
!t.DeriveFrom("System.MarshalByRefObject".AllowNoMatch()) // Types instantiated through remoting infrstructure
// find the first constructor of t called
let ctorCalled = t.Constructors.FirstOrDefault(ctor => ctor.NbMethodsCallingMe > 0)
// match t if none of its constructors is called.
where ctorCalled == null
select new { t, t.Visibility }
Is it possible for me to tweak this query to exclude classes that are referenced in my IOC container registration?
Indeed, you can vote for NDepend supports of IoC Framework on the NDepend User Voices site. This is a feature that will be implemented in the future.
For now, you can handle this with an attribute. Create an attribute class in your code, for example named MyProduct.IoCInstantiatedAttribute.
Then you can tag all your classes instantiated solely by IoC with this attribute. Since this attribute is only needed on DEBUG build (the one analyzed by NDepend) I'd advice using a conditional DEBUG syntax.
#if DEBUG
[IoCInstantiated]
#endif
class MyClass { ... }
Finally, you just have to add && !t.HasAttribute("MyProduct.IoCInstantiatedAttribute") in your concerned rule(s) et voilĂ !
Additionally, you can also write a rule to make sure that classes that have this attribute are not instantiated somewhere. This way you'll keep the usage of this attribute clean!
// <Name>Types tagged with attribute IoCInstantiated must not be instantiated elsewhere</Name>
warnif count > 0
from t in Types
where t.HasAttribute ("MyProduct.IoCInstantiatedAttribute")
let methodsInstiatingMe = Application.Methods.ThatCreateA(t)
where methodsInstiatingMe.Any()
select new { t, methodsInstiatingMe }
Personally I found that using such attribute is great, because it also documents code. When a developer is reviewing such class, he can have this important piece of information (instantiated solely through IoC) at a glance.

Auto-wiring for Ninject

Some of the IOC containers have what's called auto-wiring based on conventions, for e.g., IProductRepository maps to ProductRepository without any manual wiring on your part.
Is there such a thing with Ninject?
// use Ninject.Extensions.Conventions for convention-based binding
kernel.Scan(scanner =>
{
// look for types in this assembly
scanner.FromCallingAssembly();
// make ISomeType bind to SomeType by default (remove the 'I'!)
scanner.BindWith<DefaultBindingGenerator>();
});
copied from #Pete Montgomery comment
Ninject comes with an extension for convention based configuration. But you still need to configure your convenions. See https://github.com/ninject/ninject.extensions.conventions The syntax has changed for 3.0.0 but has become much more powerful. The following would add bindings for all classes in your system. But normally you want several of these conventions for different kind of classes (e.g. services are singletons, ....)
kernel.Bind(
x => x.FromThisAssembly()
.SelectAllClasses()
.BindAllInterfaces());

CodeDom - Linking multiple classes within a single Assembly

I have a C# application that I am trying to re-create through the use of CodeDom. This application has four classes inside of it. If I were to go into this applications directory, I would find the project file (App.csproj), and if I were to start this project file, all four classes would load together. Furthermore, if I were to build this application, all four classes would build together.
My Question: How on earth can I create this functionality through the use of CodeDom?
I have sucessfully created one of the four classes using CodeDom, but how can I go about creating the next three classes (and linking them) to the first class that I already created?
I know this may sound confusing but I will explain more if necessary.
If the classes are in the same namespace you can add them all to one CodeNamespace object and generate the code from that.
If there in different namespaces you can add the namespace of the other Classes to your first class by adding the namespaces reference of the other class's to the namespace object you are working in:-
// Add the Namespace of the other class to the current namespace onject
defaultNameSpace.Imports.Add(new CodeNamespaceImport("Project.Namespace.Namespace"));
Where defaultNameSpace is a type of CodeNamespace. The first Class you have built is added to this CodeNamespace object as below and then the code is generated from that :-
defaultNameSpace.Types.Add(mainClass);
mainClass being a type of CodeTypeDeclaration.
Hope this helps.

How can I add an existing instance to a MEF catalog?

I have an object instance, and I want to end up with a MEF catalog that contains that object instance, exported as a specific interface type. How can I do this?
TypeCatalog doesn't seem workable here, because (a) it creates a new instance instead of using an existing one, and (b) it requires the type to have an [Export] attribute. In my case, the instance comes from MEF's metadata system, so MEF creates the underlying type and I can't add attributes to it.
As far as I can tell, the usual advice is, if you've got an existing instance, you should add it to the container (e.g. via CompositionBatch), not to the catalog. But when I add this instance, I'm also adding an entire AssemblyCatalog worth of types, all in the same operation. I'll also want to be able to remove all of these types later. It makes more sense to me to bundle everything into an AggregateCatalog. That way, I can add both the assembly and the instance in one atomic operation, and I can remove them all again the same way.
For example:
// Bootstrapper code to initialize MEF:
public void Configure() {
_selectedGameCatalog = new AggregateCatalog();
var globalCatalog = new AggregateCatalog(_selectedGameCatalog);
_container = new CompositionContainer(globalCatalog);
// ... more MEF initialization ...
}
// Sometime later, I want to add more stuff to the MEF ecosystem:
public void SelectGame(Lazy<Game, IGameMetadata> entry) {
var newCatalog = new AggregateCatalog();
// Make the assembly available to import:
newCatalog.Catalogs.Add(new AssemblyCatalog(entry.Value.GetType().Assembly));
// I also want the metadata to be available to import:
IGameMetadata metadata = entry.Metadata;
newCatalog.Catalogs.Add(MakeCatalogFromInstance<IGameMetadata>(metadata));
// Replace whatever game was selected before:
_selectedGameCatalog.Catalogs.Clear();
_selectedGameCatalog.Catalogs.Add(newCatalog);
}
The part I don't know how to do is "MakeCatalogFromInstance". How can I create a catalog that contains an existing instance (registered as a specific type)?
Or, alternatively, if I'm going about this all wrong, is there a better way to plug an entire catalog and an existing instance all into MEF at the same time, with the ability to unplug them all again later and replace them with something else?
I think it's probably best to add the types to the catalog and then add the instance to the container.
Catalogs contain part definitions. Part definitions are used to create parts. (The types for this are ComposablePartDefinition and ComposablePart.) So you could theoretically write your own catalog and a part definition that always returned a part corresponding to the instance when CreatePart was called. But catalogs weren't really designed to be used this way.
For prosperity...
MEF devivides the chores of what type info is to be used (catalog) from the actual running object instances (container). To me it is a logical descicion, especially when you setup a more complex MEF environment in your application.
If you want the ability to 'change' containers on the fly, I would suggest you try to use hierarchical containers. The root catalog/container is filled with static types and any of the child containers can be filled with each specific set of meta types you need for your game.
Hope it helps,
Marc

Namespace or type specified in project level imports does not contain a public member

I have an ASP.NET 3.5 web application project in which I'm trying to implement a searchable gridview. I originally started the project as a web site and converted it to a web application. After conversion, my class ended up in the folder Old_App_Code and is called SearchGridView.vb.
Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Text
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Drawing.Design
<Assembly: TagPrefix("MyApp.WebControls", "SearchGridView")>
Namespace MyApp.WebControls
#Region "TemplateColumn"
Public Class NumberColumn
Implements ITemplate
Public Sub InstantiateIn(ByVal container As System.Web.UI.Control) Implements System.Web.UI.ITemplate.InstantiateIn
End Sub
End Class
#End Region
<ToolboxData("<{0}:SearchGridView runat=server></{0}:SearchGridView>")> _
<ParseChildren(True, "SearchFilters")> _
Public Class SearchGridView
Inherits GridView
The class file continues, but this is the first part of it.
Unfortunately, I receive the error message
Warning 1 Namespace or type specified in the project-level Imports 'MyApp.WebControls' doesn't contain any public member or cannot be found. Make sure the namespace or the type is defined and contains at least one public member. Make sure the imported element name doesn't use any aliases. DielWebProj
In web.config, I included a namespace tag for MyApp.WebControls and I included an imports tag in the .aspx page as well.
Can anyone shed light as to why this error is being raised and how I would remedy it?
Thanks,
Sid
I have a broadly similar problem to you. I have a website project using a custom control, inheriting from GriView, in the app_code folder. I was recieving the very same error, but noted that it happened only after I would add a second class or module to app_code, and would disappear if I removed it.
So the workaround I have at the moment is to just leave my custom control as the sole occupant of app_code.
One option might be to make the control part of its own project and add it as a reference to the we site/app?
I'll update this if I can find a decent solution.
EDIT:
Well, in my case it was because the control I was using was written in C#, whereas the rest of the project, and classes I added to app_code, were in VB.
The app_code folder is compiled to a single assembly, so classes of different languages cannot share it, unless you create seperate sub-folders and do some config file jiggerypokery. More details here