2016-01-20 11 views
6

Utilizziamo questo componente www.codeeffects.com che ci consente di creare regole aziendali basate sulle proprietà dell'oggetto.Come utilizzare un oggetto generato dinamicamente come origine dati del generatore di CodeEffects

Il html della vista è così:

@{ 
    ViewBag.Title = "Post Example"; 

    Html.CodeEffects().Styles() 
     .SetTheme(ThemeType.Gray) 
     .Render(); 
} 
@using (Html.BeginForm("Evaluate", "Post", FormMethod.Post)) 
{ 
    <div class="area"> 
     <h1 class="title">Post Example</h1> 
     <div style="margin-top:10px;"> 
      <span>Info:</span> 
      <span style="color:Red;">@ViewBag.Message</span> 
     </div> 
     <div style="margin-top:10px;"> 
      @{ 
    Html.CodeEffects().RuleEditor() 
     .Id("ruleEditor") 
     .SaveAction("Save", "Post") 
     .DeleteAction("Delete", "Post") 
     .LoadAction("Load", "Post") 
     .Mode(RuleType.Execution) 
     .ContextMenuRules(ViewBag.ContextMenuRules) 
     .ToolBarRules(ViewBag.ToolBarRules) 
     .Rule(ViewBag.Rule) 
     .Render(); 
      } 
     </div> 
    </div> 
    <div class="area"> 
     <h1 class="title" style="margin-top:20px;">Rule Test Form</h1> 
     @{ 
    Html.RenderPartial("_PatientForm"); 
     } 
    </div> 
} 
@{ 
    Html.CodeEffects().Scripts().Render(); 
} 

l'azione index del controller è così:

[HttpGet] 
     public ActionResult Index() 
     { 
      ViewBag.Rule = RuleModel.Create(typeof(Patient)); 
      return View(); 
     } 

Il Paziente classe è così:

// External methods and actions 
    [ExternalMethod(typeof(PatientService), "IsToday")] 
    [ExternalAction(typeof(PatientService), "RequestInfo")] 

    // Dynamic Menu Data Sources; details can be found at 
    // http://codeeffects.com/Doc/Business-Rules-Dynamic-Menu-Data-Sources 

    // The getEducationTypes() client-side function declared in /Views/Shared/_Layout.cshtml 
    [Data("Education", "getEducationTypes")] 
    // The List() method declared by the Physician class 
    [Data("Physicians", typeof(Physician), "List")] 
    public class Patient 
    { 
     // C-tor 
     public Patient() 
     { 
      this.ID = Guid.Empty; 
      this.Gender = Gender.Unknown; 
     } 

     // This property will not appear in the Rule Editor - Code Effects component ignores Guids. 
     // Details at http://codeeffects.com/Doc/Business-Rules-Data-Types 
     public Guid ID { get; set; } 

     [Field(DisplayName = "First Name", Description = "Patient's first name", Max = 30)] 
     public string FirstName { get; set; } 

     [Field(DisplayName = "Last Name", Max = 30, Description = "Patient's last name")] 
     public string LastName { get; set; } 

     [Field(DisplayName = "Email Address", ValueInputType = ValueInputType.User, Max = 150, Description = "Email address of the patient")] 
     public string Email { get; set; } 

     [Field(DisplayName = "Date of Birth", DateTimeFormat = "MMM dd, yyyy")] 
     public DateTime? DOB { get; set; } 

     [Field(ValueInputType = ValueInputType.User, Description = "Patient's gender")] 
     public Gender Gender { get; set; } 

     // This field uses the "Physicians" dynamic menu source (declared at class level) 
     [Field(DisplayName = "Physician", DataSourceName = "Physicians", Description = "Patient's primary physician")] 
     public int PhysicianID { get; set; } 

     // This field uses the "Education" client-side dynamic menu source (declared at class level) 
     [Field(DisplayName = "Education", DataSourceName = "Education", Description = "Patient's education level")] 
     public int EducationTypeID { get; set; } 

     [Field(Min = 0, Max = 200, Description = "Current pulse")] 
     public int? Pulse { get; set; } 

     [Field(Min = 0, Max = 200, DisplayName = "Systolic Pressure", Description = "Current systolic pressure")] 
     public int? SystolicPressure { get; set; } 

     [Field(Min = 0, Max = 200, DisplayName = "Diastolic Pressure", Description = "Current Diastolic pressure")] 
     public int? DiastolicPressure { get; set; } 

     [Field(Min = 0, Max = 110, Description = "Current temperature")] 
     public decimal? Temperature { get; set; } 

     [Field(DisplayName = "Headaches Box", Description = "Does the patient have frequent headaches?")] 
     public bool Headaches { get; set; } 

     [Field(DisplayName = "Allergies Box", Description = "Any allergies?")] 
     public bool Allergies { get; set; } 

     [Field(DisplayName = "Tobacco Box", Description = "Does the patient smoke?")] 
     public bool Tobacco { get; set; } 

     [Field(DisplayName = "Alcohol Box", Description = "Alcohol use?")] 
     public bool Alcohol { get; set; } 

     public Address Home { get; set; } 
     public Address Work { get; set; } 

     // This property is used to display outputs of rule actions 
     [ExcludeFromEvaluation] 
     public string Output { get; set; } 

     [Method("Full Name", "Joins together patient's first and last names")] 
     public string FullName() 
     { 
      return string.Format("{0} {1}", this.FirstName, this.LastName); 
     } 

     // Empty overload of the Register method. 
     // No Method attribute is needed here because its 
     // display name is the same as its declared name. 
     [Action(Description = "Registers new patient")] 
     public void Register() 
     { 
      this.Output = "The patient has been registered"; 
     } 

     // Overload of the Register method that takes one param. 
     // Both overloads can be used in Code Effects as two different actions 
     // as long as their display names are different. 
     [Action("Register with a Message", "Registers new patient with additional info")] 
     public void Register([Parameter(ValueInputType.User, Description = "Output message")] string message) 
     { 
      this.Output = message; 
     } 
    } 

