2014-07-05 25 views
5

Il codice seguente non funziona quando viene utilizzato RegistrationBuilder. Quando lo RegistrationBuilder non viene aggiunto al costruttore AssemblyCatalog, digitare lavoro generico vincolato.I generici aperti vincolati di tipo non funzionano con RegistrationBuilder

[TestClass] 
public class TypeConstraints 
{ 
    [TestMethod] 
    public void TypeConstraintTest() 
    { 
     var rb = new RegistrationBuilder(); 
     var a = new AssemblyCatalog(Assembly.GetExecutingAssembly(), rb); 
     //var a = new AssemblyCatalog(Assembly.GetExecutingAssembly()); //Works! 
     var aggr = new AggregateCatalog(a); 
     var c = new CompositionContainer(aggr); 
     var item = c.GetExportedValue<IConstrained<Item>>(); 
     Assert.IsNotNull(item); 
    } 
} 

public interface IConstrained<T> where T : IItem 
{} 

[Export(typeof (IConstrained<>))] 
public class Constrained<T> : IConstrained<T> where T : IItem 
{} 

public class Item : IItem 
{} 

public interface IItem 
{} 
+0

prega di elaborare, var rb = new RegistrationBuilder(); var a = nuovo AssemblyCatalog (Assembly.GetExecutingAssembly(), rb); rb è inutilizzato, semplicemente non fare nulla – InferOn

+0

Non fa ancora nulla cambia comportamento, cioè rompe i generici aperti. Il piano è di usarlo una volta il caso base, basta passarlo, funziona. –

risposta

3

Prima di tutto cerchiamo di descrivere ciò che provoca esattamente questo comportamento.

RegistrationBuilder racchiude i tipi effettivi dell'assieme in un tipo di proxy denominato CustomType. Questo proxy esiste più o meno solo per dare al RegistrationBuilder l'opportunità di iniettare gli attributi di esportazione e importazione al volo.

Purtroppo questo proxy restituisce anche i tipi avvolti quando si chiama GetGenericParameterConstraints. Quindi non è un RuntimType IItem si ottiene un CustomType IItem. Quando si tenta di ottenere un'esportazione per IConstrained, AssemblyCatalog controlla un sacco di cose per sapere se l'esportazione coincide con l'importazione. Uno di questi controlli è se il vincolo di tipo generico è soddisfatto. È più o meno un assegno come questo. (Semplificato)

exportToCheck.GenericTypeConstraints[0].IsAssignableFrom(typeof(Item)) 

Il metodo IsAssignableForm del CustomType è implementato come questo.

public override bool IsAssignableFrom(Type c) 
{ 
    ProjectingType projectingType = c as ProjectingType; 
    return !(projectingType == null) && this.Projector == projectingType.Projector && 
       base.UnderlyingType.IsAssignableFrom(projectingType.UnderlyingType); 
} 

Funziona solo se si passa un altro tipo di proxy.

Penso davvero che sia un grosso problema di RegistrationBuilder e dovresti segnalarlo a Microsoft Connect.

Per aggirare questo problema, è necessario annullare la creazione di GenericTypeContraints salvati con ComposablePartDefinition.

La cattiva notizia è che tutte le classi rilevanti sono interne, quindi non è possibile sovrascrivere il metodo GetGenericParameterConstraints.

Ho risolto questo problema ereditando AssemblyCatalog e non proiettando manualmente i tipi di vincoli.

public class MyAssemblyCatalog: AssemblyCatalog { privato Func unprojectDelegate;

private bool projectionsChecked = false; 

public MyAssemblyCatalog(Assembly assembly, CustomReflectionContext reflectionContext) 
    : base(assembly, reflectionContext) 
{ 
    this.ReflectionContext = reflectionContext; 
} 

public CustomReflectionContext ReflectionContext { get; private set; } 

public Type Unproject(Type type) 
{ 
    if (this.unprojectDelegate == null) { 
     var param = Expression.Parameter(typeof(CustomReflectionContext)); 
     var param2 = Expression.Parameter(typeof(Type)); 
     var prop = Expression.Property(param, param.Type.GetProperty("Projector", BindingFlags.Instance | BindingFlags.NonPublic)); 
     var method = prop.Type.GetMethod("Unproject", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(Type) }, null); 
     var body = Expression.Call(prop, method, param2); 
     this.unprojectDelegate = Expression.Lambda<Func<CustomReflectionContext, Type, Type>>(body, param, param2).Compile(); 
    } 
    return unprojectDelegate(this.ReflectionContext, type); 
} 

private void EnsureUnprojectedGenericTypeConstraints() 
{ 
    if (!this.projectionsChecked) { 
     foreach (var item in this) { 
      object value1; 
      if (item.Metadata.TryGetValue("System.ComponentModel.Composition.GenericParameterConstraints", out value1)) { 
       var items = (object[])value1; 
       foreach (var entry in items) { 
        var types = entry as Type[]; 
        if (types != null) { 
         for (int i = 0; i < types.Length; i++) { 
          types[i] = Unproject(types[i]); 
         } 
        } 
       } 
      } 
     } 
     projectionsChecked = true; 
    } 
} 

public override System.Collections.Generic.IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExports(ImportDefinition definition) 
{ 
    EnsureUnprojectedGenericTypeConstraints(); 
    return base.GetExports(definition); 
} 

}

Ora il test funziona. soluzione

[TestMethod] 
public void TypeConstraintTest() 
{ 
    var rb = new RegistrationBuilder(); 

    var a = new MyAssemblyCatalog(Assembly.GetExecutingAssembly(), rb); 

    var aggr = new AggregateCatalog(a); 
    var c = new CompositionContainer(aggr); 
    var item = c.GetExportedValue<IConstrained<Item>>(); 

    Assert.IsNotNull(item); 
} 
+0

Grazie per la tua approfondita ricerca. Per curiosità come hai visto il codice sorgente MEF? Decompilato via ILSpy o hai trovato il vero codice sorgente per questo? –

+1

Ho usato ILSpy ... – codeworx

+1

Ho aperto un bug con M $ https://connect.microsoft.com/VisualStudio/feedback/details/916668/type-constrained-open-generics-do-not-work-when-using -assemblycatalog-with-registrationbuilder # –

0

più semplice:

/// <summary> 
/// When RegistrationBuilder is used, there is problem with Generics constraints - in produced ExportDefinition is generics constraint with descriptior CustomType which is not comparable with Type. 
/// * so composition failed on Export not found exception. 
/// http://stackoverflow.com/questions/24590096/type-constrained-open-generics-do-not-work-with-registrationbuilder 
/// </summary> 
public static class PatchCatalogForRegistrationBuilderBug 
{ 
    public static void FixCatalogForRegistrationBuilderBug(this ComposablePartCatalog catalog) 
    { 
     foreach (var item in catalog) 
     { 
      object value1; 
      if (item.Metadata.TryGetValue("System.ComponentModel.Composition.GenericParameterConstraints", out value1)) 
      { 
       var items = (object[])value1; 
       foreach (var entry in items) 
       { 
        var types = entry as Type[]; 
        if (types != null) 
        { 
         for (int i = 0; i < types.Length; i++) 
         { 
          if (((object)types[i]).GetType().FullName != "System.Reflection.Context.Custom.CustomType") continue; //cast to object is only for due to R# warning 
          types[i] = types[i].UnderlyingSystemType; 
         } 
        } 
       } 
      } 
     } 
    } 
}