I am writing an asp.net core web application.
I have a number of classes that share the same Json serialization options. I would like to factor in these options in a single location
My approach is to create a static class that provide all these options for different aspects of teh application.
I would like to know if I can create a static member for the options or if I need to create a new instance each time is is required, keeping in mind that several concurrent threads will be using the instance returned.
In other words, can I write:
public static class JsonOptionsProviders
{
public static readonly JsonSerializerOptions ForPreferences = new() { Converters = { new DateOnlyJsonConverter(), new TimeOnlyJsonConverter() } };
}
or do I have to write:
public static class JsonOptionsProviders
{
public static JsonSerializerOptions ForPreferences => new() { Converters = { new DateOnlyJsonConverter(), new TimeOnlyJsonConverter() } };
}
JsonSerializerOptions isn't a simple data class but it's designed to be reused. From the Reuse JsonSerializerOptions instances section of How to instantiate JsonSerializerOptions instances with System.Text.Json
If you use JsonSerializerOptions repeatedly with the same options, don't create a new JsonSerializerOptions instance each time you use it. Reuse the same instance for every call. This guidance applies to code you write for custom converters and when you call JsonSerializer.Serialize or JsonSerializer.Deserialize. It's safe to use the same instance across multiple threads. The metadata caches on the options instance are thread-safe, and the instance is immutable after the first serialization or deserialization.
The performance difference in the article's example is 190ms for a reused instance vs 40140 for a new instance every time.
The JsonSerializerOptions class is used to cache metadata for the serialized types.
The serializer undergoes a warm-up phase during the first serialization of each type in the object graph when a new options instance is passed to it. This warm-up includes creating a cache of metadata that is needed for serialization. The metadata includes delegates to property getters, setters, constructor arguments, specified attributes, and so forth. This metadata cache is stored in the options instance. The same warm-up process and cache applies to deserialization.
Related
The title more or less says it all.
I am trying to configure the JSON MediaTypeFormatter to behave differently per route.
Specifically I have two routes in my WebAPI that are mapped to the same controller.
Each route performs the same operation and returns the same data but for reasons of backwards comparability with existing consumers they must format their output slightly differently.
I could put some code in the Controller to determine if the request came in on the legacy route or the new route and change the formatters accordingly.
I could also use an ActionFilter to change the formatters where required.
I was however wondering if there is a way to configure formatters at a per route level because that is the level of abstraction where my API behaves differently. This could either be at the point of Route Configuration or in a Delegate Handler.
Any suggestions?
I'm not entirely sure how much different your two JSONs are and what exactly you are doing with them, but if you ask me, I'd do it in the formatter:
public class MyJsonMediaTypeFormatter : JsonMediaTypeFormatter
{
private IHttpRouteData _route;
public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, System.Net.Http.Headers.MediaTypeHeaderValue mediaType)
{
_route = request.GetRouteData();
return base.GetPerRequestFormatterInstance(type, request, mediaType);
}
public override System.Threading.Tasks.Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, HttpContent content, TransportContext transportContext)
{
if (_route.Route.RouteTemplate.Contains("legacy"))
{
//here set the SerializerSettings for non standard JSON
//I just added NullValueHandling as an example
this.SerializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
};
}
return base.WriteToStreamAsync(type, value, writeStream, content, transportContext);
}
}
You would then replace the default JsonMEdiaTypeFormatter with this one.
config.Formatters.RemoveAt(0);
config.Formatters.Insert(0, new MyJsonMediaTypeFormatter());
In Web API you can have DelegatingHandler that only runs on a specific route, but that doesn't really make sense since Formatters collection is global so there is no point in modifying that in runtime even from a route-scoped handler.
Our Asp.net web application is using LINQ-to-SQL (Stored Procs are dragged on dropped on dbml file to create classes) and 3 tier architecture is similar to the one below. I have just created rough methods to give reader proper idea so that he can answer well.
namespace MyDataLayer
{
public class MyDataAccess
{
// global instance of datacontext
MyDataModelDataContext myDB = new MyDataModelDataContext(); (#1)
public void GetUserIDByUsername(string sUserName, ref int iUserID)
{
int? iUserIDout = 0;
// this will make call to SP in SQL DB
myDB.USP_RP_GETUSERIDBYUSERNAME(sUserName, "", ref iUserIDout);
iUserID = (int)iUserIDout;
}
public List<USP_APP_USERDETAILSResult> GetUserDetails(string sUserIDs)
{
// this will make call to SP in SQL DB
return myDB.USP_APP_USERDETAILS(sUserIDs).ToList();
}
...
... // several CRUD methods
}
}
namespace MyBusinessLayer
{
public class SiteUser
{
// global DataAccess instance
MyDataLayer.MyDataAccess myDA = new MyDataAccess(); (#2)
public void GetUserIDByUsername(string sUserName, ref int iUserID)
{
myDA.GetUserIDByUsername(sUserName, ref iUserID);
}
public List<USP_APP_USERDETAILSResult> GetUserDetails(string sUserIDs)
{
// this will make call to SP in SQL DB
return myDA.GetUserDetails(sUserIDs);
}
...
... // several CRUD methods
}
}
namespace MyWebApplication
{
public class BaseWebPage : System.Web.UI.Page
{
// static business layer instance
public static MyBusinessLayer.SiteUser UserBLInstance = new SiteUser(); (#3)
...
}
}
// Index.aspx.cs code fragment
namespace MyWebApplication
{
public class Index : BaseWebPage
{
public void PopulateUserDropDown()
{
// using static business layer instance declared in BaseWebPage
List<USP_APP_USERDETAILSResult> listUsers = UserBLInstance.GetUserDetails("1,2,3");
// do databinding and so on ...
}
...
}
}
Questions
(Ref.#1) Is having global datacontext in DataAccess good approach? yes/no why?
If your suggestion is having datacontext per request what is the best practice for that
(Ref.#2) Is having global DataAccess instance in BusinessLayer good approach? yes/no why?
If your suggestion is having DataAccess instance per request what is the best practice for that
(Ref. #3) Is static business layer instance declared in BaseWebPage good approach? yes/no why?
Best approach to manage life time of BL instance and DL instance in general
We are facing periodic InvalidCastException on production server for a very simple method which works fine if I restart my application from IIS. When this problem is there we can access the same database from SQL Management Studio and can execute same SP
Our prime suspect about this issue is poor DataContext management and I have read many articles on net about managing life time of DataContext but I am now confused about various approach.
That's why I have elaborated my questions so that many in same situation can get clear idea about problem/answer.
(Ref.#1) Is having global datacontext in DataAccess good approach? yes/no why?
Yes.
However, creating it manually inside the dataaccess class means that you can't control the lifetime of the datacontext. Instead, make it then a constructor parameter so that it is injected into the data access
(Ref.#2) Is having global DataAccess instance in BusinessLayer good approach? yes/no why?
Yes. But refer to 1. - make it injectable via the constructor.
(Ref. #3) Is static business layer instance declared in BaseWebPage good approach? yes/no why?
No. Avoid static for complex objects as usually such objects has non-trivial state. And this is when a lot of nasty issues can happen if you share such objects in a concurrent environment.
To summarize.
public class DataAccess {
public DataAccess( DataContext context ) { ... }
}
public class BusinessLayer {
public BusinessLayer( DataAccess access ) { ... }
}
public class MyPage : Page {
...
var ctx = TheDataContext.Current;
var bl = new BusinessLayer( new DataAccess( ctx ) );
}
with data context shared in a request scope:
public partial class TheDataContext {
// Allow the datacontext to be shared in a request-scope
public static TheDataContext Current {
get {
if ( HttpContext.Current.Items["context"] == null )
HttpContext.Current.Items.Add( "context", new TheDataContext() );
return (TheDataContext)HttpContext.Current.Items["context"];
}
}
}
In your sample - your MyDataLayer usually has name Repository. Definitely it is good to have DataContext instance in Repositories and do not try to use them outside. So, only in repositories you will have dependency on Linq-To-Sql, which means that you can create Stub objects for these Repositories and really easy test other parts of your application.
Definitely you should Dispose your Data Context instances, DataContext contains too many objects to keep them alive and let GC to kill them. As you can see you don't create any transaction objects when you are working with DataContextes, so I think that LinqToSql based on idea that you should have everything per transaction (of course you can also try to handle transaction manually, but do you really want to do this?). Disposing datacontextes in methods of Repository is a good approach, because this will not allow you to use cool feature of all ORM frameworks: Lazy Load. If you will try to use Lazy Load - you will like it, but usually it is just one of possible performance degradation cause.
Definitely your should use DataContextes for shorter or the same time of Request, don't try to use LongSession (it is when you trying to keep DataContext for more than one Http Request, it is just pain in ass, nothing else, if you want to read about this, just try to read couple articles about Long Running Session in Hibernate, I tried with nHibernate - don't do this at home ;) ).
How to pass global variable to a referenced assembly?
I am modifying an asp.net app. It is required to log all Employee (the current user of the website) actions like saving a new customer or update invoice data. The UI layer is calling a referenced assembly BLL.dll.
I want to pass current Emplyee to the referenced assembly. The passed Employee should be shared accross all static methods in that dll. It should be thread safe because the Employee can be changed accross requests.
I can't expose static field in the BLL because the Employee is stored in session state.
I need something not static, Global, accessible by both assemblies (UI layer and BLL.dll), and thread safe.
I am thinking about using some variable stored in current thread object. but I don't know what exactly I should do??
Any workarrounds ??
Thanks
Basically you need something in your BLL that can get the reference. You can use a strategy pattern with an interface.
// IN BLL.dll
public interface IEmployeeContextImplementation
{
Employee Current { get; }
}
public static EmployeeContext
{
private static readonly object ImplementationLock = new object();
private static IEmployeeContextImplementation Implementation;
public static void SetImplementation(IEmployeeContextImplementation impl)
{
lock(ImplementationLock)
{
Implementation = impl;
}
}
public static Employee Current { get { return Implementation.Current; }
}
Then in your web app, implement IEmployeeContextImplementation with the session state and call SetImplementation only once in application start.
However, Session state is only good enough for within the context of a request. If you need it to go on a different thread, you will have to explicitly pass it to a different thread.
How do I create a globally accessible Context object similar to the HttpContext object?
I want to create a custom class library which I want to reference from a website project. In the website project I want to be able to call the following globally:
ClassLibraryName.Context
I cannot create a global property directly in my classlibrary, so how should this be implemented? (I've seen other applications/products use this approach, one of which is Sitecore which has a custom Sitecore.Context object available)
Edit
Might this be a 'valid' solution?
namespace MyLibrary
{
public class Context
{
public static object ContextualObject
{
get;
set;
}
}
}
Yes, this is not hard to implement, if you always run this class in the context of an ASP.NET application, use this approach:
namespace MyLibrary
{
public class Context
{
public static object ContextualObject
{
get
{
var ctx = System.Web.HttpContext.Current.Items[typeof(Context)];
if (ctx == null)
{
ctx = new Context();
System.Web.HttpContext.Current.Items.Add(typeof(Context), ctx);
}
return ctx;
}
set { System.Web.HttpContext.Current.Items[typeof(Context)] = ctx; }
}
}
}
Essentially wrapping the existing HTTP context to provide your own service. This approach also doesn't store the object while the app lives, it only creates it for the current context, and when that response ends, it will die, and be regenerated during the next lifecycle. If that is not OK, store a static reference to context.
I've used this approach similarly in a class library I have at http://nucleo.codeplex.com, it works well.
HTH.
It depends on the lifetime you want the Context object to have. If you want all clients to use the same context, you can go with a singleton implementation.
If you want the context to be unique for each thread or http request you have to use a per request/thread implementation. One way to implement a per http request implementation would be to have a HttpModule create the object at every BeginRequest event and stick it in the HttpContext Items collection.
public static object ContextualObject
{
get { return HttpContext.Current.Items["MyContext"];}
}
You could create an instance of the object on Session_Start in the Global.asax.
I have been wondering, when to use static functions, and when not to in ASP.NET?
What are the advantages and disadvantages in using them, in various aspects like performance, following good practices etc (and many more, whichever you feel is relevant).
Cons:
threading issues (static functions don't require an instance to be called on, so it is easy to invoke them from different parts of the code and if they read/write to a shared state this state might be corrupted in a multi-threaded environment such as ASP.NET)
difficult to unit test (as static functions don't require an object instance, constructor injection is impossible meaning that the only way to inject dependencies is by passing them as arguments to the function itself)
Pros:
performance (this is questionable - in most cases performance gains will be completely negligible compared to other parts of the code)
There are definitely situations where static is the appropriate solution, as with any application. Any time you have some object that ought to live at the application scope, and not at the request scope, it should be static and you should use static methods to access and manipulate it.
As an example, here's a snippet of code I wrote recently for an ASP.NET application, which is essentially a serializer cache. Serializers are expensive to create and we can reuse the same one per type for as long as our application lives, so there's no need to waste time in each request thread for them:
(Note: this has been stripped down to demonstrate the static aspects)
public class XmlSerializerUtility
{
private static Dictionary<Type, XmlSerializer> serializers = new Dictionary<Type, XmlSerializer>();
private static object sync = new object();
public static T Deserialize<T>(string input)
{
XmlSerializer xs = GetSerializer(typeof(T));
using (StringReader sr = new StringReader(input))
{
return (T)xs.Deserialize(sr);
}
}
public static XmlDocument Serialize(object input)
{
XmlDocument doc = new XmlDocument();
XmlSerializer xs = GetSerializer(input.GetType());
using (MemoryStream stream = new MemoryStream())
{
xs.Serialize(stream, input);
stream.Position = 0;
doc.Load(stream);
}
return doc;
}
private static XmlSerializer GetSerializer(Type type)
{
lock (sync)
{
XmlSerializer xs = null;
if (!serializers.ContainsKey(type))
{
xs = new XmlSerializer(type);
serializers.Add(type, xs);
}
else
{
xs = serializers[type];
}
return xs;
}
}
}
The only major disadvantage to a static method is that it's almost entirely non-unit testable. Users of the method have to bind to the concrete method and cannot bind to an abstraction, thus making faking or mocking difficult if not impossible.
This may or may not be an issue, depending on the code, however.
The other thing you want to watch out for is that static data is universal across all requests to the server.