Comunque vogliamo un oggetto dinamyc costruito usando il reflection, il metodo che ho creato è questo:

private static object CreateOurNewObject() 
     { 
      string _xml = "<root>" + 
       "<column name=\"Name\">Miron</column>" + 
       "<column name=\"LastName\">Abramson</column>" + 
       "<column name=\"Blog\">www.blog.mironabramson.com</column>" + 
       "</root>"; 

      XmlDocument xmlDoc = new XmlDocument(); 
      xmlDoc.LoadXml(_xml); 

      // create a dynamic assembly and module 
      AssemblyName assemblyName = new AssemblyName(); 
      assemblyName.Name = "tmpAssembly"; 
      System.Reflection.Emit.AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); 
      ModuleBuilder module = assemblyBuilder.DefineDynamicModule("tmpModule"); 

      // create a new type builder 
      TypeBuilder typeBuilder = module.DefineType("BindableRowCellCollection", TypeAttributes.Public | TypeAttributes.Class); 

      // Loop over the attributes that will be used as the properties names in out new type 
      foreach (XmlNode node in xmlDoc.SelectSingleNode("root").ChildNodes) 
      { 
       string propertyName = node.Attributes["name"].Value; 

       // Generate a private field 
       FieldBuilder field = typeBuilder.DefineField("_" + propertyName, typeof(string), FieldAttributes.Private); 
       // Generate a public property 
       PropertyBuilder property = 
        typeBuilder.DefineProperty(propertyName, 
            PropertyAttributes.None, 
            typeof(string), 
            new Type[] { typeof(string) }); 

       // The property set and property get methods require a special set of attributes: 

       MethodAttributes GetSetAttr = 
        MethodAttributes.Public | 
        MethodAttributes.HideBySig; 

       // Define the "get" accessor method for current private field. 
       MethodBuilder currGetPropMthdBldr = 
        typeBuilder.DefineMethod("get_value", 
               GetSetAttr, 
               typeof(string), 
               Type.EmptyTypes); 

       // Intermediate Language stuff... 
       ILGenerator currGetIL = currGetPropMthdBldr.GetILGenerator(); 
       currGetIL.Emit(OpCodes.Ldarg_0); 
       currGetIL.Emit(OpCodes.Ldfld, field); 
       currGetIL.Emit(OpCodes.Ret); 

       // Define the "set" accessor method for current private field. 
       MethodBuilder currSetPropMthdBldr = 
        typeBuilder.DefineMethod("set_value", 
               GetSetAttr, 
               null, 
               new Type[] { typeof(string) }); 

       // Again some Intermediate Language stuff... 
       ILGenerator currSetIL = currSetPropMthdBldr.GetILGenerator(); 
       currSetIL.Emit(OpCodes.Ldarg_0); 
       currSetIL.Emit(OpCodes.Ldarg_1); 
       currSetIL.Emit(OpCodes.Stfld, field); 
       currSetIL.Emit(OpCodes.Ret); 

       // Last, we must map the two methods created above to our PropertyBuilder to 
       // their corresponding behaviors, "get" and "set" respectively. 
       property.SetGetMethod(currGetPropMthdBldr); 
       property.SetSetMethod(currSetPropMthdBldr); 
      } 

      // Generate our type 
      Type generetedType = typeBuilder.CreateType(); 

      // Now we have our type. Let's create an instance from it: 
      object generetedObject = Activator.CreateInstance(generetedType); 

      // Loop over all the generated properties, and assign the values from our XML: 
      PropertyInfo[] properties = generetedType.GetProperties(); 

      int propertiesCounter = 0; 

      // Loop over the values that we will assign to the properties 
      foreach (XmlNode node in xmlDoc.SelectSingleNode("root").ChildNodes) 
      { 
       string value = node.InnerText; 
       properties[propertiesCounter].SetValue(generetedObject, value, null); 
       propertiesCounter++; 
      } 

      //Yoopy ! Return our new genereted object. 
      return generetedObject; 
     } 

