问题
I am using Unity and have a Model tagged with data annotations:
public class SomeModel
{
[SlackDisplayName("ED0CAD76-263E-496F-ABB1-A4DFE6DEC5C2")]
public String SomeProperty { get; set; }
}
This SlackDisplayName property is a child class of DisplayName, which resolves a static Display Name for the property. I just wanted to make that dynamically by having this criteria fulfilled:
- The use of this annotation is possible.
- I can implement multilingual applications using that annotation.
- Language templates get identified by a GUID
- I must not pass a culture id to the annotation
So furthermore, my SlackDisplayName annotation looks like this:
/// <summary>
/// Annotation for non-fixed display names
/// </summary>
public class SlackDisplayNameAttribute : DisplayNameAttribute
{
/// <summary>
/// TODO
/// </summary>
/// <param name="identifierGUID"></param>
public SlackDisplayNameAttribute(String identifierGUID)
: this(Guid.Parse(identifierGUID))
{
}
/// <summary>
/// TODO
/// </summary>
/// <param name="identifier"></param>
public SlackDisplayNameAttribute(Guid identifier)
: base()
{
}
/// <summary>
/// The culture context to use.
/// </summary>
[Dependency]
public ICultureContext Context { get; set; }
/// <summary>
/// Gets the display name for the given GUID.
/// </summary>
public override string DisplayName
{
get
{
return "NOT_DEFINED";
//return Context.GetLanguageTemplate(new Guid());
}
}
}
And now the question is: How to get the ICultureContext from my Unity Container:
[Dependency]
public ICultureContext Context { get; set; }
It is registered, but I have no clue about how to get that property injected.
回答1:
I have solved it myself!
First of all, you need the following Unity Extension and Strategy:
Info: Found here: UnityContainer.BuildUp() - Can I make it inject new instances into properties only if these are null?
public class RecursiveBuildUpContainerExtension : UnityContainerExtension {
protected override void Initialize(){
Context.Strategies.Add( new RecursiveBuildUpBuilderStrategy( Context.Container ), UnityBuildStage.PreCreation );
}
}
public class RecursiveBuildUpBuilderStrategy : BuilderStrategy {
readonly IUnityContainer container;
public RecursiveBuildUpBuilderStrategy( IUnityContainer container ) {
this.container = container;
}
public override void PreBuildUp( IBuilderContext context ) {
if( context.Existing == null ) return;
foreach( var prop in context.Existing.GetType( ).GetProperties( ) ) {
if( ContainsType<DependencyAttribute>( prop.GetCustomAttributes( true ) ) ) {
if( prop.GetValue( context.Existing, null ) == null ) {
var value = container.Resolve( prop.PropertyType );
prop.GetSetMethod( ).Invoke( context.Existing, new[] { value } );
}
else {
var value = container.BuildUp( prop.PropertyType, prop.GetValue( context.Existing, null ) );
prop.GetSetMethod( ).Invoke( context.Existing, new[] { value } );
}
}
}
foreach (var method in context.Existing.GetType().GetMethods() ){
if( ContainsType<InjectionMethodAttribute>( method.GetCustomAttributes( true ))){
var argsInfo = method.GetParameters( );
var args = new object[argsInfo.Length];
for( int i = 0; i < argsInfo.Length; i++ ) {
args[i] = container.Resolve( argsInfo[i].ParameterType );
}
method.Invoke( context.Existing, args );
}
}
context.BuildComplete = true;
}
private static bool ContainsType<T>( IEnumerable<object> objects ){
foreach (var o in objects){
if( o is T ) return true;
}
return false;
}
}
You need this, because it is responsible for injecting the properties on "BuildUp". Next to this, you need to register your extension
container.AddNewExtension<RecursiveBuildUpContainerExtension>();
Furthermore, you need to override the default DataAnnotationsModelMetadataProvider, because the default ModelMetaDataProvider does not use Unity to inject properties to annotations. To do this, implement this class:
public class DynamicModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
private IUnityContainer _context;
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
foreach (Attribute attribute in attributes)
_context.BuildUp(attribute);
return base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
}
public DynamicModelMetadataProvider(IUnityContainer context)
: base()
{
this._context = context;
}
}
After that, edit your bootstrapper and set the new ModelMetadataProvider, to make it clear to the MVC Framework that it has to use it:
ModelMetadataProviders.Current = new DynamicModelMetadataProvider(container);
Where container is your set up IUnityContainer. You should now have instances in your Annotations Instance when having set the [DependencyAttribute] and your Methods marked with the [InjectionMethod] should get called.
[Dependency]
public ICultureContext Context { get; set; }
Hope you could use this if you had a similar problem ;)
来源:https://stackoverflow.com/questions/17728663/using-unitys-dependency-injection-in-data-annotations