2

我正在创建一个看起来有点像单元测试框架的规则集引擎。

[RuleSet(ContextA)]
public class RuleSet1
{
    [Rule(TargetingA)]
    public Conclusion Rule1(SubjectA subject)
    { Create conclusion }

    [Rule(TargetingA)]
    public Conclusion Rule2(SubjectA subject)
    { Create conclusion }

    [Rule(TargetingB)]
    public Conclusion Rule3(SubjectB subject)
    { Create conclusion }
}

    [RuleSet(ContextB)]
public class RuleSet2
{
    [Rule(TargetingB)]
    public Conclusion Rule1(SubjectB subject)
    { Create conclusion }

    [Rule(TargetingA)]
    public Conclusion Rule2(SubjectA subject)
    { Create conclusion }

    [Rule(TargetingB)]
    public Conclusion Rule3(SubjectB subject)
    { Create conclusion }
}

public class Conclusion()
{
    // Errorcode, Description and such
}
// contexts and targeting info are enums.

目标是创建一个可扩展的规则集,该规则集不会从消费者 POV 更改 API,同时在代码文件中具有良好的关注点分离。再次:就像一个单元测试框架。

我正在尝试创建一个公开以下 API 的库

public static class RuleEngine
{
    public static IEnumerable<IRuleSet> RuleSets(contextFlags contexts)
    {
        {
            return from type in Assembly.GetExecutingAssembly().GetTypes()
                   let attribute =
                       type.GetCustomAttributes(typeof (RuleSetAttribute), true)
                           .OfType<RuleSetAttribute>()
                           .FirstOrDefault()
                   where attribute != null
                   select  ?? I don't know how to convert the individual methods to Func's.
        }
    }
}

internal interface IRuleset
{
    IEnumerable<Func<SubjectA, Conclusion>> SubjectARules { get; }
    IEnumerable<Func<SubjectB, Conclusion>> SubjectBRules { get; }
}

...允许消费者像这样简单地使用(在本示例中使用 foreach 而不是 LINQ 以提高可读性)

foreach (var ruleset in RuleEgine.RuleSets(context))
{
    foreach (var rule in ruleset.SubjectARules)
    {
        var conclusion = rule(myContextA);
        //handle the conclusion
    }
}

此外,如果您能告诉我如何摆脱“TargetingA”和“TargetingB”作为 RuleAttribute 参数,而是使用反射直接检查修饰方法的参数类型,那将非常有帮助。同时维护相同的简单外部 API。

4

2 回答 2

1

你可以使用Delegate.CreateDelegateGetParameters方法来做你想做的事。

public class RuleSet : IRuleSet
{
    public IEnumerable<Func<SubjectA, Conclusion>> SubjectARules { get; set; }
    public IEnumerable<Func<SubjectB, Conclusion>> SubjectBRules { get; set; }
}

public static class RuleEngine
{
    public static IEnumerable<IRuleSet> RuleSets() // removed contexts parameter for brevity
    {
        var result = from t in Assembly.GetExecutingAssembly().GetTypes()
                     where t.GetCustomAttributes(typeof(RuleSetAttribute), true).Any()
                     let m = t.GetMethods().Where(m => m.GetCustomAttributes(typeof(RuleAttribute)).Any()).ToArray()
                     select new RuleSet 
                     {
                        SubjectARules = CreateFuncs<SubjectA>(m).ToList(), 
                        SubjectBRules = CreateFuncs<SubjectB>(m).ToList()
                     };
        return result;
    }
}

// no error checking for brevity
// TODO: use better variable names
public static IEnumerable<Func<T, Conclusion>> CreateFuncs<T>(MethodInfo[] m)
{
    return from x in m 
           where x.GetParameters()[0].ParameterType == typeof(T)
           select (Func<T, Conclusion>)Delegate.CreateDelegate(typeof(Func<T, Conclusion>), null, x);
}

然后你可以像这样使用它:

var sa = new SubjectA();
foreach (var ruleset in RuleEngine.RuleSets())
{
    foreach (var rule in ruleset.SubjectARules)
    {
        var conclusion = rule(sa);
        // do something with conclusion
    }
}
于 2013-08-27T10:47:38.220 回答
0

在您的 LINQ 查询中,您直接前往RuleSetAttribute,因此丢失了其他信息。如果你在几行代码中打破查询,你可以从类型中获取方法GetMethods(),然后你可以调用GetCustomAttribute<RuleAttribute>()

于 2013-08-27T10:13:34.703 回答