Come si sostituisce la linea nell'azione indice per utilizzare quell'oggetto invece di Paziente? typeof (oggetto), non funzionerebbe.

risposta

1

Utilizzando lo TypeBuilder, è possibile provare a virare su un'interfaccia esistente o personalizzata per il tipo generato e quindi utilizzarlo come tipo.

typeBuilder.AddInterfaceImplementation(typeof(IMyInterface)); 

Ora l'oggetto che hai creato (nell'esempio di cui sopra) è di tipo IMyInterface, che si potrebbe poi utilizzare nel Index azione:

ViewBag.Rule = RuleModel.Create(typeof(IMyInterface)); 
+0

che non funzionerebbe perché non riesco a fare un typeof (mydinamobject). public ActionResult Index() { IMyInterface myObject = (IMyInterface) ObjectBuilder.CreateOurNewObject(); ViewBag.Rule = RuleModel.Crea (typeof (IMyInterface)); return View(); } –

+0

@Didier Potresti aggiornare il tuo post originale con il codice che stai cercando di scrivere e fornire l'errore che stai ricevendo dal compilatore? Non sono sicuro di dove stai cercando di creare un 'typeof (mydinamobject)'? Se il mio post precedente era fuorviante, "IMyInterface" non è un'interfaccia reale, ma solo un segnaposto. – Chase

+0

si prega di controllare questa domanda qui http://stackoverflow.com/questions/34929745/could-not-find-or-load-assembly-tmpassembly è il codice aggiornato –

3

estrarre una porzione del CreateOurNewObject() metodo che si occupa di una creazione del tipo. Chiamalo Crea tipo (stringa xml).

Modificare AssemlbyBuilderAccess.Run in AssemblyBuilderAccess.RunAndSave. Quindi, una volta creato il tipo, chiamare il metodo assemblyBuilder.Save(). Salvalo nella posizione che troverà Assembly.Load (ad esempio bin o una delle cartelle temporanee .net) o in qualsiasi altro posto così lungo nel percorso di ricerca.

Si usa per creare il tipo e creare un'istanza di oggetti.

Poi, nell'indice, chiamare

Type myType = CreateType(xml); 
RuleModel.Create(myType); 

Se si sta facendo la valutazione al di fuori assicurarsi di usare lo stesso tipo (non si rigenerano ogni volta). Il che significa che dovrai prima caricarlo.

Type myType = Assembly.Load(assemblyName); 
object myObject = Activator.CreateInstance(myType); 
//...populate myObject with necessary values based on your xml 
Evaluator ev = new Evaluator(myType, rule); 
bool result = ev.Evaluate(myObject); 

o si potrebbe utilizzare DynamicEvaluator, che chiama semplicemente myObject.GetType()

DynamicEvaluator ev = new DynamicEvaluator(rule); 
bool result = ev.Evaluate(myObject); 

Questo dovrebbe funzionare. Il pezzo importante qui è che si salva prima il gruppo (non può essere letto dalla memoria al momento) e che si trova in una cartella che fa parte di un percorso di ricerca in modo che Assembly.Load (nome) possa trovarlo.

+2

Brillante! Grazie. Ho avuto una domanda diversa sulle regole degli effetti di codice, ma la tua risposta mi ha dato qualcosa su cui lavorare. – Lacash