/// <summary> /// Gets the set of property values to be serialized for the current instance /// </summary> /// <returns></returns> IEnumerator <object> IEnumerable <object> .GetEnumerator() { return(Instance.Type.Properties .Where(property => ExoWeb.IncludeInClientModel(property) && !property.IsStatic) .Select(property => property.IsList && !HasList(property) ? "?" : JsonConverter.GetPropertyValue(property, Instance)) .GetEnumerator()); }
/// <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(); } }
/// <summary> /// Creates a query to load an instance and a set of options paths. /// </summary> /// <param name="instance"></param> /// <param name="scope"></param> /// <param name="paths"></param> /// <returns></returns> public static ServiceRequest.Query Query(object instance, ViewScope scope, params string[] paths) { ModelType type; ModelInstance[] roots; bool isList; if (ExoWeb.TryConvertQueryInstance(instance, out type, out roots, out isList)) { return(new ServiceRequest.Query(type, roots, scope == ViewScope.InScope, isList, paths)); } else { return(null); } }
public static string ProcessRequest(string json) { return(JsonUtility.Serialize(ExoWeb.FromJson <ServiceRequest>(json).Invoke(null))); }
/// <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); }
void IJsonSerializable.Serialize(JsonWriter writer) { // Types if (Types != null && Types.Any()) { writer.WritePropertyName("types"); writer.WriteStartObject(); foreach (var type in Types) { writer.WritePropertyName(type); writer.WriteRawValue(GetTypeJson(type)); } writer.WriteEndObject(); } if (Instances != null && Instances.Any()) { writer.WritePropertyName("instances"); writer.WriteStartObject(); foreach (var typeItem in Instances) { writer.WritePropertyName(typeItem.Key); writer.WriteStartObject(); // Serialize static property values if (typeItem.Value.StaticProperties.Count > 0) { writer.WritePropertyName("static"); writer.Serialize( typeItem.Value.StaticProperties.ToDictionary( property => property.Name, property => JsonConverter.GetPropertyValue(property, property.DeclaringType))); } // Serialize instances foreach (var instanceItem in typeItem.Value.Instances) { writer.WritePropertyName(instanceItem.Key); writer.Serialize(instanceItem.Value); } writer.WriteEndObject(); } writer.WriteEndObject(); } if (Conditions != null && Conditions.Any()) { writer.WritePropertyName("conditions"); writer.Serialize(Conditions); } if (Events != null && Events.Any()) { writer.WritePropertyName("events"); writer.Serialize(Events); } if (Model != null && Model.Any()) { writer.WritePropertyName("model"); writer.Serialize(Model); } if (ServerInfo != null) { writer.WritePropertyName("serverInfo"); writer.Serialize(ServerInfo); } if (Changes != null && Changes.Any()) { writer.WritePropertyName("changes"); writer.Serialize(Changes.Where(modelEvent => !(modelEvent is ModelValueChangeEvent) || ExoWeb.IncludeInClientModel(((ModelValueChangeEvent)modelEvent).Property))); } }
/// <summary> /// Processes incoming requests and routes them to the appropriate JSON handler method. /// </summary> /// <param name="context"></param> void IHttpHandler.ProcessRequest(HttpContext context) { IsExecuting = true; try { // Perform the requested operation switch (context.Request.PathInfo) { case "/GetType": // Enable response caching context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.SetExpires(DateTime.Now.AddDays(7)); context.Response.Cache.SetMaxAge(TimeSpan.FromDays(7)); // Output the type metadata context.Response.ContentType = "application/json"; JsonUtility.Serialize(context.Response.OutputStream, new ServiceRequest(context.Request.QueryString["type"].Replace("\"", "")).Invoke(null)); break; case "/LogError": // Raise the error event context.Response.ContentType = "application/json"; ExoWeb.OnError(JsonUtility.Deserialize <ServiceError>(context.Request.InputStream)); break; default: // Deserialize the request context.Response.Cache.SetCacheability(HttpCacheability.NoCache); context.Response.Cache.SetNoStore(); ServiceRequest request = JsonUtility.Deserialize <ServiceRequest>(context.Request.InputStream); // Invoke the request and output the response context.Response.ContentType = "application/json"; using (var test = new StringWriter()) { JsonUtility.Serialize(test, request.Invoke(null)); context.Response.Write(test.ToString()); } break; } } catch (Exception e) { // look for an ExoModel.ModelException that may display a more // abstracted or informative message in the UI. Otherwise, use // the inner most exception Exception reported = e; for (Exception x = e; x != null; x = x.InnerException) { reported = x; if (x is ModelException) { break; } } // Create an error to log var error = new ServiceError(); error.Type = reported.GetType().FullName; error.StackTrace = GetFullStackTrace(e); error.Message = reported.Message; error.Url = context.Request.RawUrl; if (error.AdditionalInfo == null) { error.AdditionalInfo = new Dictionary <string, object>(); } //error.AdditionalInfo.Add("Client.RequestJson", json); // Raise the error event ExoWeb.OnError(error); // Also send the error information to the client context.Response.Clear(); context.Response.ContentType = "application/json"; context.Response.StatusCode = 500; // internal server error // Enable error information on client if (ExoWeb.EnableExceptionInformation) { context.Response.AddHeader("jsonerror", "true"); // Ensure IIS 7 doesn't intercept the error context.Response.TrySkipIisCustomErrors = true; } context.Response.Write(ExoWeb.ToJson(error)); } }