/// <summary> /// Invokes the rule on the specified <see cref="ModelInstance"/> as a result /// of the specified triggering <see cref="ModelEvent"/> /// </summary> /// <param name="root"></param> /// <param name="modelEvent"></param> internal void Invoke(ModelInstance root, ModelEvent modelEvent) { try { if (BeforeInvoke != null) { BeforeInvoke(this, EventArgs.Empty); } ModelEventScope.Perform(() => OnInvoke(root, modelEvent)); } finally { if (AfterInvoke != null) { AfterInvoke(this, EventArgs.Empty); } } }
/// <summary> /// Generates a script tag to embed a model including instance data into a server-rendered view. /// Supports initial customization of the model via an initialization delegate. /// </summary> /// <param name="query"></param> /// <param name="init"></param> /// <returns></returns> static void Model(TextWriter writer, object query, Delegate init) { // Raise the begin model event if (BeginModel != null) { BeginModel(query, EventArgs.Empty); } // Get the roots for each query var roots = new List <KeyValuePair <string, ServiceRequest.Query> >(); foreach (var prop in query.GetType().GetProperties()) { // Get the value of the model property var m = prop.GetValue(query, null); if (m == null) { continue; } // Convert the value into a query, if necessary var q = m as ServiceRequest.Query ?? Query(m); if (q != null) { q.LoadRoots(null); roots.Add(new KeyValuePair <string, ServiceRequest.Query>(prop.Name, q)); } } // Create the request object ServiceRequest request = new ServiceRequest(roots.Select(r => r.Value).ToArray()); // Synthesize init new changes for new query roots ModelTransaction initChanges = (ModelTransaction)request.Queries .SelectMany(q => q.Roots) .Where(i => i.IsNew) .Select(i => new ModelInitEvent.InitNew(i)) .Cast <ModelEvent>() .ToList(); // Perform the initialization action, if specified if (init != null) { // Determine matching parameters var parameters = new object[init.Method.GetParameters().Length]; foreach (var p in init.Method.GetParameters()) { foreach (var root in roots) { if (p.Name.Equals(root.Key, StringComparison.InvariantCultureIgnoreCase)) { // List parameter if (p.ParameterType.IsArray) { parameters[p.Position] = root.Value.Roots.Select(r => r.Instance).ToArray(); } // Instance parameter else { parameters[p.Position] = root.Value.Roots.Select(r => r.Instance).FirstOrDefault(); } break; } } // Throw an exception if the model property value cannot be cast to the corresponding initialization parameter if (parameters[p.Position] != null && !p.ParameterType.IsAssignableFrom(parameters[p.Position].GetType())) { throw new ArgumentException(String.Format("The model property '{0}' cannot be converted into the initialization parameter type of '{1}'.", p.Name, p.ParameterType.FullName)); } // Throw an exception if a valid model parameter could not be found to pass to the initialization delegate if (parameters[p.Position] == null && query.GetType().GetProperty(p.Name, BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance) == null) { throw new ArgumentException(String.Format("A model property could not be found to pass to the initialization parameter '{0}'.", p.Name)); } } // Perform initialization while capturing changes initChanges.Record(() => { // Prevent property change rules from running until after initialization ModelEventScope.Perform(() => init.DynamicInvoke(parameters)); }); } // Invoke the request queries ServiceResponse response = request.Invoke(initChanges); response.Model = roots.ToDictionary(r => r.Key, r => r.Value); foreach (var q in response.Model.Values) { q.ReducePaths(); } // Track the current model roots to support server template rendering foreach (var root in response.Model) { Templates.Page.Current.Model[root.Key] = root.Value.IsList ? (object)root.Value.Roots : (root.Value.Roots.Length == 1 ?root.Value.Roots[0] : null); } // Write the model embed script to the writer writer.Write("<script type=\"text/javascript\">$exoweb("); JsonUtility.Serialize(writer, response); writer.Write(");</script>"); // Raise the end model event if (EndModel != null) { EndModel(query, EventArgs.Empty); } }