2016-10-10 3 views
4

Ive bekam, was ich denke, kann ein ungewöhnliches Problem sein (Ive um eine Menge für eine Antwort gesucht, aber ich glaube nicht, dass Ive eine gefunden).Factory-Klasse unter Verwendung von Generika, aber ohne Basisklasse

Ich habe Nachrichten, die aus einer Warteschlange und in Abhängigkeit von dem Nachrichtentyp gelesen werden, enthält eine Nutzlast, die in eine Beton deserialisiert C# Klasse werden muss. Dies muss schließlich konkret sein (ich kann Generika nicht den ganzen Weg verwenden), weil ich Expression Trees verwende, um Bewertungen für die Klassen durchzuführen, die aus der Warteschlange kommen.

Die Basisklasse sieht wie folgt aus:

public abstract class BaseRuleMessage<T> 
{ 
    public abstract Func<T, bool> CompileRule(Rule r, T msg); 

    public T Deserialize(ClientEventQueueMessage message) 
    { 
     return JsonConvert.DeserializeObject<T>(message.Payload); 
    }   

    public BaseRuleMessage() 
    { 
     RulesCompleted = new List<int>(); 
    } 

    public IEnumerable<Rule> FilterRules(RuleGroup ruleGroup) 
    { 
     return ruleGroup.Rules.Where(item => 
      !RulesCompleted.Any(r => r.Equals(item.Id))); 
    } 

ich die Basisklasse wie folgt implementieren:

public class UiTransactionUpdate : BaseRuleMessage<UiTransactionUpdate> 
{ 

    public override Func<UiTransactionUpdate, bool> CompileRule(Rule r, UiTransactionUpdate msg) 
    { 
     var expression = Expression.Parameter(typeof(UiTransactionUpdate)); 
     Expression expr = BuildExpr(r, expression, msg); 
     return Expression.Lambda<Func<UiTransactionUpdate, bool>>(expr, expression).Compile(); 
    } 
    public Guid TransactionId { get; set; } 

    public Guid GroupId { get; set; } 

