2016-01-04 15 views
5

Ich versuche zu spielen (was ich denke) eine Factory, die ein Repository abhängig von der Enumeration erstellt, die an die Methode übergeben wird. Sieht wie folgt aus:Activator.CreateInstance mit einem generischen Repository

RepositoryFactory

public class RepositoryFactory 
{ 
    public IRepository<IEntity> GetRepository(FormTypes formType) 
    { 
     // Represents the IRepository that should be created, based on the form type passed 
     var typeToCreate = formType.GetAttribute<EnumTypeAttribute>().Type; 

     // return an instance of the form type repository 
     IRepository<IEntity> type = Activator.CreateInstance(typeToCreate) as IRepository<IEntity>; 

     if (type != null) 
      return type; 

     throw new ArgumentException(string.Format("No repository found for {0}", nameof(formType))); 
    } 
} 

IRepository

public interface IRepository <T> 
    where T : class, IEntity 
{ 
    bool Create(IEnumerable<T> entities); 

    IEnumerable<T> Read(); 

    bool Update(IEnumerable<T> entities); 

    bool Delete(IEnumerable<T> entities); 
} 

FormTypes

public enum FormTypes 
{ 
    [EnumType(typeof(Form64_9C2Repository))] 
    Form64_9C2, 

    [EnumType(typeof(Form64_9BaseRepository))] 
    Form64_9Base 
} 

EnumExtensions

public static class EnumExtensions 
{ 

    /// <summary> 
    /// Get the Enum attribute 
    /// </summary> 
    /// <typeparam name="T">The attribute</typeparam> 
    /// <param name="enumValue">The enum</param> 
    /// <returns>The type to create</returns> 
    public static T GetAttribute<T>(this System.Enum enumValue) 
     where T : Attribute 
    { 
     FieldInfo field = enumValue.GetType().GetField(enumValue.ToString()); 
     object[] attribs = field.GetCustomAttributes(typeof(T), false); 
     T result = default(T); 

     if (attribs.Length > 0) 
     { 
      result = attribs[0] as T; 
     } 

     return result; 
    } 

} 

Form64_9C2Repository

public class Form64_9C2Repository : IRepository<Form64_9C2> 
{ 
    public bool Create(IEnumerable<Form64_9C2> entities) 
    { 
     throw new NotImplementedException(); 
    } 

    public bool Delete(IEnumerable<Form64_9C2> entities) 
    { 
     throw new NotImplementedException(); 
    } 

    public IEnumerable<Form64_9C2> Read() 
    { 
     throw new NotImplementedException(); 
    } 

    public bool Update(IEnumerable<Form64_9C2> entities) 
    { 
     throw new NotImplementedException(); 
    } 
} 

IEntity

public interface IEntity { } 

Form64_9C2 (Stub)

public class Form64_9C2 : IEntity { } 

Aufruf alles wie:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine("Repository Factory Example \n\n"); 

     Business.Factory.RepositoryFactory factory = new Business.Factory.RepositoryFactory(); 

     // Get a 64 9C2 repository 
     var repo9c2 = factory.GetRepository(FormTypes.Form64_9C2); 
     Console.WriteLine(repo9c2); 
    } 
} 

Mein Problem ist mein type löst immer zu null. Ich erwarte, dass ich eine NotImplementedException bekomme, bekomme aber stattdessen die ArgumentException, weil ich keinen gültigen formType habe.

enter image description here

Vor der Implementierung IRepository<T> meine type/repository erfolgreich erstellt wurde (Arbeits Code here), irgendwelche Ideen? Ich habe gerade erst angefangen, mit Fabriken, Generika und ähnlichem herumzuspielen - also wenn ich etwas falsch mache, bitte rate!

Antwort

5

Ihr Code funktioniert nicht für den genau gleichen Grund, aus dem diese Linie nicht kompiliert:

IRepository<IEntity> repo = new Form64_9C2Repository(); 

Grundsätzlich IRepository<IEntity> ist nicht die gleiche wie IRepository<Form64_9C2> auch wenn Form64_9C2IEntity implementiert.

Dies gearbeitet haben könnte, wenn der T generische Parameter auf der IRepository Schnittstelle covariant war:

public interface IRepository<out T> where T : class, IEntity 
{ 
    IEnumerable<T> Read();  
} 

Aber leider würde dies bedeuten, dass es nur als Rückgabetyp für die Methoden erscheinen, nicht als Parameter. Welches ist ein No-Go für Ihre Update, Delete und Create Methoden. Man könnte natürlich auch eine Struktur wie definieren:

public interface IReadonlyRepository<out T> where T : class, IEntity 
{ 
    IEnumerable<T> Read();  
} 

public interface IRepository<T>: IReadonlyRepository<T> where T : class, IEntity 
{ 
    bool Update(IEnumerable<T> entities); 
    bool Delete(IEnumerable<T> entities); 
    bool Create(IEnumerable<T> entities); 
} 

und haben Ihre GetRepository Methode eine IReadonlyRepository<IEntity> zurückzukehren.

Wenn dies nicht für Sie arbeiten werden Sie einen zusätzlichen Parameter benötigen die konkrete Einheit Typ angeben, damit Sie die richtige Besetzung durchführen:

public IRepository<TEntity> GetRepository<TEntity>(FormTypes formType) where TEntity: class, IEntity 
    { 
     // Represents the IRepository that should be created, based on the form type passed 
     var typeToCreate = formType.GetAttribute<EnumTypeAttribute>().Type; 

     // return an instance of the form type repository 
     IRepository<TEntity> type = Activator.CreateInstance(typeToCreate) as IRepository<TEntity>; 

     if (type != null) 
      return type; 

     throw new ArgumentException(string.Format("No repository found for {0}", nameof(formType))); 
    } 
} 

und wenn zusätzlich zur Angabe des Repository-Typ ruft Sie müssen die Unternehmen Typ angeben.

var repo9c2 = factory.GetRepository<Form64_9C2>(FormTypes.Form64_9C2); 
+0

wow, nur machen 'out T' viel verändert - obwohl ich nicht wirklich verstehen, warum :(Leider wollte ich in der Lage sein, ein Repository für alle CRUD zu lösen nicht nur R. Ich sehe, dass du einen Weg darstellst, dies zu tun, aber indem du eine Ergänzung hinzufügst naler Parameter. Ich hatte wirklich gehofft, mein Repository und die damit verbundene Entität "zusammen" zu halten, um zu vermeiden, dass ein bestimmtes Repository mit einer in Konflikt stehenden Entität aufgerufen wird. Danke +1, ich akzeptiere in Kürze, dass ich nicht etwas ähnliches finde, was ich wirklich erreichen wollte. – Kritner