I'm trying to find a way to wrap a a json response with a callback for jsonp. The problem I am running into though is I don't want to use the 'JsonResult' class to construct the response as it wraps it with its own object where as if I return the model directly it is properly serialized to json.
So far I have tried using the a 'ActionFilter' but couldn't find out how I could wrap the result after the action executed.
Any direction at all would be appreciated
I've been down this road, and can offer the following code which encapsulates JsonP calls into an ActionResult, adds a method to your Controller which allows you to prioritize the type of ActionResult you want, and a couple of extension methods to glue it all together. The only requirement is to consistently name your callback parameter, so it can be reliably culled from the Request.
First, the derived ActionResult:
using System;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;
namespace CL.Enterprise.Components.Web.Mvc
{
/// <summary>
/// Extension of JsonResult to handle JsonP requests
/// </summary>
public class JsonPResult : ActionResult
{
private JavaScriptSerializer _jser = new JavaScriptSerializer();
public Encoding ContentEncoding { get; set; }
public string ContentType { get; set; }
public object Data { get; set; }
public string JsonCallback { get; set; }
public JsonPResult() { }
/// <summary>
/// Package and return the result
/// </summary>
public override void ExecuteResult(ControllerContext Context)
{
//Context.IsChildAction
if (Context == null)
{
throw new ArgumentNullException("Context");
}
JsonCallback = Context.HttpContext.Request["callback"];
if (string.IsNullOrEmpty(JsonCallback))
{
JsonCallback = Context.HttpContext.Request["jsoncallback"];
}
if (string.IsNullOrEmpty(JsonCallback))
{
throw new ArgumentNullException("JsonP callback parameter required for JsonP response.");
}
HttpResponseBase CurrentResponse = Context.HttpContext.Response;
if (!String.IsNullOrEmpty(ContentType))
{
CurrentResponse.ContentType = ContentType;
}
else
{
CurrentResponse.ContentType = "application/json";
}
if (ContentEncoding != null)
{
CurrentResponse.ContentEncoding = ContentEncoding;
}
if (Data != null)
{
CurrentResponse.Write(string.Format("{0}({1});", JsonCallback, _jser.Serialize(Data)));
}
}
}
}
Next, the Controller extension methods:
using System.IO;
using System.Web.Mvc;
namespace CL.Enterprise.Components.Web.Mvc
{
/// <summary>
/// Extension methods for System.Web.Mvc.Controller
/// </summary>
public static class ContollerExtensions
{
/// <summary>
/// Method to return a JsonPResult
/// </summary>
public static JsonPResult JsonP(this Controller controller, object data)
{
JsonPResult result = new JsonPResult();
result.Data = data;
//result.ExecuteResult(controller.ControllerContext);
return result;
}
/// <summary>
/// Get the currently named jsonp QS parameter value
/// </summary>
public static string GetJsonPCallback(this Controller controller)
{
return
controller.ControllerContext.RequestContext.HttpContext.Request.QueryString["callback"] ??
controller.ControllerContext.RequestContext.HttpContext.Request.QueryString["jsoncallback"] ??
string.Empty;
}
}
}
Finally, add this method to your Controller:
/// <summary>
/// Gets an ActionResult, either as a jsonified string, or rendered as normally
/// </summary>
/// <typeparam name="TModel">Type of the Model class</typeparam>
/// <param name="UsingJson">Do you want a JsonResult?</param>
/// <param name="UsingJsonP">Do you want a JsonPResult? (takes priority over UsingJson)</param>
/// <param name="Model">The model class instance</param>
/// <param name="RelativePathToView">Where in this webapp is the view being requested?</param>
/// <returns>An ActionResult</returns>
public ActionResult GetActionResult<T>(T Model, bool UsingJsonP, bool UsingJson, string RelativePathToView)
{
string ViewAsString =
this.RenderView<T>(
RelativePathToView,
Model
);
if (UsingJsonP) //takes priority
{
string Callback = this.GetJsonPCallback();
JsonPResult Result = this.JsonP(ViewAsString.Trim());
return Result;
}
if (UsingJson)//secondary
{
return Json(ViewAsString.Trim(), JsonRequestBehavior.AllowGet);
}
return View(Model); //tertiary
}
Related
Coding in C#, I am blocked on a silly JSON deserialization and hoping your expert eyes will find what I'm missing.
I am trying to extract the following JSON file:
{
"Document":
{
"Version": "1.0.0",
"LastUpdate": "10.08.2020",
"Description": "This document enumarates manufactuer capabilities",
"Id": ""
},
"Capacities": [
{
"Id": 0,
"Description": "",
"Enable": true,
"RelativePath": "Company1\\manifest.json"
},
{
"Id": 1,
"Description": "",
"Enable": true,
"RelativePath": "Company2\\manifest.json"
}
]
}
The ManifestDescriptor class should self-extract (and store) JSON data through its constructor call. The implementation is as follow:
using JsonTools;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Serilog;
using SessionService;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace BaseService.Manifest
{
public class ManifestDescriptor : JObject
{
/// <summary>
/// Contains Json document descriptor data
/// </summary>
private DocSection _document;
private List<Capacity> _capacity;
/// <summary>
/// Contains Json document descriptor data
/// </summary>
[JsonProperty("Document")]
public DocSection Document
{
get => _document;
set
{
if (value != _document)
{
_document = value;
}
}
}
/// <summary>
/// Contains document capacities
/// </summary>
[JsonProperty("Capacities")]
public List<Capacity> Capacities
{
get => _capacity;
internal set
{
if (value != _capacity)
{
_capacity = value;
}
}
}
public ManifestDescriptor(string absolutePath)
{
string l_rawJson = string.Empty;
if (File.Exists(absolutePath))
{
_fileInfo = new FileInfo(absolutePath);
if (!JsonParser.LoadJsonFile(absolutePath, ref l_rawJson))
{
Log.Error("Could not extract Json file data.");
return;
}
// Populate current class with manifest data
JsonConvert.PopulateObject(l_rawJson, this);
}
else Log.Warning("Given manifest file do not exist: \n\r {0}", absolutePath);
}
}
}
ManifestDescriptor contains a Document property implemented as follow:
using Newtonsoft.Json;
using System;
using System.IO;
using System.Linq;
namespace BaseService.Manifest
{
/// <summary>
/// Class container giving Json document version & its utility
/// </summary>
public class DocSection
{
private Version _version;
private DateTime _lastUpdate;
private string _description;
private string _id;
private string _class;
/// <summary>
/// Contains document version
/// </summary>
[JsonProperty("Version")]
public Version Version
{
get => _version;
internal set
{
if (value != _version)
{
_version = value;
}
}
}
/// <summary>
/// Contains last update document version
/// </summary>
[JsonProperty("LastUpdate")]
public DateTime LastUpdate
{
get => _lastUpdate;
internal set
{
if(value != _lastUpdate)
{
_lastUpdate = value;
}
}
}
/// <summary>
/// Contains document description
/// </summary>
[JsonProperty("Description")]
public string Description
{
get => _description;
internal set
{
if(value != _description)
{
_description = value;
}
}
}
/// <summary>
/// Contains document ID
/// </summary>
[JsonProperty("Id")]
public string Id
{
get => _id;
internal set
{
if (value != _id)
{
_id = value;
}
}
}
/// <summary>
/// Object class type to instanciate regarding capacity data of the document
/// </summary>
[JsonIgnore]
[JsonProperty("Class")]
public string Class
{
get => _class;
internal set
{
if(value != _class)
{
_class = value;
}
}
}
}
}
However when reaching the line JsonConvert.PopulateObject(l_rawJson, this); called in ManifestDescriptor class constructor, I am reaching the following execption:
Cannot populate JSON object onto type
'BaseService.Manifest.ManifestDescriptor'. Path 'Document', line 2,
position 13."
May you have an idea of the issue?
Thanks for your attention,
As per #SelimYildiz, you really need to separate the population of objetc outside of the object you are trying to serialize.
If your intention is to serialize your ManifestDescriptor without knowing the process of it all you need is to past your json. Then you can create a wrapper class that will do the thing.
What is your intention of making the ManifestDescriptor inherit a JObject?
This might give you a hint, for this sample demo I didn't inherit the JObject and instead of JsonConvert.PopulateObject I used JsonConvert.Deserialized.
Wrapper Class
public class ParserWrapper<T>
where T : new()
{
private FileInfo _fileInfo;
public T ParsedObject { get; set; }
public ParserWrapper(string absolutePath)
{
string l_rawJson = string.Empty;
if (File.Exists(absolutePath))
{
_fileInfo = new FileInfo(absolutePath);
if (!JsonParser.LoadJsonFile(absolutePath, ref l_rawJson))
{
System.Diagnostics.Debug.Write("Could not extract Json file data.");
return;
}
// Populate current class with manifest data
ParsedObject = JsonConvert.DeserializeObject<T>(l_rawJson);
}
else System.Diagnostics.Debug.Write("Given manifest file do not exist: \n\r {0}", absolutePath);
}
}
ManifestorDescriptor
public class ManifestDescriptor
{
private DocSection _document;
private List<Capacity> _capacity;
[JsonProperty("Document")]
public DocSection Document
{
get => _document;
set
{
if (value != _document)
{
_document = value;
}
}
}
[JsonProperty("Capacities")]
public List<Capacity> Capacities
{
get => _capacity;
internal set
{
if (value != _capacity)
{
_capacity = value;
}
}
}
}
Implementation
var obj = new ParserWrapper<ManifestDescriptor>("json1.json");
Output:
Thanks for your attention. Combined comments help me to find out the issue. Coming back from #Rahul Sharma code answer and drawing back towards my original code, I could detect the two following issues:
Setter should not be declared as internal otherwise JsonConvert.PopulateObject(...) function will end with a nullable object.
I've set ManifestDescriptor class as child of Jobject. Removing the parent seems to resolve the raised exception I get.
The working code is then:
ManifestDescriptor class:
public class ManifestDescriptor
{
/// <summary>
/// Manifest file information field
/// </summary>
private FileInfo _fileInfo;
/// <summary>
/// Contains Json document descriptor data
/// </summary>
private DocSection _document;
private List<Capacity> _capacity;
/// <summary>
/// Manifest file information
/// </summary>
public FileInfo FileInfo
{
get => _fileInfo;
set
{
if(value != _fileInfo)
_fileInfo = value;
}
}
/// <summary>
/// Contains Json document descriptor data
/// </summary>
[JsonProperty("Document")]
public DocSection Document
{
get => _document;
set
{
if (value != _document)
{
_document = value;
}
}
}
/// <summary>
/// Contains document capacities
/// </summary>
[JsonProperty("Capacities")]
public List<Capacity> Capacities
{
get => _capacity;
internal set
{
if (value != _capacity)
{
_capacity = value;
}
}
}
public ManifestDescriptor(string absolutePath)
{
string l_rawJson = string.Empty;
if (File.Exists(absolutePath))
{
_fileInfo = new FileInfo(absolutePath);
if (!JsonParser.LoadJsonFile(absolutePath, ref l_rawJson))
{
Log.Error("Could not extract Json file data.");
return;
}
// Populate current class with manifest data
JsonConvert.PopulateObject(l_rawJson, this);
}
else Log.Warning("Given manifest file do not exist: \n\r {0}", absolutePath);
}
}
DocSection class (Document property):
/// <summary>
/// Class container giving Json document version & its utility
/// </summary>
public class DocSection
{
private Version _version;
private DateTime _lastUpdate;
private string _description;
private string _id;
private string _class;
/// <summary>
/// Contains document version
/// </summary>
[JsonProperty("Version")]
public Version Version
{
get => _version;
set
{
if (value != _version)
{
_version = value;
}
}
}
/// <summary>
/// Contains last update document version
/// </summary>
[JsonProperty("LastUpdate")]
public DateTime LastUpdate
{
get => _lastUpdate;
set
{
if(value != _lastUpdate)
{
_lastUpdate = value;
}
}
}
/// <summary>
/// Contains document description
/// </summary>
[JsonProperty("Description")]
public string Description
{
get => _description;
set
{
if(value != _description)
{
_description = value;
}
}
}
/// <summary>
/// Contains document ID
/// </summary>
[JsonProperty("Id")]
public string Id
{
get => _id;
internal set
{
if (value != _id)
{
_id = value;
}
}
}
}
I am looking for a way to combine x amount of very similar CRUD functions into one without having to use x amount of if else statements to check the type of a generic.
I have Web API controllers that I want to make calls from like this:
Service.Get<FooModel>(number, type, part, version);
This is to prevent having to have an extremely similar function for 40+ API endpoints. The issue is when I receive this in my service, I have to check the type of the generic given and compare with those 40+ object types in the one function. All of the models currently inherit from a base inherited model.
Current generic function
(Create, Update, Delete functions are similar):
public T Get<T>(string documentNr, string type, string part, string version) where T : InheritedModel, new()
{
try
{
T model = new T();
if (typeof(T) == typeof(InheritedModel))
{
using (var repo = new InheritedModelConsumer(ref _helper))
{
model = (T)repo.Get(documentNr, type, part, version);
}
}
else if (typeof(T) == typeof(FooModel))
{
using (var repo = new FooModelConsumer(ref _helper))
{
model = (T)(object)repo.Get(documentNr, type, part, version);
}
}
else if (typeof(T) == typeof(ComponentModel))
{
using (var repo = new ComponentModelConsumer(ref _helper))
{
model = (T)(object)repo.Get(documentNr, type, part, version);
}
}
else if (typeof(T) == typeof(BarModel))
{
using (var repo = new BarModelConsumer(ref _helper))
{
model = (T)(object)repo.Get(documentNr, type, part, version);
}
}
... and so on
... and so on
...
else
throw new Exception("Type T structure not defined");
return model;
}
catch (Exception)
{
throw;
}
finally
{
_helper.Dispose();
}
}
This does work, but if it is possible I am looking for something where I can say at run time, "oh I have this object of Type T, and well since I know the functions all have the same inputs I'm going to instantiate this consumer of Type TConsumer, call consumer.Get(inputs), and then return an object of T to whatever API controller called me."
Edit
Example of a simple consumer class in use
internal sealed class FooConsumer : RepositoryConsumer<Foo, FooRepository, FooFilter>
{
public FooConsumer(ref SqlHelper helper) : base(ref helper) { }
public List<Foo> GetAll(string token)
{
return _repo.Get().Where(x => Extensions.StringContainsToken(x.AccountName, token)).ToList();
}
}
Repository Consumer that all consumers inherit from .
T is the model, K is the Repository (custom ORM class), and O is Filter for the WHERE clause the ORM executes.
public abstract class RepositoryConsumer<T, K, O> : IDisposable, IRepositoryConsumer<T> where T : class, new() where K : Repository<T, O>, new() where O : QueryFilter, new()
{
/// <summary>
/// Repository instance
/// </summary>
protected K _repo;
/// <summary>
/// Only constructor avaialble. MUst pass SqlHelper instance for transaction support
/// </summary>
/// <param name="sql"></param>
public RepositoryConsumer(ref SqlHelper sql)
{
_repo = Activator.CreateInstance(typeof(K), new object[] { sql }) as K;
}
/// <summary>
/// Allow consumer initializations in using statements
/// </summary>
public void Dispose()
{
}
/// <summary>
/// Create instance of T
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public virtual int Create(T data)
{
return _repo.Create(data);
}
/// <summary>
/// Bulk create instances of T
/// </summary>
/// <param name="contract"></param>
/// <returns></returns>
public virtual int Create(BaseBulkable<T> contract)
{
return _repo.BulkCreate(contract);
}
/// <summary>
/// Get an instance of T based on a single PK field id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public virtual T Get(long id)
{
return _repo.Get(id);
}
/// <summary>
/// Gets all instances of T
/// </summary>
/// <returns></returns>
public virtual List<T> GetAll()
{
return _repo.Get();
}
/// <summary>
/// Updates an instance of T
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public virtual int Update(T data)
{
return _repo.Update(data);
}
/// <summary>
/// Updates an instance of T based on a single PK field id
/// </summary>
/// <param name="id"></param>
/// <param name="data"></param>
/// <returns></returns>
public virtual int Update(long id, T data)
{
return _repo.Update(id, data);
}
/// <summary>
/// Deletes an instance of T
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public virtual int Delete(T data)
{
return _repo.Delete(data);
}
/// <summary>
/// Deletes an instance of T based on a single PK field id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public virtual int Delete(long id)
{
return _repo.Delete(id);
}
}
I am implementing Web API versioning as in Web API Versioning. My controllers are in 2 separate namespaces, and I've used a custom SelectController method to choose which version to use based on a a query parameter.
e.g.
http://myapi/api/values?version=1.0
This all works fine but some actions in the controllers use the Route attribute
[Route("api/values/getNames")]
public HttpResponseMessage Get() { ... }
Which are mapped to the correct controller by default using
config.MapHttpAttributeRoutes();
in WebApiConfig.cs
This will not work if I have multiple versions of the API with the same route. Am I able to provide a custom implementation for config.MapHttpAttributeRoutes() so I can select the correct version of the API to use, or is there a better way of doing this?
There is an example for this in the official WebApi 2.1 examplex on Codeplex.
It relies on a request header value to store the version.
I think it's a lot nicer, since it allows the routes to stay the same for all versions. Clients select the version simply by including an HTTP header in the request (in this case the version number).
This sample shows how to use Attribute Routing and Constraints in
ASP.NET Web API to dynamically filter controllers by an 'api-version'
HTTP header. When a route uses Constraints, each constraint has a
chance to prevent the route from matching a given request. In this
sample, a custom RouteFactoryAttribute (VersionedRoute) adds a
constraint to each attribute route.
...
The custom constraint implementation (VersionConstraint) is
implemented based on the value of 'api-version' matching an integer
value. The value of the allowed version for the constraint is provided
by the VersionedRoute attribute placed on each controller. When a
request comes in, the header value of 'api-version' is matched against
the expected version. This example uses a header but a constraint
implementation could use any criteria to decided if the request is
valid for the route.
Anyway, the end result would end up looking like this:
[VersionedRoute("api/Customer", 1)]
public class CustomerVersion1Controller : ApiController
{
// controller code goes here
}
[VersionedRoute("api/Customer", 2)]
public class CustomerVersion2Controller : ApiController
{
// controller code goes here
}
Here is a solution that will let you use the Web API 2 way of versioned routes (headers), in addition to query parameter support (i.e. use a header called 'api-version' or a querystring parameter named '?api-version=XXX'.
The HTTP Route constraint does the work:
/// <summary>
/// Add a route constraint to detect version header or by query string
/// </summary>
public class RouteVersionHttpConstraint : IHttpRouteConstraint
{
public const string VersionHeaderName = "api-version";
private const int DefaultVersion = 1;
/// <summary>
/// Add a route constraint to detect version header or by query string
/// </summary>
/// <param name="allowedVersion"></param>
public RouteVersionHttpConstraint(int allowedVersion)
{
AllowedVersion = allowedVersion;
}
public int AllowedVersion
{
get;
private set;
}
/// <summary>
/// Perform the controller match
/// </summary>
/// <param name="request"></param>
/// <param name="route"></param>
/// <param name="parameterName"></param>
/// <param name="values"></param>
/// <param name="routeDirection"></param>
/// <returns></returns>
public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
if (routeDirection == HttpRouteDirection.UriResolution)
{
int version = GetVersionHeaderOrQuery(request) ?? DefaultVersion;
if (version == AllowedVersion)
{
return true;
}
}
return false;
}
/// <summary>
/// Check the request header, and the query string to determine if a version number has been provided
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private int? GetVersionHeaderOrQuery(HttpRequestMessage request)
{
string versionAsString;
IEnumerable<string> headerValues;
if (request.Headers.TryGetValues(VersionHeaderName, out headerValues) && headerValues.Count() == 1)
{
versionAsString = headerValues.First();
int version;
if (versionAsString != null && Int32.TryParse(versionAsString, out version))
{
return version;
}
}
else
{
var query = System.Web.HttpUtility.ParseQueryString(request.RequestUri.Query);
string versionStr = query[VersionHeaderName];
int version = 0;
int.TryParse(versionStr, out version);
if (version > 0)
return version;
}
return null;
}
}
And the route factory:
/// <summary>
/// Versioning support for the WebAPI controllers
/// </summary>
public class RouteVersionAttribute : RouteFactoryAttribute
{
public int Version { get; private set; }
public RouteVersionAttribute() : this(null, 1)
{
}
/// <summary>
/// Specify a version for the WebAPI controller
/// </summary>
/// <param name="version"></param>
public RouteVersionAttribute(int version) : this(null, version)
{
}
public RouteVersionAttribute(string template, int version)
: base(template)
{
Version = version;
}
public override IDictionary<string, object> Constraints
{
get
{
var constraints = new HttpRouteValueDictionary();
constraints.Add("version", new RouteVersionHttpConstraint(Version));
return constraints;
}
}
public override IDictionary<string, object> Defaults
{
get
{
var defaults = new HttpRouteValueDictionary();
defaults.Add("version", 1);
return defaults;
}
}
}
Usage:
[RouteVersion("api/versiontest", 1)]
public class Version1TestController : BaseApiController
{
// get: api/versiontest
[HttpGet]
public HttpResponseMessage get()
{
return Request.CreateResponse(HttpStatusCode.OK, new { Version = "API Version 1 selected" });
}
}
[RouteVersion("api/versiontest", 2)]
public class Version2TestController : ApiController
{
// get: api/versiontest
[HttpGet]
public HttpResponseMessage get()
{
return Request.CreateResponse(HttpStatusCode.OK, new { Version = "API Version 2 selected" });
}
}
I extended Michael Brown's answer to allow setting a default version:
Only now I'm thinking how to make it work with Swashbuckle swagger.
RouteVersionAttribute:
using System.Collections.Generic;
using System.Web.Http.Routing;
namespace YourNameSpace.Filters
{
/// <summary>
/// Here is a solution that will let you use the Web API 2 way of versioned routes (headers),
/// in addition to query parameter support (i.e.use a header called 'api-version' or
/// a querystring parameter named '?api-version=XXX'.
/// <para>https://stackoverflow.com/a/28934352/3187389</para>
/// <para>https://stackoverflow.com/questions/25299889/customize-maphttpattributeroutes-for-web-api-versioning</para>
/// </summary>
public class RouteVersionAttribute : RouteFactoryAttribute
{
public int Version { get; private set; }
public int VersionDefault { get; private set; }
public RouteVersionAttribute() : this(null, 1, true)
{
}
/// <summary>
/// Specify a version for the WebAPI controller or an action method
/// for example: [RouteVersion("Test", 1)] or [RouteVersion("Test", 1, true)]
/// </summary>
/// <param name="version"></param>
/// <param name="isDefault"></param>
public RouteVersionAttribute(int version, bool isDefault = false) : this(null, version, isDefault)
{
}
/// <summary>
/// Specify a version for the WebAPI controller or an action method
/// for example: [RouteVersion("Test", 1)] or [RouteVersion("Test", 1, true)]
/// </summary>
/// <param name="template"></param>
/// <param name="version"></param>
/// <param name="isDefault"></param>
public RouteVersionAttribute(string template, int version, bool isDefault = false)
: base(template)
{
Version = version;
if (isDefault)
VersionDefault = version;
}
public override IDictionary<string, object> Constraints
{
get
{
var constraints = new HttpRouteValueDictionary();
constraints.Add("version", new RouteVersionHttpConstraint(Version, VersionDefault));
return constraints;
}
}
public override IDictionary<string, object> Defaults
{
get
{
var defaults = new HttpRouteValueDictionary();
defaults.Add("version", VersionDefault);
return defaults;
}
}
}
}
RouteVersionHttpConstraint:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http.Routing;
namespace Boyd.Core.Filters
{
/// <summary>
/// Here is a solution that will let you use the Web API 2 way of versioned routes (headers),
/// in addition to query parameter support (i.e.use a header called 'api-version' or
/// a querystring parameter named '?api-version=XXX'.
/// <para>https://stackoverflow.com/a/28934352/3187389</para>
/// <para>https://stackoverflow.com/questions/25299889/customize-maphttpattributeroutes-for-web-api-versioning</para>
/// </summary>
public class RouteVersionHttpConstraint : IHttpRouteConstraint
{
public const string VersionHeaderName = "api-version";
private readonly int VersionDefault = 1;
/// <summary>
/// Add a route constraint to detect version header or by query string
/// </summary>
/// <param name="allowedVersion"></param>
public RouteVersionHttpConstraint(int allowedVersion, int versionDefault)
{
AllowedVersion = allowedVersion;
VersionDefault = versionDefault;
}
public int AllowedVersion
{
get;
private set;
}
/// <summary>
/// Perform the controller match
/// </summary>
/// <param name="request"></param>
/// <param name="route"></param>
/// <param name="parameterName"></param>
/// <param name="values"></param>
/// <param name="routeDirection"></param>
/// <returns></returns>
public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
if (routeDirection == HttpRouteDirection.UriResolution)
{
int version = GetVersionHeaderOrQuery(request) ?? VersionDefault;
if (version == AllowedVersion)
{
return true;
}
}
return false;
}
/// <summary>
/// Check the request header, and the query string to determine if a version number has been provided
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private int? GetVersionHeaderOrQuery(HttpRequestMessage request)
{
string versionAsString;
if (request.Headers.TryGetValues(VersionHeaderName, out IEnumerable<string> headerValues)
&& headerValues.Count() == 1)
{
versionAsString = headerValues.First();
if (versionAsString != null && Int32.TryParse(versionAsString, out int version))
{
return version;
}
}
else
{
var query = System.Web.HttpUtility.ParseQueryString(request.RequestUri.Query);
string versionStr = query[VersionHeaderName];
int.TryParse(versionStr, out int version);
if (version > 0)
return version;
}
return null;
}
}
}
Usage (can be used on the controller or action methods):
#region Temporary Tests
// {{BaseUrl}}Test?api-version=1
[HttpGet]
[RouteVersion("Test", 1)]
public async Task<IHttpActionResult> Test1([FromBody]GetCustomerW2GsForPropertyRequest request)
{
return await Task.FromResult(Ok("API Version 1 selected"));
}
[HttpGet]
[RouteVersion("Test", 2)]
[RouteVersion("Test", 3)]
[RouteVersion("Test", 4)]
public async Task<IHttpActionResult> Test4([FromBody]GetCustomerW2GsForPropertyRequest request)
{
return await Task.FromResult(Ok("API Version 2, 3 or 4 selected"));
}
[HttpGet]
[RouteVersion("Test", 5, true)]
public async Task<IHttpActionResult> Test5([FromBody]GetCustomerW2GsForPropertyRequest request)
{
return await Task.FromResult(Ok("API Version 5 selected"));
}
#endregion Temporary Tests
I have an ASP.NET MVC project in which the model is managed through .NET entities and it seems that some times it loses the connection, but this happens only on stored procedures.
I get the following error:
Execution of the command requires an open and available connection. The connection's current state is broken.
Why is this happening?
Code
public ObjectResult<Categories> GetCategoriesStructure() {
return ObjectContext.getCategoriesStructure();
}
var catss = GetCategoriesStructure().ToList();
this exception occurs when I am trying to assign the List to catss variable
Object Context Instantiation
public abstract class ObjectContextManager {
/// <summary>
/// Returns a reference to an ObjectContext instance.
/// </summary>
public abstract TObjectContext GetObjectContext<TObjectContext>()
where TObjectContext : ObjectContext, new();
}
public abstract class BaseDAO<TObjectContext, TEntity> : IBaseDAO<TObjectContext, TEntity>
where TObjectContext : System.Data.Objects.ObjectContext, new()
where TEntity : System.Data.Objects.DataClasses.EntityObject {
private ObjectContextManager _objectContextManager;
/// <summary>
/// Returns the current ObjectContextManager instance. Encapsulated the
/// _objectContextManager field to show it as an association on the class diagram.
/// </summary>
private ObjectContextManager ObjectContextManager {
get { return _objectContextManager; }
set { _objectContextManager = value; }
}
/// <summary>
/// Returns an ObjectContext object.
/// </summary>
protected internal TObjectContext ObjectContext {
get {
if (ObjectContextManager == null)
this.InstantiateObjectContextManager();
return ObjectContextManager.GetObjectContext<TObjectContext>();
}
}
/// <summary>
/// Default constructor.
/// </summary>
public BaseDAO() { }
/// <summary>
/// Instantiates a new ObjectContextManager based on application configuration settings.
/// </summary>
private void InstantiateObjectContextManager() {
/* Retrieve ObjectContextManager configuration settings: */
Hashtable ocManagerConfiguration = ConfigurationManager.GetSection("ObjectContextManagement.ObjectContext") as Hashtable;
if (ocManagerConfiguration != null && ocManagerConfiguration.ContainsKey("managerType")) {
string managerTypeName = ocManagerConfiguration["managerType"] as string;
if (string.IsNullOrEmpty(managerTypeName))
throw new ConfigurationErrorsException("The managerType attribute is empty.");
else
managerTypeName = managerTypeName.Trim().ToLower();
try {
/* Try to create a type based on it's name: */
Assembly frameworkAssembly = Assembly.GetAssembly(typeof(ObjectContextManager));
Type managerType = frameworkAssembly.GetType(managerTypeName, true, true);
/* Try to create a new instance of the specified ObjectContextManager type: */
this.ObjectContextManager = Activator.CreateInstance(managerType) as ObjectContextManager;
} catch (Exception e) {
throw new ConfigurationErrorsException("The managerType specified in the configuration is not valid.", e);
}
} else
throw new ConfigurationErrorsException("ObjectContext tag or its managerType attribute is missing in the configuration.");
}
/// <summary>
/// Persists all changes to the underlying datastore.
/// </summary>
public void SaveAllObjectChanges() {
this.ObjectContext.SaveChanges();
}
/// <summary>
/// Adds a new entity object to the context.
/// </summary>
/// <param name="newObject">A new object.</param>
public virtual void Add(TEntity newObject) {
this.ObjectContext.AddObject(newObject.GetType().Name, newObject);
}
/// <summary>
/// Deletes an entity object.
/// </summary>
/// <param name="obsoleteObject">An obsolete object.</param>
public virtual void Delete(TEntity obsoleteObject) {
this.ObjectContext.DeleteObject(obsoleteObject);
}
public void Detach(TEntity obsoleteObject) {
this.ObjectContext.Detach(obsoleteObject);
}
/// <summary>
/// Updates the changed entity object to the context.
/// </summary>
/// <param name="newObject">A new object.</param>
public virtual void Update(TEntity newObject) {
ObjectContext.ApplyPropertyChanges(newObject.GetType().Name, newObject);
ObjectContext.Refresh(RefreshMode.ClientWins, newObject);
}
public virtual TEntity LoadByKey(String propertyName, Object keyValue) {
IEnumerable<KeyValuePair<string, object>> entityKeyValues =
new KeyValuePair<string, object>[] {
new KeyValuePair<string, object>(propertyName, keyValue) };
// Create the key for a specific SalesOrderHeader object.
EntityKey key = new EntityKey(this.ObjectContext.GetType().Name + "." + typeof(TEntity).Name, entityKeyValues);
return (TEntity)this.ObjectContext.GetObjectByKey(key);
}
#region IBaseDAO<TObjectContext,TEntity> Members
public bool validation(TEntity newObject) {
return newObject.GetType().Name.ToString() == "Int32";
}
#endregion
}
Without knowing how you are instantiating your ObjectContext, I'll throw something in the answer bucket here.
This is how I do my Entity Framework commands and connections (for small simple projects at least):
using (MyEntities context = new MyEntities())
{
return context.getCategoriesStructure();
}
You can also optionally pass in a connection string when instantiating your context (if not, it will use the one in your app.config):
new MyEntities("...connection string...")
If this does not help your issue, please help us understand your code a little better by posting how you are creating your ObjectContext. You could at least attempt to do it this way to see if it works; that will tell you whether it is an issue with your connection string or not.
I'm building an app in .NET and C#, and I'd like to cache some of the results by using attributes/annotations instead of explicit code in the method.
I'd like a method signature that looks a bit like this:
[Cache, timeToLive=60]
String getName(string id, string location)
It should make a hash based on the inputs, and use that as the key for the result.
Naturally, there'd be some config file telling it how to actually put in memcached, local dictionary or something.
Do you know of such a framework?
I'd even be interested in one for Java as well
With CacheHandler in Microsoft Enterprise Library you can easily achieve this.
For instance:
[CacheHandler(0, 30, 0)]
public Object GetData(Object input)
{
}
would make all calls to that method cached for 30 minutes. All invocations gets a unique cache-key based on the input data and method name so if you call the method twice with different input it doesn't get cached but if you call it >1 times within the timout interval with the same input then the method only gets executed once.
I've added some extra features to Microsoft's code:
My modified version looks like this:
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.UI;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.Unity.InterceptionExtension;
namespace Middleware.Cache
{
/// <summary>
/// An <see cref="ICallHandler"/> that implements caching of the return values of
/// methods. This handler stores the return value in the ASP.NET cache or the Items object of the current request.
/// </summary>
[ConfigurationElementType(typeof (CacheHandler)), Synchronization]
public class CacheHandler : ICallHandler
{
/// <summary>
/// The default expiration time for the cached entries: 5 minutes
/// </summary>
public static readonly TimeSpan DefaultExpirationTime = new TimeSpan(0, 5, 0);
private readonly object cachedData;
private readonly DefaultCacheKeyGenerator keyGenerator;
private readonly bool storeOnlyForThisRequest = true;
private TimeSpan expirationTime;
private GetNextHandlerDelegate getNext;
private IMethodInvocation input;
public CacheHandler(TimeSpan expirationTime, bool storeOnlyForThisRequest)
{
keyGenerator = new DefaultCacheKeyGenerator();
this.expirationTime = expirationTime;
this.storeOnlyForThisRequest = storeOnlyForThisRequest;
}
/// <summary>
/// This constructor is used when we wrap cached data in a CacheHandler so that
/// we can reload the object after it has been removed from the cache.
/// </summary>
/// <param name="expirationTime"></param>
/// <param name="storeOnlyForThisRequest"></param>
/// <param name="input"></param>
/// <param name="getNext"></param>
/// <param name="cachedData"></param>
public CacheHandler(TimeSpan expirationTime, bool storeOnlyForThisRequest,
IMethodInvocation input, GetNextHandlerDelegate getNext,
object cachedData)
: this(expirationTime, storeOnlyForThisRequest)
{
this.input = input;
this.getNext = getNext;
this.cachedData = cachedData;
}
/// <summary>
/// Gets or sets the expiration time for cache data.
/// </summary>
/// <value>The expiration time.</value>
public TimeSpan ExpirationTime
{
get { return expirationTime; }
set { expirationTime = value; }
}
#region ICallHandler Members
/// <summary>
/// Implements the caching behavior of this handler.
/// </summary>
/// <param name="input"><see cref="IMethodInvocation"/> object describing the current call.</param>
/// <param name="getNext">delegate used to get the next handler in the current pipeline.</param>
/// <returns>Return value from target method, or cached result if previous inputs have been seen.</returns>
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
lock (input.MethodBase)
{
this.input = input;
this.getNext = getNext;
return loadUsingCache();
}
}
public int Order
{
get { return 0; }
set { }
}
#endregion
private IMethodReturn loadUsingCache()
{
//We need to synchronize calls to the CacheHandler on method level
//to prevent duplicate calls to methods that could be cached.
lock (input.MethodBase)
{
if (TargetMethodReturnsVoid(input) || HttpContext.Current == null)
{
return getNext()(input, getNext);
}
var inputs = new object[input.Inputs.Count];
for (int i = 0; i < inputs.Length; ++i)
{
inputs[i] = input.Inputs[i];
}
string cacheKey = keyGenerator.CreateCacheKey(input.MethodBase, inputs);
object cachedResult = getCachedResult(cacheKey);
if (cachedResult == null)
{
var stopWatch = Stopwatch.StartNew();
var realReturn = getNext()(input, getNext);
stopWatch.Stop();
if (realReturn.Exception == null && realReturn.ReturnValue != null)
{
AddToCache(cacheKey, realReturn.ReturnValue);
}
return realReturn;
}
var cachedReturn = input.CreateMethodReturn(cachedResult, input.Arguments);
return cachedReturn;
}
}
private object getCachedResult(string cacheKey)
{
//When the method uses input that is not serializable
//we cannot create a cache key and can therefore not
//cache the data.
if (cacheKey == null)
{
return null;
}
object cachedValue = !storeOnlyForThisRequest ? HttpRuntime.Cache.Get(cacheKey) : HttpContext.Current.Items[cacheKey];
var cachedValueCast = cachedValue as CacheHandler;
if (cachedValueCast != null)
{
//This is an object that is reloaded when it is being removed.
//It is therefore wrapped in a CacheHandler-object and we must
//unwrap it before returning it.
return cachedValueCast.cachedData;
}
return cachedValue;
}
private static bool TargetMethodReturnsVoid(IMethodInvocation input)
{
var targetMethod = input.MethodBase as MethodInfo;
return targetMethod != null && targetMethod.ReturnType == typeof (void);
}
private void AddToCache(string key, object valueToCache)
{
if (key == null)
{
//When the method uses input that is not serializable
//we cannot create a cache key and can therefore not
//cache the data.
return;
}
if (!storeOnlyForThisRequest)
{
HttpRuntime.Cache.Insert(
key,
valueToCache,
null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
expirationTime,
CacheItemPriority.Normal, null);
}
else
{
HttpContext.Current.Items[key] = valueToCache;
}
}
}
/// <summary>
/// This interface describes classes that can be used to generate cache key strings
/// for the <see cref="CacheHandler"/>.
/// </summary>
public interface ICacheKeyGenerator
{
/// <summary>
/// Creates a cache key for the given method and set of input arguments.
/// </summary>
/// <param name="method">Method being called.</param>
/// <param name="inputs">Input arguments.</param>
/// <returns>A (hopefully) unique string to be used as a cache key.</returns>
string CreateCacheKey(MethodBase method, object[] inputs);
}
/// <summary>
/// The default <see cref="ICacheKeyGenerator"/> used by the <see cref="CacheHandler"/>.
/// </summary>
public class DefaultCacheKeyGenerator : ICacheKeyGenerator
{
private readonly LosFormatter serializer = new LosFormatter(false, "");
#region ICacheKeyGenerator Members
/// <summary>
/// Create a cache key for the given method and set of input arguments.
/// </summary>
/// <param name="method">Method being called.</param>
/// <param name="inputs">Input arguments.</param>
/// <returns>A (hopefully) unique string to be used as a cache key.</returns>
public string CreateCacheKey(MethodBase method, params object[] inputs)
{
try
{
var sb = new StringBuilder();
if (method.DeclaringType != null)
{
sb.Append(method.DeclaringType.FullName);
}
sb.Append(':');
sb.Append(method.Name);
TextWriter writer = new StringWriter(sb);
if (inputs != null)
{
foreach (var input in inputs)
{
sb.Append(':');
if (input != null)
{
//Diffrerent instances of DateTime which represents the same value
//sometimes serialize differently due to some internal variables which are different.
//We therefore serialize it using Ticks instead. instead.
var inputDateTime = input as DateTime?;
if (inputDateTime.HasValue)
{
sb.Append(inputDateTime.Value.Ticks);
}
else
{
//Serialize the input and write it to the key StringBuilder.
serializer.Serialize(writer, input);
}
}
}
}
return sb.ToString();
}
catch
{
//Something went wrong when generating the key (probably an input-value was not serializble.
//Return a null key.
return null;
}
}
#endregion
}
}
Microsoft deserves most credit for this code. We've only added stuff like caching at request level instead of across requests (more useful than you might think) and fixed some bugs (e.g. equal DateTime-objects serializing to different values).
To do exactly what you are describing, i.e. writing
public class MyClass {
[Cache, timeToLive=60]
string getName(string id, string location){
return ExpensiveCall(id, location);
}
}
// ...
MyClass c = new MyClass();
string name = c.getName("id", "location");
string name_again = c.getName("id", "location");
and having only one invocation of the expensive call and without needing to wrap the class with some other code (f.x. CacheHandler<MyClass> c = new CacheHandler<MyClass>(new MyClass());) you need to look into an Aspect Oriented Programming framework. Those usually work by rewriting the byte-code, so you need to add another step to your compilation process - but you gain a lot of power in the process. There are many AOP-frameworks, but PostSharp for .NET and AspectJ are among the most popular. You can easily Google how to use those to add the caching-aspect you want.