I'm looking for a working example of dynamic NRules. Actually, I want to write the rules in a notepad file and want to read them at runtime.
I have been searching it through the internet for last 4 days but didn't find anything.
Any help is appreciable.
NRules is primarily positioned as a rules engine where rules are authored in C#, and compiled into assemblies.
There is a companion project https://github.com/NRules/NRules.Language that defines a textual DSL (called Rule#) for expressing rules. It's less feature-complete than C# DSL, but is potentially what you are looking for.
You would still have a project in C# that loads the textual rules from the file system or a DB, and drives the rules engine.
You would use https://www.nuget.org/packages/NRules.RuleSharp package for parsing the textual rules into a rule model, and https://www.nuget.org/packages/NRules.Runtime to compile the rule model into an executable form and run the rules.
Given a domain model:
namespace Domain
{
public class Customer
{
public string Name { get; set; }
public string Email { get; set; }
}
}
And given a text file with rules called MyRuleFile.txt:
using Domain;
rule "Empty Customer Email"
when
var customer = Customer(x => string.IsNullOrEmpty(x.Email));
then
Console.WriteLine("Customer email is empty. Customer={0}", customer.Name);
Here is an example of how the rules driver code might look like:
var repository = new RuleRepository();
repository.AddNamespace("System");
//Add references to any assembly that the rules are using, e.g. the assembly with the domain model
repository.AddReference(typeof(Console).Assembly);
repository.AddReference(typeof(Customer).Assembly);
//Load rule files
repository.Load(#"MyRuleFile.txt");
//Compile rules
var factory = repository.Compile();
//Create a rules session
var session = factory.CreateSession();
//Insert facts into the session
session.Insert(customer);
//Fire rules
session.Fire();
Output:
Customer email is empty. Customer=John Do
Related
I'm using OData v4 and the models are configured with the EdmModel Builder for our .NET Web API.
I have two models defined like this:
public class Customer
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
}
public class Order
{
public int OrderId { get; set; }
public string Category { get; set; }
public Customer OrderCustomer { get; set;}
}
These models have corresponding controllers and are registered as follows:
builder.EntitySet<Customer>("Customers")
.EntityType
.HasKey(x => x.CustomerId);
builder.EntitySet<Order>("Orders")
.EntityType
.HasKey(x => x.OrderId)
.Expand(
maxDepth: 2,
expandType: SelectExpandType.Automatic,
properties: nameof(Order.OrderCustomer));
I'm able to make OData requests to both these endpoints as follows:
/Customers/{Id} and
/Orders/{Id}.
I'm expecting that when I query the Orders, the nested EntitySet Customers will auto expand since I've set expandType: SelectExpandType.Automatic on the Order EntitySet. However, I can't get the CustomerOrder property to auto-expand on the Customer and I have to call the request with an expand parameter:
/Orders/{Id}?$expand=OrderCustomer.
I think this is because both Customer and Order are registered as EntitySets, so OData expects that an expand parameter be provided if they are nested. Is there a way to get the OrderCustomer property to auto-expand (i.e. without the need for the expand parameter to be provided)? My understanding of OData/ Edm Models is pretty elementary so any help is appreciated.
Your fluent configuration is correct, for OData v4, that will work for both collection and item queries.
If it is not working for you there are 3 possible issues:
You do not appear to be using the OData v4 URL convention for item queries, in v4 the expected URL is:
/Orders({Id})
This brings into question how you modified the router to support the v3 syntax, there are multiple variation on how to implement the v3 routes so it is possible that changes made in this area could affect the way that default expansion and selection is applied, or if it should apply.
You may not be including the navigation data in your data query. If the data is not retrieved from the data store, then it stands to reason that it will not be in the output recordset. If you are manually using the ODataQueryOptions.ApplyTo() to apply the user request to your query, then this will not take into account the configuration on the model, it will only apply thequery options that the caller has specified.
The caller might be specifying an Empty $expand= which will cancel out the auto configuration. Even if the originating caller has not specified any query options it is common enough for OData APIs to have standard or custom middleware running that might be manipulating the request query strings. To verify the URL is untampered, log it in your GET method handler and make sure the $expand is not specified.
As with the previous point, the ODataQueryOptions parameter in your GET method should NOT show any value for the SelectExpand if you want the auto configuration to be applied.
Finally, the last place to check is that you haven't overriden the default EnableQueryAttribute. If you have implemented your own custom implementation of EnableQueryAttribute then make sure that you still call the base implementation to correctly apply the ODataQueryOptions AND the schema defaults to the underlying IQueryable result.
In addition to the answer provided by Chris Schaller.
Another issue I had was caused by the casing on the property. For example, I have camelCasing enabled on the builder
builder.EnableLowerCamelCase();
This means the naming in the Expand on the configuration needed to be updated to match.
.Expand(
maxDepth: 2,
expandType: SelectExpandType.Automatic,
properties: "orderCustomer"); // <-- camelCase
This seems to be required even if EnablePropertyNameCaseInsensitive is enabled in the ODataOptions.
Question is in the title. Can we programmatically change the database table which an object in the Model class, like one below, refers to and continue to operate on the new table?
public class Word
{
public int ID { get; set; }
public string Text { get; set; }
}
This originally refers to "Words" table automatically in EntityFramework, is there a way to change it before/during runtime? If so, how?
EDIT:
I get all the string used in Views in the project from the database table, "Words", by their ID's. Now, what I want is, a user enters a new language to system, and a new table will be created, for example WordsEnglish. From then, the Word object will refer to WordEnglish, if user selects English as language.
It would be desirable with a use case to better understand what you are trying to accomplish, but here goes...
In the DbContext.OnModelCreating method you can configure the model, e.g.
// Removes pluralization convention for all tables.
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
or
// Specific table name for Word Entity.
modelBuilder.Entity<Word>().ToTable("TableContainingWords");
If you are changing your model, Code First Migrations might be what you need.
I havent found a way to truly dynamically extend an EF model at runtime. Given what goes on in DB context inherited class, the use of generated views for performance and a model class approach, avoiding recompilation seems hard. I have generated code, compiled and access this using assembly discovery approaches. But this is all unsatisfactory from my viewpoint , so i have stopped investigating this path. Very clunky outcome.
Ironically the topic you provide as a use case for such a problem, is one that doesnt need dynamic EF in my view.
I have exactly the same use case, language specific look for messages/labels etc Ie a language specific textpool.
Why not add language to the class/table.
Use a table or Enum for supported languages.
Use Language in the Textpool table/s
Use a different model class for presentation. (view model).
So you can present it the way like .
public class Word
{
Guid ID {get;set;} // logical key is WordID + Language
public int WordID { get; set; } // implement with new id or 2 field key
public Language Language {get;set;} // see cultureInfo for more details
public bool IsMaster {get;set;}
public string Text { get; set; } // consider renaming due to reserved word implications
}
public class language
{
int ID,
String Lang
}
}
My place of work currently maintains a website for several customers which is written using classic asp. Each customer requires specific parts of the website to be written specifically to them.
For example, customer A requires an Address to be input, displayed and stored in the following format:
Address Line 1
Address Line 2
Address Line 3
Address Line 4
Address Line 5
Postcode
whereas customer B requires the Address to be input, displayed and stored as:
Street
Town
City
Postcode
and so forth...
Therefore, my place of work took the path of storing the data as xml in the database and using xsl (of which I currently know little) to transform the data to html.
So if we require information from the user via a html form, the xml is transformed using xsl. The user then enters the information and submits the data via the form. An asp page is then used to validate the data. This asp page is specific to the xsl page used to display the form. Therefore, we are now in a postion where for each customer we have many xsl pages and many customer specific asp pages (where much of the code is duplicated).
I have been asked to move the site over to asp.net mvc3 and to remove much of the duplication and was wondering what would be the best way to cater for this customer specific field functionality. My preference would be to keep the data stored as xml as the database layer is accessed using com components which I would like to reuse without changing.
I have read that I could keep the xsl pages and develop an xslt view engine to display the html. However, I am not sure how I would validate the data when the user submits the form?
What would be the best way to display customer specific fields if I was to remove the xsl completely? Or would I have to have customer specific views and view models?
Any thoughts would be much appreciated.
If you really want to use MVC's built in validation / model functionality I think your best bet would be to use the XmlSerializer or use DataContracts to develop something that serializes to and from your XML (once its retrieved from the COM objects, so you don't need to re-code those), then you can use those classes as Models for MVC and use the standard data annotations for taking advantage of the richer MVC model functionality and skip the XSL step entirely.
To couple this with a custom specific view, what I typically do is override the default view engine to have one that actually will try names that are more specific to the customer/object and then fallback to a general one.
This view engine would allow you to pass a view to pass a view name (ie. FallbackViewEngine.BuildViewName("General", "Customer Name") and it would look for "General.Customer Name.cshtml" first and then "General.cshtml" as a fallback. This way you can actually use customer specific views in your view folder.
public class FallbackViewEngine : RazorViewEngine
{
const string NameSeparator = "==";
const string FileSeparator = ".";
public static string BuildViewName(string root, params string[] fallbackList)
{
if (string.IsNullOrWhiteSpace(root)) throw new ArgumentNullException("root");
if (fallbackList == null) throw new ArgumentNullException("fallbackList");
var sb = new StringBuilder(root);
foreach (var s in fallbackList)
{
if (string.IsNullOrWhiteSpace(s)) continue;
sb.Append(NameSeparator);
sb.Append(s);
}
return sb.ToString();
}
public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
{
if (string.IsNullOrWhiteSpace(viewName)) throw new ArgumentNullException("viewName");
var names = viewName.Split(new string[] {NameSeparator}, StringSplitOptions.None);
var searched = new List<string>();
//iterate from specific to general
for (var i = names.Length; i >= 1; i--)
{
var result = base.FindView(controllerContext, string.Join(FileSeparator, names, 0, i), masterName, useCache);
if (result.View != null)
{
return result;
}
else
{
searched.AddRange(result.SearchedLocations);
}
}
return new ViewEngineResult(searched);
}
}
I have a model with some parameters that a User should be able to see but not edit and others they should be able to edit. The same is true of the Author. So, I used [UIHint("Author")] and [UIHint("User")] attributes and wrote a couple editor templates, like so:
#inherits System.Web.Mvc.WebViewPage
#if (ViewBag.RoleId > (int)Role.RoleEnum.Author)
{
#Html.TextBoxFor(m => m, new { disabled = "disabled" })
}
else
{
#Html.TextBoxFor(m => m)
}
This almost does what I want. I'd like to be able to apply these attributes to booleans and get check boxes - like the default EditorFor. I suppose I could make another template and use something like [UIHint("AuthorBool")], but I'm hoping to come up with something better.
Hi Oniel,
You could create separate ViewModels for each type of user and use the data annotation of [ReadOnly]. But then you get into the realms of large amounts of repetition.
Personally I would recommend that you create your own version of each data type and implement standard role based handling using additionalmetadata data annotations to customise. Okay bit of work to begin with but then massively re-usable and highly portable.
Example:
[UIHint("MyCustomTemplateControl")]
[AdditionalMetadata("DenyEditUnlessInRole", "Admin")]
public string MyName { get; set; }
or:
[UIHint("MyCustomTemplateControl")]
[AdditionalMetadata("DenyEditIfInRole", "StandardUser")]
public string MyName { get; set; }
You can perform a code based / database based lookup in a class somewhere else that your datatypes templates query to make a decision on whether a user/role should get read/edit access to this property.
Does this make sense?
As a third option, create an editortemplate for the entire object and only include those fields and field types you are interesting in exposing.
MVC is so flexible - I suppose in the end it depends on how DRY do you want to make your code.
Good luck!
Dan.
I'm working on writing a non-trivial "unit test" (perhaps better called a "UI test"?). In this case, I think I want a test that (reflectively?) finds all appropriate action handlers and then verifies that our SiteMap has a node for those Action Handlers. What I need is to identify when a developer adds a page to our system and forgets to add it to the SiteMap (this seems to be a common problem and I'd like to make it go away, which a test should easily be able to do for us). Ultimately, we want to make sure that any page that a user can land on will have a home in our SiteMap so that it builds the appropriate breadcrumb to tell the user where they are in our system (that breadcrumb part already works perfect for us, as long as the page is in the SiteMap). I would much rather try to do this with a test than try to force some policy/procedure update on us that is yet another thing we have to deal with.
Any tips on some existing code to start from in this endeavor? And if not, any thoughts on the best way to do this?
One possibility that I am considering is to reflectively identify any method that is decorated with the AcceptVerbs attribute which doesn't have a return type of JsonResult (and perhaps a couple others which would clearly not be "web pages" such as FileResult). And perhaps limit my search by first identifying classes which inherit System.Web.Mvc.Controller.
I'd love to hear a better way to do this, though. And would love even more if most of this was already written and shared to save me some time. :-)
I see this is mvc application=>
your solution would lie in routing(for me_)
you know every option which is not marked as [nonaction] is registered in Global.asax
which contains
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
and in this routes you will be able to find all accessible methods =>eg actions
this is created in basic mvc
Hope this will help
Here is the important part of my solution. This works well for us.
var mvcAssembly = typeof (AccountController).Assembly;
AllControllers = mvcAssembly.GetTypes().Where(type => type.IsSubclassOf(typeof (Controller))).ToList();
UnfilteredActionHandlers = new List<MethodInfo>();
foreach (var controller in AllControllers)
{
UnfilteredActionHandlers.AddRange(controller.GetMethods()
.Where(methodInfo =>
typeof (ActionResult).IsAssignableFrom(methodInfo.ReturnType)
&& !ControllerClassesToIgnore.Contains(methodInfo.ReflectedType)));
}
We have a few collections that we filter the UnfilteredActionHandlers collection by depending on how we want to use them:
internal List<MethodInfo> UnfilteredActionHandlers { get; set; }
internal List<MethodInfo> ActionHandlersExcludingFilteredReturnTypes { get; set; }
internal List<MethodInfo> ActionHandlersFilteredByAttributes { get; set; }
internal List<MethodInfo> ActionHandlersFilteredByBoth { get; set; }
internal static List<Type> ReturnTypesToIgnore { get; set; }
internal static List<Type> RequiredAttributes { get; set; }
internal static List<Type> ControllerClassesToIgnore { get; set; }