    public decimal StatusValue { get; set; } 

ich dann so etwas tun zu nennen:

switch (message.MessageType) 
      { 
       case "UI_UPDATE": 
       { 
        message.Payload = RemoveNullGroupIdReference(jsonPayload, message.Payload); 
        var deserializedMessage = new UiTransactionUpdate().Deserialize(message); 
        deserializedMessage.RulesCompleted = deserializedMessage.RulesCompleted ?? new List<int>(); 

        foreach (var rule in deserializedMessage.FilterRules(ruleGroup)) 
        { 

Was Ich möchte wirklich wissen, wie kann ich eine Fabrik erstellen (oder kann ich?), Um die Implementierung der Basis cl zu definieren ass so, dass ich eine konkrete Klasse für meine Ausdrucksbaumauswertungen zurückgeben kann, ohne den gesamten aufrufenden Code für jeden Typ wiederholen zu müssen.

+2

Warum ist 'eine Instanzmethode und kein statisches Verfahren Deserialize'? Was enteralisierst du eigentlich? Ich hatte ein "UITransactionUpdate" angenommen. –

+0

Die erste Sache, die in den Sinn kommt, ist dynamisch mit Typ-Feld -> auf konkrete Klasse zugeordnet. Alternativ können viele Nachrichtenübergabebibliotheken die Deserialisierung für bestimmte Typen übernehmen. –

+0

Ich muss jeden Typ deeserialize, den der Basistyp implementiert (ich weiß nicht welcher Typ, bis die Nachricht ankommt) und UiTransactionUpdate ist nur eine von vielen verschiedenen Implementierungen. Es gibt mehr Methoden, die typspezifisch sind und deserialize könnte ein schlechtes Beispiel sein. Aber es ist der Prozess der Instanziierung eines Typs basierend auf einer Zeichenfolge, die ich lösen möchte. – KerSplosh

Antwort

2

Ich vermied es, dynamic zu verwenden, aber das bedeutete, dass ich das Objekt als object übergeben hatte. Ich ziehe es nichtdynamic zu verwenden, aber in diesem Fall kann Objekte zur Laufzeit Gießen nicht besser sein.

Ich musste auch den Code ändern, so dass anstelle einer Func<T, bool> gibt es eine Methode, die Func ausführen würde. Damit sollte vermieden werden, auf die generische Klasse zu verweisen. Ich bin mir nicht sicher, ob Sie tatsächlich die Func in Ihrer tatsächlichen Implementierung benötigen.

Ich hatte eine neue Basisklasse zu erstellen, die nicht allgemein eingegeben wurde.

// Horrible name, do change it to something more appropriate 
public abstract class BaseBaseRuleMessage 
{ 
    public IList<int> RulesCompleted { get; set; } 

    public IEnumerable<Rule> FilterRules(RuleGroup ruleGroup) 
    { 
     return ruleGroup.Rules.Where(item => 
       !RulesCompleted.Any(r => r.Equals(item.Id))); 
    } 

    public BaseBaseRuleMessage DeserializeToBaseBaseRuleMessage(ClientEventQueueMessage message) 
    { 
     return (BaseBaseRuleMessage) DeserializeToType(message); 
    } 

    protected abstract object DeserializeToType(ClientEventQueueMessage message); 

    public abstract bool ExecuteRule(Rule rule, object msg); 
} 

die BaseRuleMessage Aktualisiert von BaseBaseRuleMessage abzuleiten (und bewegt einige Eigenschaften der Basisklasse.

public abstract class BaseRuleMessage<T> : BaseBaseRuleMessage 
    where T : BaseRuleMessage<T> 
{ 
    public abstract Func<T, bool> CompileRule(Rule r, T msg); 

    protected override object DeserializeToType(ClientEventQueueMessage message) 
    { 
     return JsonConvert.DeserializeObject(message.Payload, typeof(T)); 
    } 

    protected BaseRuleMessage() 
    { 
     RulesCompleted = new List<int>(); 
    } 

    public override bool ExecuteRule(Rule rule, object msg) 
    { 
     var message = (T) msg; 
     if (message == null) 
     { 
      throw new InvalidOperationException(); 
     } 
     return CompileRule(rule, message).Invoke(message); 
    } 
} 

Die konkrete Klasse im Grunde das gleiche. Ich meine eigene BuildExpr implementiert haben, um sicherzustellen, dass die Code kann kompilieren

public class UiTransactionUpdate : BaseRuleMessage<UiTransactionUpdate> 
{ 
    public override Func<UiTransactionUpdate, bool> CompileRule(Rule r, UiTransactionUpdate msg) 
    { 
     var expression = Expression.Parameter(typeof(UiTransactionUpdate)); 
     Expression expr = BuildExpr(r, expression, msg); 
     return Expression.Lambda<Func<UiTransactionUpdate, bool>>(expr, expression).Compile(); 
    } 

    public Guid TransactionId { get; set; } 

    public Guid GroupId { get; set; } 

    public decimal StatusValue { get; set; } 

    private Expression BuildExpr(Rule rule, ParameterExpression parameterExpression, UiTransactionUpdate message) 
    { 
     var transactionIdProperty = Expression.Property(parameterExpression, "TransactionId"); 
     var value = Expression.Constant(rule.TransactionId); 

     return Expression.Equal(transactionIdProperty, value); 
    } 
} 

es zu benutzen:.

var messageTypeToTypeMap = new Dictionary<string, Func<BaseBaseRuleMessage>> 
{ 
    {"UI_UPDATE",() => new UiTransactionUpdate()} 
}; 

var factoryFunc = messageTypeToTypeMap[message.MessageType]; 
message.Payload = RemoveNullGroupIdReference(jsonPayload, message.Payload); 
var ruleMessage = factoryFunc.Invoke(); 
var deserializedMessage = ruleMessage.DeserializeToBaseBaseRuleMessage(message); 
deserializedMessage.RulesCompleted = deserializedMessage.RulesCompleted ?? new List<int>(); 

foreach (var rule in deserializedMessage.FilterRules(ruleGroup)) 
{ 
    var isTrue = deserializedMessage.ExecuteRule(rule, deserializedMessage); 
} 
+0

groß Danke, dass du dir die Zeit genommen hast, eine funktionierende Antwort zu geben. Ich habe ein wenig von dem eigentlichen Code aus meiner Frage weggelassen, also musste ich ein wenig Refactoring machen, um deine Vorschläge einzubeziehen, aber nach der Implementierung funktionierten alle wie erwartet .. Auch - ich wählte 'SimpleRuleMessageBase' anstelle von BaseBase :) – KerSplosh

Verwandte Themen