Represents a response to a service request. Depending on the type of request, different values will be initialize on the instance.
Inheritance: IJsonSerializable
 internal ServiceRequestEventArgs(ServiceRequest request, ServiceResponse response)
 {
     this.Request = request;
     this.Response = response;
 }
Exemple #2
0
        /// <summary>
        /// Outputs the JSON for the specified instance to the response stream.
        /// </summary>
        /// <param name="response"></param>
        internal ServiceResponse Invoke(ModelTransaction initChanges)
        {
            // Raise the begin request event
            ExoWeb.OnBeginRequest(this);

            // Create a response for the request
            ServiceResponse response = new ServiceResponse();

            response.ServerInfo = new ServerInformation();
            response.Changes    = initChanges;

            try
            {
                // Set the types to return from the request
                response.Types = Types;

                // Apply view initialization changes
                if (Changes != null && Changes.Length > 0 && Changes[0].Source == ChangeSource.Init)
                {
                    response.Changes = Changes[0].Changes;
                    response.Changes.Perform();
                }

                // Load root instances
                if (Queries != null)
                {
                    foreach (var query in Queries)
                    {
                        query.Prepare(response);
                        query.LoadRoots(response.Changes);
                    }
                }

                // Preload the scope of work before applying changes
                PerformQueries(response, false, initChanges != null);

                // Apply additional changes and raise domain events
                ApplyChanges(response);

                // Load instances specified by load queries
                PerformQueries(response, true, initChanges != null);

                // Condense the transaction log
                if (response.Changes != null)
                {
                    response.Changes.Condense();
                }

                // Send conditions for instances loaded in the request
                if (response.Instances != null || response.Changes != null)
                {
                    // Add instances created during the request
                    if (response.Changes != null)
                    {
                        foreach (var instance in response.Changes.OfType <ModelInitEvent.InitNew>().Select(modelEvent => modelEvent.Instance))
                        {
                            response.inScopeInstances.Add(instance);
                        }
                    }

                    // Ensure conditions are evaluated before extracting them
                    ExoWeb.OnEnsureConditions(response, response.inScopeInstances);

                    // Extract conditions for all instances involved in the request
                    Dictionary <string, List <Condition> > conditionsByType = new Dictionary <string, List <Condition> >();
                    foreach (var condition in response.inScopeInstances.SelectMany(instance => Condition.GetConditions(instance)))
                    {
                        List <Condition> conditions;
                        if (!conditionsByType.TryGetValue(condition.Type.Code, out conditions))
                        {
                            conditionsByType[condition.Type.Code] = conditions = new List <Condition>();
                        }

                        if (!conditions.Contains(condition))
                        {
                            conditions.Add(condition);
                        }
                    }
                    response.Conditions = conditionsByType;
                }
            }
            finally
            {
                // Raise the end request event
                ExoWeb.OnEndRequest(this, response);
            }

            // Return the response
            return(response);
        }
