2017-10-04 107 views
0

我正在使用反射來查找給定類型的構造函數。然後,我想緩存它的類型上的構造函數,以便在下一次需要構造函數時即時使用它。下面的代碼是這樣做的,但它需要我將構造函數存儲爲返回對象,然後將其轉換爲所需的類型。我希望有一種方法可以使它更安全。通用字典上的類型約束

private static readonly ConcurrentDictionary<Type, Func<Guid, object>> AggregateConstructors = new ConcurrentDictionary<Type, Func<Guid, object>>(); 

public TAggregate GetAggregate<TAggregate>(Guid aggregateId) where TAggregate : AggregateRoot 
{ 
    var constructor = AggregateConstructors.GetOrAdd(typeof(TAggregate), GetConstructorFunc<TAggregate>()); 
    // Requires a cast. 
    var aggregate = (TAggregate)constructor(aggregateId); 

    var history = eventStore.GetDomainEvents(aggregateId); 
    aggregate.LoadFromHistory(history); 

    return aggregate; 
} 

private Func<Guid, TAggregate> GetConstructorFunc<TAggregate>() 
{ 
    var parameter = Expression.Parameter(typeof(Guid), "aggregateId"); 
    var constructor = typeof(TAggregate).GetConstructor(new[] { typeof(Guid) }); 
    var lambda = Expression.Lambda<Func<Guid, TAggregate>>(Expression.New(constructor, parameter), parameter); 
    return lambda.Compile(); 
} 

我想有下面幾行內容:

private static readonly ConcurrentDictionary<Type, Func<Guid, SameTypeAsKey>> AggregateConstructors = new ConcurrentDictionary<Type, Func<Guid, SameTypeAsKey>>(); 

public TAggregate GetAggregate<TAggregate>(Guid aggregateId) where TAggregate : AggregateRoot 
{ 
    var constructor = AggregateConstructors.GetOrAdd(typeof(TAggregate), GetConstructorFunc<TAggregate>()); 
    var aggregate = constructor(aggregateId); 

    var history = eventStore.GetDomainEvents(aggregateId); 
    aggregate.LoadFromHistory(history); 

    return aggregate; 
} 

private Func<Guid, TAggregate> GetConstructorFunc<TAggregate>() 
{ 
    var parameter = Expression.Parameter(typeof(Guid), "aggregateId"); 
    var constructor = typeof(TAggregate).GetConstructor(new[] { typeof(Guid) }); 
    var lambda = Expression.Lambda<Func<Guid, TAggregate>>(Expression.New(constructor, parameter), parameter); 
    return lambda.Compile(); 
} 

回答

0

不,這是不可能做到這一點。第一個代碼是好的,不需要改進。

你另一個想法是使用通用類的靜態字段,而不是併發詞典:

static class AggregateConstructors<TAggregate> 
{ 
    public static Func<Guid, TAggregate> Value { get; private set; } 
    public static TAggregate Create(Guid aggregateId) => Value(aggregateId); 

    static AggregateConstructors() 
    { 
     var parameter = Expression.Parameter(typeof(Guid), "aggregateId"); 
     var constructor = typeof(TAggregate).GetConstructor(new[] { typeof(Guid) }); 
     var lambda = Expression.Lambda<Func<Guid, TAggregate>>(Expression.New(constructor, parameter), parameter); 
     Value = lambda.Compile(); 
    } 
} 

public TAggregate GetAggregate<TAggregate>(Guid aggregateId) where TAggregate : AggregateRoot 
{ 
    var aggregate = AggregateConstructors<TAggregate>.Create(aggregateId); 

    var history = eventStore.GetDomainEvents(aggregateId); 
    aggregate.LoadFromHistory(history); 

    return aggregate; 
} 

但需要注意的泛型類中靜態變量不推薦使用。

0

一個選擇是你只需要添加另一個層,它會覆蓋字典,爲你做演員。這很容易被擴展方法

public static class ExtensionMethods 
{ 
    public static Func<Guid, TAggregate> GetOrAdd<TAggregate>(this ConcurrentDictionary<Type, Func<Guid, object>> @this, Func<Guid, TAggregate> factory) 
    { 
     var constructor = @this.GetOrAdd(typeof(TAggregate), (key) => factory); 
     return (guid) => (TAggregate)constructor(guid); 
    } 
} 

這讓你做

public TAggregate GetAggregate<TAggregate>(Guid aggregateId) where TAggregate : AggregateRoot 
{ 
    var constructor = AggregateConstructors.GetOrAdd<TAggregate>(GetConstructorFunc<TAggregate>()); 
    var aggregate = constructor(aggregateId); 

    var history = eventStore.GetDomainEvents(aggregateId); 
    aggregate.LoadFromHistory(history); 

    return aggregate; 
} 

另一種選擇是,如果LoadFromHistory是在基AggregateRoot類的功能,你可以只更換objectAggregateRoot然後將強制轉換爲該方法的結束。

public TAggregate GetAggregate<TAggregate>(Guid aggregateId) where TAggregate : AggregateRoot 
{ 
    var constructor = AggregateConstructors.GetOrAdd(typeof(TAggregate), GetConstructorFunc<TAggregate>()); 
    // Requires a cast. 
    var aggregate = constructor(aggregateId); 

    var history = eventStore.GetDomainEvents(aggregateId); 
    aggregate.LoadFromHistory(history); 

    return (TAggregate)aggregate; 
}