मैं नियमों को परिभाषित करने के लिए NRules का उपयोग कर रहा हूं जो सभी एक सामान्य आधार वर्ग से प्राप्त होते हैं, जो स्वयं Rule
से प्राप्त होते हैं।
जब मैं एक नए तथ्य को सम्मिलित करने के लिए एक डीएसएल एक्सटेंशन का उपयोग करता हूं जो एक मेल खाने वाली वस्तु को लपेटता है, ऐसा लगता है कि मिलान की गई वस्तु को विस्तार विधि में पारित किया गया है null
।
यहां एक आत्मनिर्भर उदाहरण दिया गया है जो समस्या का प्रदर्शन करना चाहिए। मैं दो नियमों को परिभाषित करने के लिए xUnit
परीक्षण ढांचे का उपयोग कर रहा हूं, प्रत्येक समान परीक्षण के साथ। पहला पास हो जाता है, दूसरा फेल हो जाता है।
using NRules;
using NRules.Fluent;
using NRules.Fluent.Dsl;
using Xunit;
using System.Linq;
using System.Reflection;
namespace IntegrationTests.Engine
{
// A simple domain model
public interface IFruit { }
public class Apple : IFruit { }
public class Basket
{
public Basket(IFruit apple)
{
MyApple = apple;
}
public IFruit MyApple { get; private set; }
}
// A base class for the rules
public abstract class RuleBase : Rule
{
public override void Define()
{
// Empty
}
}
// The first rule, which does not use the extension:
public class TestRule : RuleBase
{
public override void Define()
{
base.Define();
Apple a = null;
When()
.Match(() => a);
Then()
.Do(ctx => ctx.Insert(new Basket(a)));
}
}
// The second rule, which uses an extension to add a new fact
public class TestRuleWithExtension : RuleBase
{
public override void Define()
{
base.Define();
Apple apple = null;
When()
.Match(() => apple);
Then()
.AddToBasket(apple);
}
}
// The DSL extension
public static class DslExtensions
{
public static IRightHandSideExpression AddToBasket(this IRightHandSideExpression rhs, IFruit fruit)
{
return rhs.Do(ctx => ctx.Insert(new Basket(fruit)));
}
}
// The tests
public class ExtensionTest
{
// This one tests the first rule and passes
[Fact]
public void TestInsert()
{
//Load rules
var repository = new RuleRepository();
repository.Load(x => x
.From(Assembly.GetExecutingAssembly())
.Where(rule => rule.Name.EndsWith("TestRule")));
//Compile rules
var factory = repository.Compile();
//Create a working session
var session = factory.CreateSession();
//Load domain model
var apple = new Apple();
//Insert facts into rules engine's memory
session.Insert(apple);
//Start match/resolve/act cycle
session.Fire();
// Query for inserted facts
var bananas = session.Query<Basket>().FirstOrDefault();
// Assert that the rule has been applied
Assert.Equal(apple, bananas.MyApple);
}
// This one tests the second rule, and fails
[Fact]
public void TestInsertWithExtension()
{
//Load rules
var repository = new RuleRepository();
repository.Load(x => x
.From(Assembly.GetExecutingAssembly())
.Where(rule => rule.Name.EndsWith("TestRuleWithExtension")));
//Compile rules
var factory = repository.Compile();
//Create a working session
var session = factory.CreateSession();
//Load domain model
var apple = new Apple();
//Insert facts into rules engine's memory
session.Insert(apple);
//Start match/resolve/act cycle
session.Fire();
// Query for inserted facts
var bananas = session.Query<Basket>().FirstOrDefault();
// Assert that the rule has been applied
Assert.Equal(apple, bananas.MyApple);
}
}
}
सवाल यह है कि डीएसएल एक्सटेंशन वाला दूसरा नियम ठीक से काम क्यों नहीं करता है? क्या मैं कुछ गलत कर रहा हूँ और मैं इसे कैसे ठीक कर सकता हूँ?
1 उत्तर
NRules DSL के साथ ध्यान देने वाली पहली बात यह है कि जब आप किसी नियम में एक मिलान चर घोषित करते हैं और उससे जुड़ते हैं तो क्या होता है:
Apple apple = null;
When()
.Match(() => apple);
इस चर को वास्तव में कभी भी कोई मान निर्दिष्ट नहीं किया गया है। इसे एक एक्सप्रेशन ट्री के रूप में कैप्चर किया जाता है, और इसका नाम निकाला जाता है, और बाद में उसी वेरिएबल को संदर्भित करने वाले अन्य एक्सप्रेशन को खोजने के लिए उपयोग किया जाता है। इंजन तब उन संदर्भों को वास्तविक मिलान वाले तथ्य से बदल देता है। उदाहरण के लिए:
Then()
.Do(ctx => ctx.Insert(new Basket(apple)));
यहां "सेब" वही सेब चर है, जब क्लॉज होता है, इसलिए NRules इसे पहचानता है और अभिव्यक्तियों को एक साथ सही ढंग से सिलाई करता है।
जब आपने एक विस्तार विधि निकाली, तो आपने चर का नाम "फल" रखा:
public static IRightHandSideExpression AddToBasket(this IRightHandSideExpression rhs, IFruit fruit)
{
return rhs.Do(ctx => ctx.Insert(new Basket(fruit)));
}
इंजन अब इसे उसी तथ्य संदर्भ के रूप में नहीं पहचानता है, क्योंकि "फल" और "सेब" मेल नहीं खाते।
तो, फिक्स # 1 केवल वैरिएबल को उसी तरह नाम देना है जैसे घोषणा:
public static class DslExtensions
{
public static IRightHandSideExpression AddToBasket(this IRightHandSideExpression rhs, IFruit apple)
{
return rhs.Do(ctx => ctx.Insert(new Basket(apple)));
}
}
जाहिर है यह आदर्श नहीं है, क्योंकि आप चर के मिलान नामकरण पर भरोसा कर रहे हैं। चूंकि NRules एक्सप्रेशन ट्री के संदर्भ में काम करता है, इसलिए जेनेरिक एक्सटेंशन मेथड बनाने का एक बेहतर तरीका यह होगा कि इसे एक्सप्रेशन ट्री के संदर्भ में भी लिखा जाए, और अब वेरिएबल नेमिंग पर निर्भर नहीं है।
तो, फिक्स # 2 लैम्ब्डा एक्सप्रेशन का उपयोग करके एक्सटेंशन विधि लिखना है।
public class TestRuleWithExtension : RuleBase
{
public override void Define()
{
base.Define();
Apple apple = null;
When()
.Match(() => apple);
Then()
.AddToBasket(() => apple);
}
}
public static class DslExtensions
{
public static IRightHandSideExpression AddToBasket(this IRightHandSideExpression rhs, Expression<Func<IFruit>> alias)
{
var context = Expression.Parameter(typeof(IContext), "ctx");
var ctor = typeof(Basket).GetConstructor(new[] {typeof(IFruit)});
var newBasket = Expression.New(ctor, alias.Body);
var action = Expression.Lambda<Action<IContext>>(
Expression.Call(context, nameof(IContext.Insert), null, newBasket),
context);
return rhs.Do(action);
}
}
ध्यान दें कि AddToBasket(() => apple)
अब लैम्ब्डा एक्सप्रेशन को कैप्चर करता है, जिसे बाद में एक्सट्रेक्ट किया जाता है और एक्सटेंशन विधि के कार्यान्वयन में उपयोग किया जाता है। कुछ अभिव्यक्ति जादू के साथ मैंने आपके पास एक लैम्ब्डा अभिव्यक्ति के बराबर बनाया, लेकिन इस बार किसी विशिष्ट चर नामकरण पर भरोसा नहीं किया।
संबंधित सवाल
नए सवाल
c#
C # (उच्चारण "तेज देखें") Microsoft द्वारा विकसित एक उच्च स्तरीय, सांख्यिकीय रूप से टाइप किया हुआ, बहु-प्रतिमान प्रोग्रामिंग भाषा है। C # कोड आमतौर पर Microsoft के .NET परिवार के टूल और रन-टाइम को लक्षित करता है, जिसमें .NET फ्रेमवर्क, .NET कोर और Xamarin अन्य शामिल हैं। C # या C # के औपचारिक विनिर्देश में लिखे गए कोड के बारे में प्रश्नों के लिए इस टैग का उपयोग करें।