I have created an attribute that inherits LocationInterceptionAspect.
For demonstration purposes the code is as follows:
[Serializable]
public class RepeaterAttribute : LocationInterceptionAspect
{
public override bool CompileTimeValidate(PostSharp.Reflection.LocationInfo locationInfo)
{
var propertyInfo = locationInfo.PropertyInfo;
if (propertyInfo == null) return false;
if (propertyInfo.PropertyType != typeof(String))
return false;
return base.CompileTimeValidate(locationInfo);
}
public override void OnSetValue(LocationInterceptionArgs args)
{
args.Value = ((String)args.Value) + ((String)args.Value);
args.ProceedSetValue();
}
}
I have a library called External and in it is a class called Parent.
namespace External
{
public class Parent
{
public String ParentProperty { get; set; }
}
}
In a console application I have a class called Child that inherits from Parent.
The console application references the External library.
public class Child : External.Parent
{
public String ChildProperty { get; set; }
}
In my console application my code is.
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var child = new Child();
child.ParentProperty = "A";
Console.WriteLine("This should be 'AA' : '{0}'", child.ParentProperty);
child.ChildProperty = "B";
Console.WriteLine("This should be 'BB' : '{0}'", child.ChildProperty);
Console.ReadKey();
}
}
}
and in the console application's AssemblyInfo.cs I have:
[assembly: ConsoleApplication.Repeater(AttributeTargetTypes = "ConsoleApplication.Child")]
But when I run the Repeater attribute is not being applied to the inherited "ParentProperty" from the Parent class.
PostSharp is not able to change classes in different assemblies that in the one it is transforming. The base property is declared in a different assembly. This is a limitation of LocationInterceptionAspect.
You can use MethodInterception, which supports intercepting methods in different assemblies:
[Serializable]
public class SetterRepeaterAttribute : MethodInterceptionAspect
{
public override void OnInvoke( MethodInterceptionArgs args )
{
args.Arguments[0] = ((String)args.Arguments[0]) + ((String)args.Arguments[0]);
args.Proceed();
}
}
And multicast it on assembly level to the setter of the base class:
[assembly: ConsoleApplication2.SetterRepeater(
AttributeTargetMembers = "set_ParentProperty",
AttributeTargetTypes = "External.Parent",
AttributeTargetAssemblies = "regex:.*")]
Note that in this case the interception is done at the call site level, the ParentProperty setter is not changed itself. Calls from the original assembly won't be intercepted.
Related
I have a Part base class
class Part {
public PartType Type { get; set; }
}
with many implementations.
class Wire : Part { }
I have a TreeView in my program. And when I click on an element of it, I want a list to be filled with all the Parts of the Type I clicked in the TreeView.
When I have multiple lists open I want only those be loading the parts that have the same PartType as I clicked in the TreeView.
class BasePartListViewModel<T> : ListViewModel where T : Part {
protected override void OnTreeSelectionChanged(PartType type)
if (type == PartType.Wire) {
//load the Wires from the DB and display them
}
else {
//ignore the changed type event
}
}
}
But since this is a base class for all Parts by using T I would like to replace
if (_type == PartTypeEnum.Wire)
with something like
if (_type == T.Type)
but that does of course not work. How else?
Since the part type is a static information for a class type by design (am I right?), you could use an attribute to store it:
[AttributeUsage(AttributeTargets.Class)]
public class PartTypeAttribute : Attribute
{
public readonly PartType PartType;
public PartTypeAttribute(PartType partType)
{
PartType = partType;
}
}
Then apply it to the descendant classes:
[PartType(PartType.Wire)]
class Wire : Part
{
}
Then in the static constructor of the BasePartListViewModel class you can obtian the corresponding value:
class BasePartListViewModel<T> : ListViewModel
where T : Part
{
private static PartType PartTypeOfT;
static BasePartListViewModel()
{
var attr = typeof(T).GetCustomAttributes(typeof(PartTypeAttribute), true)
.FirstOrDefault() as PartTypeAttribute;
if (attr != null)
PartTypeOfT = attr.PartType;
}
protected override void OnTreeSelectionChanged(PartType type)
{
if (type == PartTypeOfT) {
....
}
}
}
If you do this.GetType() it will return as BasePartListViewModel`1[Wire]
You should not be ideally referring this in the base class.
I'm trying to import parts and include a custom MetadataAttribute, following the imperative model, using .NET 4.5
Below, I've included the simplest of example I can, which illustrates the problem.
When this code is executed, the Engine class constructor is called, and passed an empty Enumerator, rather than the two plugins which are clearly part of the project.
At the moment I'm suspecting the PluginMetadata attribute, but I don't see how to get Metadata into the catalog without it.
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Registration;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var builder = new RegistrationBuilder();
builder.ForTypesDerivedFrom<IPlugIn>().Export<Lazy<IPlugIn, IPlugInMetadata>>();
builder.ForType<Engine>().Export();
var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly(), builder);
var container = new CompositionContainer(catalog);
var engine = container.GetExport<Engine>();
engine.Value.Run();
}
}
internal class Engine
{
private IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> PlugIns { get; set; }
public Engine(IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> plugins)
{
PlugIns = plugins;
}
public void Run()
{
foreach (var plugIn in PlugIns)
{
Console.WriteLine("Starting {0}", plugIn.Metadata.Name);
plugIn.Value.Work();
}
}
}
interface IPlugIn
{
void Work();
}
interface IPlugInMetadata
{
string Name { get; }
}
[MetadataAttribute]
class PlugInMetadataAttribute : ExportAttribute, IPlugInMetadata
{
public PlugInMetadataAttribute(string name)
{
this.name = name;
}
private readonly string name;
public string Name { get { return name; } }
}
[PlugInMetadata("PlugIn1")]
class PlugIn1 : IPlugIn
{
public void Work()
{
Console.WriteLine("PlugIn 1 working");
}
}
[PlugInMetadata("PlugIn2")]
class PlugIn2 : IPlugIn
{
public void Work()
{
Console.WriteLine("PlugIn 2 working");
}
}
}
Metadata interfaces must not have any properties with setters. You should modify the IPlugInMetadata interface so its properties won't have any setters, otherwise the composition will fail:
interface IPlugInMetadata
{
string Name { get; }
}
Also, you should consider making your PlugInMetadataAttribute class inherit from ExportAttribute rather than Attribute. That will allow using this attribute as an export attribute and you won't have to use a RegistrationBuilder.
EDIT: I think I found your problem
When trying to use ImportMany in the constructor, you must specify so explicitly, so your constructor should look like this:
[ImportingConstructor]
public Engine([ImportMany] IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> plugins)
{
PlugIns = plugins;
}
Alternatively, you can choose to import it as a property:
[ImportMany]
private IEnumerable<Lazy<IPlugIn, IPlugInMetadata>> PlugIns { get; set; }
As a side note, when deriving from ExportAttribute, you'd like to include constructors that automatically export your part as IPlugIn:
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
class PlugInMetadataAttribute : ExportAttribute, IPlugInMetadata
{
public PlugInMetadataAttribute()
: base(typeof(IPlugIn))
{
}
public PlugInMetadataAttribute(string contractName)
: base(contractName, typeof(IPlugIn))
{
}
public string Name { get; set; }
}
Introduction
Class SessionModel is a service locator providing several services (I am going to elaborate my system architecture in the future, but for now I need to do it that way).
Code
I edited the following code part to be a Short, Self Contained, Correct (Compilable), Example (SSCCE):
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var sessionModel = new SessionModel(3);
// first case (see text down below):
var compositionContainer = new CompositionContainer();
// second case (see text down below):
//var typeCatalog = new TypeCatalog(typeof (SessionModel));
//var compositionContainer = new CompositionContainer(typeCatalog);
compositionContainer.ComposeExportedValue(sessionModel);
var someService = compositionContainer.GetExportedValue<ISomeService>();
someService.DoSomething();
}
}
public class SessionModel
{
private int AValue { get; set; }
[Export]
public ISomeService SomeService { get; private set; }
public SessionModel(int aValue)
{
AValue = aValue;
// of course, there is much more to do here in reality:
SomeService = new SomeService();
}
}
public interface ISomeService
{
void DoSomething();
}
public class SomeService : ISomeService
{
public void DoSomething()
{
Console.WriteLine("DoSomething called");
}
}
}
Problem
I would like MEF to consider the parts (i.e. SomeService) exported by the service locator when composing other parts, but unfortunately this does not work.
First Case
When I try to get the exported value for ISomeService there is a System.ComponentModel.Composition.ImportCardinalityMismatchException telling me there are no exports with this contract name and required type identity (ConsoleApplication1.ISomeService).
Second Case
If I create the CompositionContainer using the TypeCatalog the exception is slightly different. It is a System.ComponentModel.Composition.CompositionException telling me MEF doesn't find a way to create a ConsoleApplication1.SessionModel (which is right and the reason why I am doing it myself).
Additional Information
mefx says for both cases:
[Part] ConsoleApplication1.SessionModel from: DirectoryCatalog (Path=".")
[Export] ConsoleApplication1.SessionModel.SomeService (ContractName="ConsoleApplication1.ISomeService")
[Part] ConsoleApplication1.SessionModel from: AssemblyCatalog (Assembly="ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")
[Export] ConsoleApplication1.SessionModel.SomeService (ContractName="ConsoleApplication1.ISomeService")
What do I have to do? Is this possible with MEF or do I have to use Unity or StructureMap, or something else? Can this be done implementing an ExportProvider?
OK, that's how I did it:
I implemented my own SessionModelExportProvider finding exports in my SessionModel (see code below). Class SessionModelExport is just for holding the export data and – instead of creating an instance of a service – returning the value of the property of the SessionModel.
public class SessionModelExportProvider : ExportProvider
{
private List<Export> Exports { get; set; }
public SessionModelExportProvider(SessionModel sessionModel)
{
// get all the properties of the session model having an Export attribute
var typeOfSessionModel = typeof (SessionModel);
PropertyInfo[] properties = typeOfSessionModel.GetProperties();
var propertiesHavingAnExportAttribute =
from p in properties
let exportAttributes = p.GetCustomAttributes(typeof (ExportAttribute), false)
where exportAttributes.Length > 0
select new
{
PropertyInfo = p,
ExportAttributes = exportAttributes
};
// creating Export objects for each export
var exports = new List<Export>();
foreach (var propertyHavingAnExportAttribute in propertiesHavingAnExportAttribute)
{
var propertyInfo = propertyHavingAnExportAttribute.PropertyInfo;
foreach (ExportAttribute exportAttribute in propertyHavingAnExportAttribute.ExportAttributes)
{
string contractName = exportAttribute.ContractName;
if (string.IsNullOrEmpty(contractName))
{
Type contractType = exportAttribute.ContractType ?? propertyInfo.PropertyType;
contractName = contractType.FullName;
}
var metadata = new Dictionary<string, object>
{
{CompositionConstants.ExportTypeIdentityMetadataName, contractName},
{CompositionConstants.PartCreationPolicyMetadataName, CreationPolicy.Shared}
};
var exportDefinition = new ExportDefinition(contractName, metadata);
var export = new SessionModelExport(sessionModel, propertyInfo, exportDefinition);
exports.Add(export);
}
}
Exports = exports;
}
protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition,
AtomicComposition atomicComposition)
{
return Exports.Where(e => definition.IsConstraintSatisfiedBy(e.Definition));
}
}
public class SessionModelExport : Export
{
private readonly SessionModel sessionModel;
private readonly PropertyInfo propertyInfo;
private readonly ExportDefinition definition;
public SessionModelExport(SessionModel sessionModel, PropertyInfo propertyInfo, ExportDefinition definition)
{
this.sessionModel = sessionModel;
this.propertyInfo = propertyInfo;
this.definition = definition;
}
public override ExportDefinition Definition
{
get { return definition; }
}
protected override object GetExportedValueCore()
{
var value = propertyInfo.GetValue(sessionModel, null);
return value;
}
}
The problem is that the SomeService is an instance property. You could have several SessionModel objects in your system, and MEF would have no way of knowing which SessionModel is returning the ISomeService instance that is supposed to be matched to an import.
Instead, just make SessionModel a static class and SomeService a static property. Alternatively, make SessionModel a singleton. The SomeService property would still be static, but would export the service from the one-and-only instance of SessionModel.
using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.ReflectionModel;
using System.Reflection;
using System.Linq;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
var catalogs = new AggregateCatalog();
var catalog = new System.ComponentModel.Composition.Hosting.AssemblyCatalog(Assembly.GetExecutingAssembly());
catalogs.Catalogs.Add(catalog);
var sessionModel = new SessionModel(3);
var container = new CompositionContainer(catalog);
ISomeService someService = container.GetExportedValueOrDefault<ISomeService>(sessionModel.cname);
if (someService != null)
{
someService.DoSomething();
}
}
}
public class SessionModel
{
private int AValue { get; set; }
//[Import("One",typeof(ISomeService))]
//public ISomeService SomeService { get; private set; }
public SessionModel(int aValue)
{
AValue = aValue;
// of course, there is much more to do here in reality:
}
public string cname { get { return "One"; } }
}
public class SessionModel1
{
private int AValue { get; set; }
//[Import("Two",typeof(ISomeService))]
//public ISomeService SomeService { get; private set; }
public SessionModel1(int aValue)
{
AValue = aValue;
}
public string cname { get { return "Two"; } }
}
public interface ISomeService
{
void DoSomething();
}
[Export("One",typeof(ISomeService))]
public class SomeService : ISomeService
{
public SomeService()
{
Console.WriteLine("Some Service Called");
}
public void DoSomething()
{
Console.WriteLine("DoSomething called");
Console.ReadKey();
}
}
[Export("Two",typeof(ISomeService))]
public class SomeService1 : ISomeService
{
public SomeService1()
{
Console.WriteLine("Some Service1 Called");
}
public void DoSomething()
{
Console.WriteLine("DoSomething called 1");
Console.ReadKey();
}
}
}
First case: By passing sessionModel to ComposeExportedValue you add a part of type SessionModel and not of ISomeService. To make this case work you nee to pass the service to ComposeExportedValue.
compositionContainer.ComposeExportedValue(sessionModel.SomeService);
Second case: In this case you leave the creation of parts to the container. The container can create new parts if there is either a parameter-less constructor or a constructor with parameters decorated with the ImportingConstructorAttribute. This most probably means that you will need to change your design a bit.
Personally I would go with the first case, but try to keep this to a minimum. After all the normal (and suggested) usage of MEF is letting the container create and handle parts.
I am using VS 2010 RTM and trying to perform some basic validation on a simple type using MetadataTypeAttribute. When I put the validation attribute on the main class, everything works. However, when I put it on the metadata class, it seems to be ignored. I must be missing something trivial, but I've been stuck on this for a while now.
I had a look at the Enterprise Library validation block as a workaround, but it doesn't support validation of single properties out of the box. Any ideas?
class Program
{
static void Main(string[] args)
{
Stuff t = new Stuff();
try
{
Validator.ValidateProperty(t.X, new ValidationContext(t, null, null) { MemberName = "X" });
Console.WriteLine("Failed!");
}
catch (ValidationException)
{
Console.WriteLine("Succeeded!");
}
}
}
[MetadataType(typeof(StuffMetadata))]
public class Stuff
{
//[Required] //works here
public string X { get; set; }
}
public class StuffMetadata
{
[Required] //no effect here
public string X { get; set; }
}
It seems that the Validator doesn't respect MetadataTypeAttribute:
http://forums.silverlight.net/forums/p/149264/377212.aspx
The relationship must be explicity registered:
TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(
typeof(Stuff),
typeof(StuffMetadata)),
typeof(Stuff));
This helper class will register all the metadata relationships in an assembly:
public static class MetadataTypesRegister
{
static bool installed = false;
static object installedLock = new object();
public static void InstallForThisAssembly()
{
if (installed)
{
return;
}
lock (installedLock)
{
if (installed)
{
return;
}
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
foreach (MetadataTypeAttribute attrib in type.GetCustomAttributes(typeof(MetadataTypeAttribute), true))
{
TypeDescriptor.AddProviderTransparent(
new AssociatedMetadataTypeTypeDescriptionProvider(type, attrib.MetadataClassType), type);
}
}
installed = true;
}
}
}
Supplying an instance of the metadata class instead of the main class to the ValidationContext constructor seems to work for me.
The code below successfully recognizes internal classes which are decorated with my custom "Module" attribute, I load the assembly like this:
Assembly assembly = Assembly.GetExecutingAssembly();
However, when I load in an external module and look through its classes, it finds the classes in the external assembly but does not recognize the custom attributes:
Assembly assembly = Assembly.LoadFrom(#"c:\tests\modules\CustomModules.dll");
What do I have to specify so that C# recognizes custom attributes in external .dlls the same as it does with the internal classes?
Here is the code that successfully runs through and recognizes internal classes decorated with my "Module" attribute:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace DynamicAssembly2
{
class Program
{
static void Main(string[] args)
{
var modules = from t in GetModules()
select t;
foreach (var module in modules)
{
ModuleAttribute[] moduleAttributes = GetModuleAttributes(module);
Console.WriteLine(module.FullName);
foreach (var moduleAttribute in moduleAttributes)
{
Console.WriteLine(moduleAttribute.Description);
}
}
Console.ReadLine();
}
public static IEnumerable<Type> GetModules()
{
//Assembly assembly = Assembly.LoadFrom(#"c:\tests\modules\CustomModules.dll");
Assembly assembly = Assembly.GetExecutingAssembly();
return GetAssemblyClasses(assembly)
.Where((Type type) => {return IsAModule(type);});
}
public static IEnumerable<Type> GetAssemblyClasses(Assembly assembly)
{
foreach (Type type in assembly.GetTypes())
{
Console.WriteLine(type.FullName);
yield return type;
}
}
public static bool IsAModule(Type type)
{
return GetModuleAttribute(type) != null;
}
public static ModuleAttribute GetModuleAttribute(Type type)
{
ModuleAttribute[] moduleAttributes = GetModuleAttributes(type);
Console.WriteLine(moduleAttributes.Length);
if (moduleAttributes != null && moduleAttributes.Length != 0)
return moduleAttributes[0];
return null;
}
public static ModuleAttribute[] GetModuleAttributes(Type type)
{
return (ModuleAttribute[])type.GetCustomAttributes(typeof(ModuleAttribute), true);
}
}
}
Here is my custom Mdoule attribute:
using System;
namespace DynamicAssembly2
{
[AttributeUsage(AttributeTargets.Class)]
public class ModuleAttribute : Attribute
{
public string Description { get; set; }
}
}
Here is a custom module:
namespace DynamicAssembly2
{
[Module(Description="This is the main customer class.")]
class Customers
{
}
}
How many instances of the ModuleAttribute class do you have defined across your assemblies?
It looks like you have one in DynamicAssembly2 and one in CustomModules ... in which case they are different types.
Your code in DynamicAssembly2 should use the attribute class defined in CustomModules (or both assemblies should load the attribute from a 3rd assembly).
I'm pretty sure that it doesn't distinguish... are you sure you are asking for the right attribute? As long as both projects reference the same ModuleAttribute, then typeof(ModuleAttribute) should work. Otherwise you'll need to first find the Type of the attribute you want (from Assembly.GetType()), and use that when calling GetCustomAttributes.
Aloha
I've tested your code but couldn't get it to compile because the type ModuleAttribute must be known in both the Main program and the external dll. I assumed there were no references involved.
I did get it to work by introducing a reference.
Here's the class. This assembly holds a reference to DynamicAssembly
using System;
namespace DynamicAssembly2
{
[DynamicAssembly.Module(Description = "This is the main customer class.")]
public class Customers
{
}
}
Here's the code in DynamicAssembly:
using System.Reflection;
namespace DynamicAssembly
{
[AttributeUsage(AttributeTargets.Class)]
public class ModuleAttribute : Attribute
{
public string Description { get; set; }
}
class Program
{
static void Main(string[] args)
{
var modules = from t in GetModules()
select t;
foreach (var module in modules)
{
ModuleAttribute[] moduleAttributes = GetModuleAttributes(module);
Console.WriteLine(module.FullName);
foreach (var moduleAttribute in moduleAttributes)
{
Console.WriteLine(moduleAttribute.Description);
}
}
Console.ReadLine();
}
public static IEnumerable<Type> GetModules()
{
Assembly assembly = Assembly.LoadFrom(#"C:\Temp\ClassLibrary1\bin\Debug\ClassLibrary1.dll");
return GetAssemblyClasses(assembly)
.Where((Type type) => { return IsAModule(type); });
}
public static IEnumerable<Type> GetAssemblyClasses(Assembly assembly)
{
foreach (Type type in assembly.GetTypes())
{
yield return type;
}
}
public static bool IsAModule(Type type)
{
return GetModuleAttribute(type) != null;
}
public static ModuleAttribute GetModuleAttribute(Type type)
{
ModuleAttribute[] moduleAttributes = GetModuleAttributes(type);
Console.WriteLine(moduleAttributes.Length);
if (moduleAttributes != null && moduleAttributes.Length != 0)
return moduleAttributes[0];
return null;
}
public static ModuleAttribute[] GetModuleAttributes(Type type)
{
return (ModuleAttribute[])type.GetCustomAttributes(typeof(ModuleAttribute), true);
}
}
}
I had the same problem. If the attribute was defined in an internal *cs, I could get the attribute, but if it was defined in an external dll, I would just get null when I queried for it (no error), but no linker error either.
Apparently a custom Attribute defined in an DLL and the same custom Attribute defined in local code (*cs) will happily compile into one application without any warnings about multiple definitions (the name spaces are identical). I got rid of the local code (*cs), and fixed the references and it worked.