Exemple #3
0
            /// <summary>
            /// Processes static property paths in order to determine the information to serialize.
            /// </summary>
            /// <param name="path"></param>
            internal static void PrepareStaticPath(string path, ServiceResponse response)
            {
                string type     = null;
                string property = null;

                try
                {
                    if (path.IndexOf('.') < 0)
                    {
                        throw new ArgumentException("'" + path + "' is not a valid static property path.");
                    }

                    // Split the static property reference
                    int propertyIndex = path.LastIndexOf('.');
                    type     = path.Substring(0, propertyIndex);
                    property = path.Substring(propertyIndex + 1);

                    // Get the model type
                    ModelType modelType = ModelContext.Current.GetModelType(type);
                    if (modelType == null)
                    {
                        throw new ArgumentException("'" + type + "' is not a valid model type for the static property path of '" + path + "'.");
                    }

                    // Get the model property
                    ModelProperty modelProperty = modelType.Properties[property];
                    if (modelProperty == null || !modelProperty.IsStatic)
                    {
                        throw new ArgumentException("'" + property + "' is not a valid property for the static property path of '" + path + "'.");
                    }

                    // Add the property to the set of static properties to serialize
                    response.GetModelTypeInfo(modelType).StaticProperties.Add(modelProperty);

                    // Register instances for static reference properties to be serialized
                    ModelReferenceProperty reference = modelProperty as ModelReferenceProperty;
                    if (reference != null)
                    {
                        // Get the cached set of instances to be serialized for the property type
                        ModelTypeInfo propertyTypeInfo = response.GetModelTypeInfo(reference.PropertyType);

                        // Static lists
                        if (reference.IsList)
                        {
                            foreach (ModelInstance instance in modelType.GetList(reference))
                            {
                                ModelTypeInfo typeInfo = instance.Type == reference.PropertyType ? propertyTypeInfo : response.GetModelTypeInfo(instance.Type);
                                if (!typeInfo.Instances.ContainsKey(instance.Id))
                                {
                                    typeInfo.Instances.Add(instance.Id, new ModelInstanceInfo(instance));
                                }
                            }
                        }

                        // Static references
                        else
                        {
                            ModelInstance instance = modelType.GetReference(reference);

                            if (instance != null)
                            {
                                ModelTypeInfo typeInfo = instance.Type == reference.PropertyType ? propertyTypeInfo : response.GetModelTypeInfo(instance.Type);
                                if (!typeInfo.Instances.ContainsKey(instance.Id))
                                {
                                    typeInfo.Instances.Add(instance.Id, new ModelInstanceInfo(instance));
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new ApplicationException(string.Format("Error preparing static path '{0}'{1}{2}: [{3}]",
                                                                 path,
                                                                 type == null ? string.Empty : (" for type '" + type + "'"),
                                                                 property == null ? string.Empty : (" and property '" + property + "'"),
                                                                 ex.Message),
                                                   ex);
                }
            }
Exemple #4
0
            /// <summary>
            /// Prepares the query by parsing instance and static paths to determine
            /// what information is being requested by the query.
            /// </summary>
            /// <param name="response"></param>
            internal void Prepare(ServiceResponse response)
            {
                if (Include != null && Include.Length > 0)
                {
                    string paths = "{";
                    foreach (var p in Include)
                    {
                        var path = p.Replace(" ", "");
                        if (path.StartsWith("this."))
                            path = path.Substring(5);
                        else if (path.StartsWith("this{"))
                            path = path.Substring(4);

                        ModelPath instancePath;
                        if (From.TryGetPath(path, out instancePath))
                            paths += path + ",";
                        else
                            PrepareStaticPath(path, response);
                    }

                    if (paths.Length > 1)
                        Path = From.GetPath(paths.Substring(0, paths.Length - 1) + "}");
                }
            }
Exemple #5
0
        /// <summary>
        /// Recursively builds up a list of instances to serialize.
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="instances"></param>
        /// <param name="paths"></param>
        /// <param name="path"></param>
        static void ProcessInstance(ModelInstance instance, ModelStepList steps, bool includeInResponse, bool inScope, bool forLoad, ServiceResponse response)
        {
            // Avoid processing cached instances not included in the response
            if (instance.IsCached && !includeInResponse)
            {
                return;
            }

            ModelInstanceInfo instanceInfo = null;

            // Track the instance if the query represents a load request
            if (includeInResponse)
            {
                // Fetch or initialize the dictionary of instances for the type of the current instance
                ModelTypeInfo typeInfo = response.GetModelTypeInfo(instance.Type);

                // Add the current instance to the dictionary if it is not already there
                if (!typeInfo.Instances.TryGetValue(instance.Id, out instanceInfo))
                {
                    typeInfo.Instances[instance.Id] = instanceInfo = new ModelInstanceInfo(instance);
                }

                // Track in scope instances to limit conditions
                if (inScope && !instance.IsCached)
                {
                    response.inScopeInstances.Add(instance);
                }
            }

            // Exit immediately if there are no child steps to process
            if (steps == null)
            {
                return;
            }

            // Process query steps for the current instance
            foreach (var step in steps)
            {
                // Recursively process child instances
                foreach (var childInstance in step.GetInstances(instance))
                {
                    ProcessInstance(childInstance, step.NextSteps, includeInResponse, inScope, forLoad, response);
                }

                // Mark value lists to be included during serialization
                if (step.Property.IsList && includeInResponse)
                {
                    instanceInfo.IncludeList(step.Property);
                }
            }

            // Run all property get rules on the instance
            if (inScope)
            {
                if (forLoad)
                {
                    instance.RunPendingPropertyGetRules(p => p is ModelValueProperty || steps.Any(s => s.Property == p));
                }
                else
                {
                    instance.RunPendingPropertyGetRules(p => p is ModelValueProperty);
                }
            }
        }
Exemple #6
0
        /// <summary>
        /// Raises domain events.
        /// </summary>
        void RaiseEvents(ServiceResponse response, ModelTransaction transaction)
        {
            // Process each event in the request
            if (Events != null)
            {
                response.Events = Events
                    .Select((domainEvent) =>
                    {
                        // Restore the instance to be a valid model instance
                        if (domainEvent.Instance != null)
                        {
                            if (transaction != null)
                                domainEvent.Instance = transaction.GetInstance(domainEvent.Instance.Type, domainEvent.Instance.Id);
                            else
                                domainEvent.Instance = domainEvent.Instance.Type.Create(domainEvent.Instance.Id);
                        }

                        var result = domainEvent.Raise(transaction);

                        if (result == null)
                            return null;

                        ModelType type;
                        ModelInstance[] roots;
                        bool isList;

                        if (ExoWeb.TryConvertQueryInstance(result, out type, out roots, out isList))
                        {
                            Query newQuery = new Query(type, roots, true, isList, domainEvent.Include);
                            newQuery.Prepare(response);

                            Query[] newQueries = new Query[] { newQuery };
                            Queries = Queries == null ? newQueries : Queries.Union(newQueries).ToArray();

                            return isList ? (object)roots : (object)roots[0];
                        }
                        else if (domainEvent.Include != null && domainEvent.Include.Length > 0)
                        {
                            Query newQuery = new Query(domainEvent.Instance.Type, new[] { domainEvent.Instance }, true, false, domainEvent.Include);
                            newQuery.Prepare(response);

                            Query[] newQueries = new Query[] { newQuery };
                            Queries = Queries == null ? newQueries : Queries.Union(newQueries).ToArray();

                            return result;
                        }
                        else
                            return result;
                    })
                    .ToArray();
            }
        }
Exemple #7
0
            /// <summary>
            /// Processes static property paths in order to determine the information to serialize.
            /// </summary>
            /// <param name="path"></param>
            internal static void PrepareStaticPath(string path, ServiceResponse response)
            {
                string type = null;
                string property = null;

                try
                {
                    if (path.IndexOf('.') < 0)
                        throw new ArgumentException("'" + path + "' is not a valid static property path.");

                    // Split the static property reference
                    int propertyIndex = path.LastIndexOf('.');
                    type = path.Substring(0, propertyIndex);
                    property = path.Substring(propertyIndex + 1);

                    // Get the model type
                    ModelType modelType = ModelContext.Current.GetModelType(type);
                    if (modelType == null)
                        throw new ArgumentException("'" + type + "' is not a valid model type for the static property path of '" + path + "'.");

                    // Get the model property
                    ModelProperty modelProperty = modelType.Properties[property];
                    if (modelProperty == null || !modelProperty.IsStatic)
                        throw new ArgumentException("'" + property + "' is not a valid property for the static property path of '" + path + "'.");

                    // Add the property to the set of static properties to serialize
                    response.GetModelTypeInfo(modelType).StaticProperties.Add(modelProperty);

                    // Register instances for static reference properties to be serialized
                    ModelReferenceProperty reference = modelProperty as ModelReferenceProperty;
                    if (reference != null)
                    {
                        // Get the cached set of instances to be serialized for the property type
                        ModelTypeInfo propertyTypeInfo = response.GetModelTypeInfo(reference.PropertyType);

                        // Static lists
                        if (reference.IsList)
                        {
                            foreach (ModelInstance instance in modelType.GetList(reference))
                            {
                                ModelTypeInfo typeInfo = instance.Type == reference.PropertyType ? propertyTypeInfo : response.GetModelTypeInfo(instance.Type);
                                if (!typeInfo.Instances.ContainsKey(instance.Id))
                                    typeInfo.Instances.Add(instance.Id, new ModelInstanceInfo(instance));
                            }
                        }

                        // Static references
                        else
                        {
                            ModelInstance instance = modelType.GetReference(reference);

                            if (instance != null)
                            {
                                ModelTypeInfo typeInfo = instance.Type == reference.PropertyType ? propertyTypeInfo : response.GetModelTypeInfo(instance.Type);
                                if (!typeInfo.Instances.ContainsKey(instance.Id))
                                    typeInfo.Instances.Add(instance.Id, new ModelInstanceInfo(instance));
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new ApplicationException(string.Format("Error preparing static path '{0}'{1}{2}: [{3}]",
                        path,
                        type == null ? string.Empty : (" for type '" + type + "'"),
                        property == null ? string.Empty : (" and property '" + property + "'"),
                        ex.Message),
                    ex);
                }
            }
Exemple #8
0
 private void ProcessQueryInstances(ServiceResponse response, bool forLoad)
 {
     // Recursively build up the list of instances to serialize
     foreach (Query query in Queries)
     {
         if ((forLoad && query.ForLoad) || query.InScope)
             foreach (ModelInstance root in query.Roots)
                 ProcessInstance(root, query.Path != null ? query.Path.FirstSteps : null, forLoad && query.ForLoad, query.InScope, forLoad, response);
     }
 }
Exemple #9
0
        /// <summary>
        /// Performs the queries for the current request, either to prepare the model before 
        /// applying changes (forLoad = false), or to actually load the model and transmit the 
        /// requested instances to the client.
        /// </summary>
        /// <param name="response"></param>
        /// <param name="forLoad"></param>
        void PerformQueries(ServiceResponse response, bool forLoad, bool forInit)
        {
            // Load data based on the specified queries
            if (Queries != null)
            {
                // Record changes while processing queries
                if (forInit)
                {
                    response.Changes.Record(() => ProcessQueryInstances(response, forLoad), evt =>
                    {
                        if (evt.Instance.IsNew)
                            return true;

                        var refChange = evt as ModelReferenceChangeEvent;
                        if (refChange != null)
                        {
                            return (refChange.OldValue != null && refChange.OldValue.IsNew) ||
                                (refChange.NewValue != null && refChange.NewValue.IsNew);
                        }

                        var listChange = evt as ModelListChangeEvent;
                        if (listChange != null)
                        {
                            return (listChange.Added != null && listChange.Added.Any(i => i.IsNew)) ||
                                (listChange.Removed != null && listChange.Removed.Any(i => i.IsNew));
                        }

                        return false;
                    });
                }
                else if (forLoad)
                    (response.Changes ?? new ModelTransaction()).Record(() => ProcessQueryInstances(response, forLoad));
                else
                    ProcessQueryInstances(response, forLoad);
            }
        }
Exemple #10
0
        /// <summary>
        /// Apply changes and raises domain events.
        /// </summary>
        void ApplyChanges(ServiceResponse response)
        {
            // Consolidate previous changes
            ModelTransaction transaction =
                ModelTransaction.Combine((Changes ?? new ChangeSet[0])
                .Where(cs => cs.Source != ChangeSource.Init)
                .Select(cs => cs.Changes));

            // Chain the transactions about to be applied to any previously applied initialization changes
            if (response.Changes != null && transaction != null)
                response.Changes.Chain(transaction);

            // Apply changes and raise domain events
            if (transaction != null)
                response.Changes = transaction.Perform(() => RaiseEvents(response, transaction), MaxKnownId);

            // Otherwise, just raise events
            else
                response.Changes = (response.Changes ?? new ModelTransaction()).Record(() => RaiseEvents(response, null), MaxKnownId);
        }
Exemple #11
0
        /// <summary>
        /// Recursively builds up a list of instances to serialize.
        /// </summary>
        /// <param name="instance"></param>
        /// <param name="instances"></param>
        /// <param name="paths"></param>
        /// <param name="path"></param>
        static void ProcessInstance(ModelInstance instance, ModelStepList steps, bool includeInResponse, bool inScope, bool forLoad, ServiceResponse response)
        {
            // Avoid processing cached instances not included in the response
            if (instance.IsCached && !includeInResponse)
                return;

            ModelInstanceInfo instanceInfo = null;

            // Track the instance if the query represents a load request
            if (includeInResponse)
            {
                // Fetch or initialize the dictionary of instances for the type of the current instance
                ModelTypeInfo typeInfo = response.GetModelTypeInfo(instance.Type);

                // Add the current instance to the dictionary if it is not already there
                if (!typeInfo.Instances.TryGetValue(instance.Id, out instanceInfo))
                    typeInfo.Instances[instance.Id] = instanceInfo = new ModelInstanceInfo(instance);

                // Track in scope instances to limit conditions
                if (inScope && !instance.IsCached)
                    response.inScopeInstances.Add(instance);
            }

            // Exit immediately if there are no child steps to process
            if (steps == null)
                return;

            // Process query steps for the current instance
            foreach (var step in steps)
            {
                // Recursively process child instances
                foreach (var childInstance in step.GetInstances(instance))
                    ProcessInstance(childInstance, step.NextSteps, includeInResponse, inScope, forLoad, response);

                // Mark value lists to be included during serialization
                if (step.Property.IsList && includeInResponse)
                    instanceInfo.IncludeList(step.Property);
            }

            // Run all property get rules on the instance
            if (inScope)
            {
                if (forLoad)
                    instance.RunPendingPropertyGetRules(p => p is ModelValueProperty || steps.Any(s => s.Property == p));
                else
                    instance.RunPendingPropertyGetRules(p => p is ModelValueProperty);
            }
        }
Exemple #12
0
        /// <summary>
        /// Outputs the JSON for the specified instance to the response stream.
        /// </summary>
        /// <param name="response"></param>
        internal ServiceResponse Invoke(ModelTransaction initChanges)
        {
            // Raise the begin request event
            ExoWeb.OnBeginRequest(this);

            // Create a response for the request
            ServiceResponse response = new ServiceResponse();
            response.ServerInfo = new ServerInformation();
            response.Changes = initChanges;

            try
            {
                // Set the types to return from the request
                response.Types = Types;

                // Apply view initialization changes
                if (Changes != null && Changes.Length > 0 && Changes[0].Source == ChangeSource.Init)
                {
                    response.Changes = Changes[0].Changes;
                    response.Changes.Perform();
                }

                // Load root instances
                if (Queries != null)
                {
                    foreach (var query in Queries)
                    {
                        query.Prepare(response);
                        query.LoadRoots(response.Changes);
                    }
                }

                // Preload the scope of work before applying changes
                PerformQueries(response, false, initChanges != null);

                // Apply additional changes and raise domain events
                ApplyChanges(response);

                // Load instances specified by load queries
                PerformQueries(response, true, initChanges != null);

                // Condense the transaction log
                if (response.Changes != null)
                    response.Changes.Condense();

                // Send conditions for instances loaded in the request
                if (response.Instances != null || response.Changes != null)
                {
                    // Add instances created during the request
                    if (response.Changes != null)
                    {
                        foreach (var instance in response.Changes.OfType<ModelInitEvent.InitNew>().Select(modelEvent => modelEvent.Instance))
                            response.inScopeInstances.Add(instance);
                    }

                    // Ensure conditions are evaluated before extracting them
                    ExoWeb.OnEnsureConditions(response, response.inScopeInstances);

                    // Extract conditions for all instances involved in the request
                    Dictionary<string, List<Condition>> conditionsByType = new Dictionary<string, List<Condition>>();
                    foreach (var condition in response.inScopeInstances.SelectMany(instance => Condition.GetConditions(instance)))
                    {
                        List<Condition> conditions;
                        if (!conditionsByType.TryGetValue(condition.Type.Code, out conditions))
                            conditionsByType[condition.Type.Code] = conditions = new List<Condition>();

                        if (!conditions.Contains(condition))
                            conditions.Add(condition);
                    }
                    response.Conditions = conditionsByType;
                }
            }
            finally
            {
                // Raise the end request event
                ExoWeb.OnEndRequest(this, response);
            }

            // Return the response
            return response;
        }