簡體   English   中英

StructureMap通過注入而不是服務位置來解析依賴性

[英]StructureMap resolve dependency through injection instead of service location

在我的項目中,我使用匯編掃描程序注冊了許多ISerializers實現。 FWIW這是注冊我的ISerializers的代碼

Scan(scanner =>
{
    scanner.AssemblyContainingType<ISerializer>();
    scanner.AddAllTypesOf<ISerializer>().NameBy(type => type.Name);
    scanner.WithDefaultConventions();
});

然后正確注冊

ISerializer (...ISerializer)
Scoped as:  Transient

JsonSerializer    Configured Instance of ...JsonSerializer
BsonSerializer    Configured Instance of ...BsonSerializer

等等。

目前,我能夠弄清楚如何解決我想要的串行器的唯一方法是使用硬編碼服務位置調用

jsonSerializer = ObjectFactory.GetNamedInstance<ISerializer>("JsonSerializer");

現在我在課堂上知道我特別想要jsonSerializer,所以有沒有辦法配置一個規則或類似的東西,讓ISerializer根據屬性名連接命名實例? 所以我可以

MySomeClass(ISerializer jsonSerializer, ....)

StructureMap正確解決了這種情況? 或者我接近這個錯誤,也許我應該只注冊實現ISerializer的具體類型,然后專門使用

MySomeClass(JsonSerializer jsonSerializer, ....)

對於具體類的這些方面的東西?

當您執行依賴注入並且需要能夠創建給定接口的特殊類型實例時,建議的解決方案是創建專用工廠類。 這允許您在不實際注入容器的情況下使用命名參數。

這是您要注入的抽象類型:

public interface ISerializerFactory
{
    ISerializer GetSerializer(string name);
}

這是具體類型,它使用您的容器(StructureMap):

public class StructureMapSerializerFactory : ISerializerFactory
{
    public ISerializer GetSerializer(string name)
    {
        return ObjectFactory.GetNamedInstance<ISerializer>(name);
    }
}

然后你的課將如下所示:

public class MyClass
{
    private readonly ISerializerFactory serializerFactory;

    public MyClass(ISerializerFactory serializerFactory)
    {
        if (serializerFactory == null)
            throw new ArgumentNullException("serializerFactory");
        this.serializerFactory = serializerFactory;
    }

    public string SerializeSomeData(MyData data)
    {
        ISerializer serializer = serializerFactory.GetSerializer("Json");
        return serializer.Serialize(data);
    }
}

我寫過這個傳遞“Json”而不是“JsonSerializer”,它不會自動運行。 但我認為您應該更改您的注冊名稱以消除冗余的“Serializer”后綴(我們已經知道它是一個序列化器,因為我們要求使用ISerializer )。 換句話說,創建一個這樣的方法:

private static string ExtractSerializerName(Type serializerType)
{
    string typeName = serializerType.Name;
    int suffixIndex = typeName.IndexOf("Serializer");
    return (suffixIndex >= 0) ?
        typeName.Substring(0, suffixIndex - 1) : typeName;
}

並注冊如下:

scanner.AddAllTypesOf<ISerializer>().NameBy(type => ExtractSerializerName(type));

然后你可以使用字符串“Json”來創建它而不是“JsonSerializer”,它看起來會有點不那么難看,感覺不那么耦合。

如果您不喜歡硬編碼的字符串,那么您可以做的另一件事是為您的工廠創建一個枚舉:

public enum SerializationFormat { Json, Bson, Xml };

public interface ISerializerFactory
{
    ISerializer GetSerializer(SerializationFormat format);
}

public class StructureMapSerializerFactory : ISerializerFactory
{
    public ISerializer GetSerializer(SerializationFormat format)
    {
        return ObjectFactory.GetNamedInstance<ISerializer>(format.ToString());
    }
}

所以不要寫這個:

ISerializer serializer = serializerFactory.GetSerializer("Json");

你可以這樣寫:

ISerializer serializer =
    serializerFactory.GetSerializer(SerializationFormat.Json);

從長遠來看,哪個更容易出錯。

從長遠來看,這可能更容易維護,因為如果你開始更改序列化程序的類名和/或名稱不一致,那么你可以用switch語句替換簡單的ToString()並實際將枚舉值映射到您正在注冊的班級名稱。

我可能會將所有這些代碼 - 包括你的問題中的自動注冊代碼 - 放在同一個命名空間,甚至是相同的代碼文件中,以清楚地表明這些代碼都是相互依賴的。

據我所知,這不是組裝掃描功能的意義所在。 當單個程序集具有許多不同接口的實現時(例如, IRepository<File>IRepository<Folder>等),它會更有用。 因此,例如,當您引用測試程序集時,您將注入測試存儲庫,而當您正在生產時,您將注入實體框架存儲庫。

在您的情況下,它看起來不像您的任何示例完全注入依賴項。 換句話說,當你寫作

ObjectFactory.GetNamedInstance<ISerializer>("JsonSerializer");

由於對字符串進行了硬編碼,你仍然依賴於Json序列化程序,而且從那個調用中返回一些其他類型的序列化程序也沒有意義。

我無法確切地告訴您使用StructureMap完成什么,但如果您需要根據某些運行時條件返回特定的序列化程序,則可以查看條件構造

另一方面,它聽起來並不像你在這里的那種轉換,所以你一定要考慮擺脫它。 畢竟,上面的代碼確實沒有什么不同

new JsonSerializer();

StructureMap是一個很棒的工具,但並不是每個項目都需要。

祝好運!

由於您的代碼假定它正在獲取JsonSerializer,因此請創建一個只有JsonSerializer實現的新IJsonSerializer接口。 任何需要JsonSerializer的類都應該接受IJsonSerializer。 如果仍需要ISerializer接口在所有序列化器中都是通用的,則IJsonSerializer可以用作標記接口。

或者,當您在StructureMap中注冊類時,可以將特定的ISerializer實現綁定到您的類。

x.For<MySomeClass>().Use(c => new MySomeClass(c.GetInstance<JsonSerializer>()));

我好奇。 ISerializer對它自己有什么價值呢? 讓我們從具體實現轉到運行時選擇的一個或多個。

如果您的類型依賴於特定類型的序列化程序,則依賴它(IJsonSerializer)。 這要求在容器中注冊該類型的默認實例。

但是,如果您更多地考慮將ISerializers作為策略,那么您將注冊所有ISerializers,然后依賴它們的數組,StructureMap將推入所有已注冊ISerializers的數組。 然后,使用這些序列化器的類負責選擇使用哪一個。

在策略場景中,您可能需要序列化程序上的一些元數據供協調類用於區分它們。 恕我直言,這應該不是容器配置,如注冊類型上的名稱,但實現本身的元數據。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM