SORU
27 HAZİRAN 2011, PAZARTESİ


Nasıl kural motoru uygulamak için?

Aşağıdaki saklayan db bir tablo var:

RuleID  objectProperty ComparisonOperator  TargetValue
1       age            'greater_than'             15
2       username       'equal'             'some_name'
3       tags           'hasAtLeastOne'     'some_tag some_tag2'

Şimdi bu kurallar bütünü var ki:

List<Rule> rules = db.GetRules();

Şimdi bir kullanıcı bir örneği de var:

User user = db.GetUser(....);

Nasıl bu döngü kuralları ve mantığı uygulamak ve karşılaştırmalar vb gerçekleştirmek istiyorsunuz?

if(user.age > 15)

if(user.username == "some_name")

Nesne özelliği gibi beri 'yaş' ya da 'karşılaştırma yöneticisi ile birlikte tabloda saklanan,' 've' 'nasıl mümkün olabilir? eşit great_than home

C# statik olarak dil, bu kadar ileri gitmek nasıl emin değil yazdığınız.

CEVAP
3 Temmuz 2011, Pazar


Bu parçacıkKuralları hızlı çalıştırılabilir kod içine derler(Expression trees kullanarak) ve geçiş açıklamaları karışık: herhangi bir ihtiyacı yok

(Edit : full working example with generic method)

public Func<User, bool> CompileRule(Rule r)
{
    var paramUser = Expression.Parameter(typeof(User));
    Expression expr = BuildExpr(r, paramUser);
    // build a lambda function User->bool and compile it
    return Expression.Lambda<Func<User, bool>>(expr, paramUser).Compile();
}

Sonra yazabilirsiniz:

List<Rule> rules = new List<Rule> {
    new Rule ("Age", "GreaterThan", "20"),
    new Rule ( "Name", "Equal", "John"),
    new Rule ( "Tags", "Contains", "C#" )
};

// compile the rules once
var compiledRules = rules.Select(r => CompileRule(r)).ToList();

public bool MatchesAllRules(User user)
{
    return compiledRules.All(rule => rule(user));
}

Burada BuildExpr uygulanması:

Expression BuildExpr(Rule r, ParameterExpression param)
{
    var left = MemberExpression.Property(param, r.MemberName);
    var tProp = typeof(User).GetProperty(r.MemberName).PropertyType;
    ExpressionType tBinary;
    // is the operator a known .NET operator?
    if (ExpressionType.TryParse(r.Operator, out tBinary)) {
        var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tProp));
        // use a binary operation, e.g. 'Equal' -> 'u.Age == 15'
        return Expression.MakeBinary(tBinary, left, right);
    } else {
        var method = tProp.GetMethod(r.Operator);
        var tParam = method.GetParameters()[0].ParameterType;
        var right = Expression.Constant(Convert.ChangeType(r.TargetValue, tParam));
        // use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
        return Expression.Call(left, method, right);
    }
}

'Büyük' 'greater_than' vb. yerine kullanılan unutmayın - bu 'Büyük'.Operatör için NET bir isim, bu nedenle herhangi bir ekstra eşleme ihtiyacımız yok.

Eğer gerçekten özel bir isim, çok basit bir sözlük oluşturmak ve sadece kuralları derleme önce tüm operatörler çevirebilirsiniz:

var nameMap = new Dictionary<string, string> {
    { "greater_than", "GreaterThan" },
    { "hasAtLeastOne", "Contains" }
};

Kod basitlik için tür Kullanıcı kullandığına dikkat edin. Nesnelerin herhangi bir tür için generic Rule compiler bir genel tür T ile Kullanıcı değiştirebilirsiniz.

Ayrıca not: sinek üreten kod İfade ağaçları API tanıtıldı önce bile mümkün, Yansıma kullanıyordu.Yayarlar. Yöntem LambdaExpression.() Derleme Yansıma kullanır.Yorganın altına yayar (bu ILSpy kullanarak görebilirsiniz).

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • magnum33563

    magnum33563

    8 NİSAN 2011
  • The Onion

    The Onion

    14 Mart 2006
  • VvCompHelpvV

    VvCompHelpvV

    4 EYLÜL 2007