I have an ASPX Web Forms control that imports from another project the Person class.
public partial class cuTest : System.Web.UI.UserControl
There I have created in that control a property for an object session and a method to populate that session.
public Person person
{
get
{
if (this.Session["Person"] == null)
{
Session["Person"] = new Person();
}
return Session["Person"] as Person;
}
}
private void crearPago()
{
this.person.name = "Max";
this.person.surname = "Ford";
}
Now when I want to call it from the page that contains that control.
var x = cuTest.person; I need to check if it's not empty since this can't be null. How can I do that?
I have a static class Web that I add there any properties that are useful throughout the application.
One thing that differs is that the Request, Response and Session objects are not directly available. So, I have 3 helper functions that return the current Request, Response and Session objects, if available.
So, a Person person property would be like that:
using System;
using System.Web;
using System.Web.SessionState;
namespace MyWebApp
{
public static class Web
{
private static HttpRequest request
{
get
{
if (HttpContext.Current == null) return null;
return HttpContext.Current.Request;
}
}
private static HttpResponse response
{
get
{
if (HttpContext.Current == null) return null;
return HttpContext.Current.Response;
}
}
private static HttpSessionState session
{
get
{
if (HttpContext.Current == null) return null;
return HttpContext.Current.Session;
}
}
public static Person person
{
get
{
// Here you can change what is returned when session is not available
if (session == null) return null;
if(session["Person"] == null) {
session["Person"] = new Person();
}
return session["Person"] as Person;
}
set
{
// Here you can change how to handle the case when session is not available
if (session == null) return;
session["Person"] = value;
}
}
}
}
To use it, in your Page or UserControl code, you can write
// get
var x = MyWebApp.Web.person;
// set
MyWebApp.Web.person = x;
You can simply check if the name/surname is null or empty with:
string.IsNullOrEmpty(x.name);
Related
I am currently accessing my database directly when I get a GET-request by using something like this: _context.EXT_PRAMEX_INSERTS.Where(item => item.MatID == MaterialID.
But my database does not change very often so I want to create a data-class that fetches all tables on startup into lists.
And then I just parse the lists when I need to get any of the entries.
I was thinking that I could create a singleton-class that fetches all data on startup and then I just use that instance to get my data. But how do I access the context inside the singleton ? Or is there a better way to accomplish what I am trying to do ?
public sealed class ExternalDatabase
{
private static ExternalDatabase _instance = null;
private static readonly object _instanceLock = new object();
private List<EXT_PramexInserts>? pramexInserts;
private List<EXT_PrototypeEndmills>? prototypeEndmills;
private static List<IProducts> extAll = new List<IProducts>();
ExternalDatabase()
{
pramexInserts = pramexInserts == null ? _context.Set<EXT_PramexInserts>().ToList() : pramexInserts;
prototypeEndmills = prototypeEndmills == null ? _context.Set<EXT_PrototypeEndmills>().ToList() : prototypeEndmills;
if (extAll.Count == 0)
{
extAll = extAll.Union(pramexInserts).ToList().Union(prototypeEndmills).ToList();
}
}
public static ExternalDatabase Instance
{
get
{
lock (_instanceLock)
{
if (_instance == null)
{
_instance = new ExternalDatabase();
}
return _instance;
}
}
}
public List<IProducts> GetExtraData(string materialid)
{
var result = extAll.FindAll(p => p.MaterialID == materialid);
return result;
}
}
[ApiController]
[Route("DB")]
public class EXT_TablesController : ControllerBase
{
private readonly Context _context;
private static ExternalDatabase data = ExternalDatabase.Instance;
public EXT_TablesController(Context context)
{
_context = context;
}
//...
[HttpPost]
public IEnumerable<IProducts> Post([FromBody] EXT_Request request)
{
string selectedTable = null;
var (MaterialID, Type, Brand) = request;
if (Type != null && Brand != null)
{
selectedTable = $"EXT_{Brand}_{Type}";
switch (selectedTable)
{
case "EXT_PRAMEX_INSERTS":
//var items = _context.EXT_PRAMEX_INSERTS.Where(item => item.MatID == MaterialID);
return data.GetExtraData(MaterialID);
default:
return null;
}
}
return null;
}
}
It's better to keep your DbContext short-lived.
If you want to use the context in a singleton class, you can try DbContextFactory.
Also you may want to have a look at the secondary level cache pattern. Here is a ready-to-go package.
I don't know if it is the best answer but I followed one of the tips that Mehdi Dehghani suggested in the comment above.
I created a new context inside the singleton and it solved my problem.
I'm having a controller class like
class MyController : Controller
{
User user
{
get
{
if(Session["User"] == null)
Session["User"] = // Constructing object here
return Session["User"];
}
}
....
}
I have lot of properties like User in this controller. I want to get/set without all this
if(Session["User"] == null)
noises. It could be something with attribute like
class MyController : Controller
{
[SessionCacheObject("User")]
User user;
}
How do I do this or is there any other smart way?
Look at my User session.
public static aspnet_Users LogInUser
{
get
{
if (HttpContext.Current.Session["LogInUser"] != null)
{
return (aspnet_Users)HttpContext.Current.Session["LogInUser"];
}
else
{
return null;
}
}
set
{
HttpContext.Current.Session["LogInUser"] = value;
}
}
Now, whenever add data to session like below.
LogInUser = user; // get data from wherever you need and set it.
I have a cookie helper class that gets and sets data for a cookie.
In my controller action I'm trying to update a List collection and persist that to the cookie.
UPDATE: It seems using HttpContext.Current.Response.Cookies.Add() even if the cookie exists or not it will do an upsert on it and work correctly.
So what's the purpose of Reponse.Cookie.Set() then?
private List<int> _TestNumbers = new List<int>();
cookie = new CookieHelper(_searchCookieName);
cookie.SetData("testNumbers", _TestNumbers);
_TestNumbers.Add(1);
cookie.SetData("testNumbers", _TestNumbers);
_TestNumbers.Add(2);
cookie.SetData("testNumbers", _TestNumbers);
_TestNumbers.Add(3);
cookie.SetData("testNumbers", _TestNumbers);
The cookie helper class
public class CookieHelper
{
public CookieHelper(string cookieName = null, HttpContext context = null)
{
// Set param defaults
context = context ?? HttpContext.Current;
if (cookieName != null)
_cookieName = cookieName;
// Load cookie if it exists, if not create one.
_cookie = context.Request.Cookies[_cookieName] ?? new HttpCookie(_cookieName);
Save();
}
public object GetData(string name)
{
return _cookie[name] == null ? null : new Base64Serializer().Deserialize(_cookie[name]);
}
public void SetData(string name, object value)
{
_cookie[name] = new Base64Serializer().Serialize(value);
Save();
}
public void Save()
{
_cookie.Expires = DateTime.UtcNow.AddDays(_cookieExpiration);
// Create the cookie if it doesn't exist
if(HttpContext.Current.Request.Cookies.Get(_cookieName) == null)
HttpContext.Current.Response.Cookies.Add(_cookie);
else
HttpContext.Current.Response.Cookies.Set(_cookie);
}
}
I have a CRM 2011 plugin using C#: Synchronus, Post-Operation and Not-Sandbox.
Whenever launched, the plugin works fine for the first time. But my problem is when I call the plugin the second time (with other GUID), the entity will have the first calling's values. So instead of having the new entity attributes that im just calling, im having the old entity's attributes (first call). Is there any solution to this problem?
Note that I have tried the Async mode and it worked fine, but I need to display error messages on the screen after calling a Webservice so I need it to be on Sync mode.
Plugin CLass:
namespace FNBChangeCustomerInfGroup
{
public abstract class Plugin : IPlugin
{
public IServiceProvider _serviceProvider;
public IOrganizationServiceFactory _organizationServiceFactory;
public IPluginExecutionContext _pluginExecutionContext;
public IOrganizationService _organizationService;
public ResourceManager _resourceManager;
public Lazy<Helper> _helper;
public IPluginExecutionContext PluginExecutionContext
{
get
{
if (_pluginExecutionContext == null)
{
_pluginExecutionContext = _serviceProvider.GetService(typeof(IPluginExecutionContext)) as IPluginExecutionContext;
if (_pluginExecutionContext == null)
{
throw new InvalidOperationException("Cannot get Execution Context");
}
}
return _pluginExecutionContext;
}
}
public IOrganizationServiceFactory OrganizationServiceFactory
{
get
{
if (_organizationServiceFactory == null)
{
_organizationServiceFactory = _serviceProvider.GetService(typeof(IOrganizationServiceFactory)) as IOrganizationServiceFactory;
if (_organizationServiceFactory == null)
{
throw new InvalidOperationException("Cannot get Service Factory");
}
}
return _organizationServiceFactory;
}
}
public IOrganizationService OrganizationService
{
get
{
if (_organizationService == null)
{
_organizationService = OrganizationServiceFactory.CreateOrganizationService(_pluginExecutionContext.UserId);
if (_organizationService == null)
{
throw new InvalidOperationException("Cannot get Organization Service");
}
}
return _organizationService;
}
}
protected virtual ResourceManager ResourceManager
{
get
{
if (_resourceManager == null)
{
_resourceManager = new ResourceManager(Constants.File_Resources, Assembly.GetExecutingAssembly());
if (_resourceManager == null)
{
throw new InvalidOperationException("Cannot access Resources file");
}
}
return _resourceManager;
}
}
protected Lazy<Helper> Helper
{
get
{
if (_helper == null || !_helper.IsValueCreated)
{
_helper = new Lazy<Helper>(() => new Helper(_organizationService));
}
return _helper;
}
}
public IExtensibleDataObject CheckType<ExtensibleDataObjectType>(string entityLogicalName) where ExtensibleDataObjectType : IExtensibleDataObject, new()
{
// Check if the input parameters property bag contains a target
// of the operation and that target is of type EntityReference.
if (PluginExecutionContext.InputParameters.Contains(Constants.InputParameter_Target) && PluginExecutionContext.InputParameters[Constants.InputParameter_Target] is ExtensibleDataObjectType)
{
// Obtain the target business entity from the input parameters.
IExtensibleDataObject entity = (PluginExecutionContext.InputParameters[Constants.InputParameter_Target]) as IExtensibleDataObject;
// Verify that the entity represents a risk carrier quote.
if (typeof(Entity).IsAssignableFrom(typeof(ExtensibleDataObjectType)))
{
if (!String.Equals(((Entity)entity).LogicalName, entityLogicalName)) { return null; }
}
else if (typeof(EntityReference).IsAssignableFrom(typeof(ExtensibleDataObjectType)))
{
if (!String.Equals(((EntityReference)entity).LogicalName, entityLogicalName)) { return null; }
}
return entity;
}
else
{
return null;
}
}
public virtual void Execute(IServiceProvider serviceProvider)
{
this._serviceProvider = serviceProvider;
}
}
}
This is what happens when a plugin class contains fields. Plugin classes are not allowed to be stateful, because they are instantiated once by the CRM platform and then reused. Therefore fields on these classes are not thread safe.
Check your CRMPluginBase class. Looking at method base.CheckType<Entity>() chances are this class has a private field holding an IPluginExecutionContext instance. Redesign your plugin base class and make shure it does not contain instance fields at all.
I have a singleton provider, where the main function is to retrieve an object from a webservice, and cache depending on the webservice cache headers response. This object will be accessed quite a lot. My question is when the data in the webservice changes, will any subsequent call to the singleton automatically be reflected?
public class ConfigurationProvider
{
#region Private Member Variables
private static readonly Lazy<ConfigurationProvider> _instance = new Lazy<ConfigurationProvider>(() => new ConfigurationProvider());
private static readonly HttpCache _cache = new HttpCache();
#endregion
#region Constructors
private ConfigurationProvider()
{
}
#endregion
#region Public Properties
public static ConfigurationProvider Instance
{
get { return _instance.Value; }
}
public ShowJsonResponse Configuration
{
get
{
// Try and get the configurations from webservice and add to cache
var cacheExpiry = 0;
return _cache.GetAndSet(WebApiConstant.ProxyCacheKeys.ShowJsonKey, ref cacheExpiry, () => GetConfiguration(ref cacheExpiry));
}
}
#endregion
#region Private Methods
private ShowJsonResponse GetConfiguration(ref int cacheExpiry)
{
var httpClient = new HttpClient();
try
{
var response = httpClient.GetAsync(WebApiConstant.Configuration.WebserviceUrl).Result;
if (response.IsSuccessStatusCode)
{
var showResponse = response.Content.ReadAsAsync<ShowJsonResponse>().Result;
if (response.Headers.CacheControl.Public && response.Headers.CacheControl.MaxAge.HasValue)
{
cacheExpiry = response.Headers.CacheControl.MaxAge.Value.Seconds;
}
// TODO: Remove when finished testing
// Default to 60 seconds for testing
cacheExpiry = 20;
return showResponse;
}
}
catch (HttpRequestException ex)
{
}
cacheExpiry = 0;
return null;
}
#endregion
}
The HttpCache class is just a wrapper around HttpRuntime Cache. The GetAndSet method just tries to retrieve the cache object and sets it if not found.
public override T GetAndSet<T>(string key, ref int duration, Func<T> method)
{
var data = _cache == null ? default(T) : (T) _cache[key];
if (data == null)
{
data = method();
if (duration > 0 && data != null)
{
lock (sync)
{
_cache.Insert(key, data, null, DateTime.Now.AddSeconds(duration), Cache.NoSlidingExpiration);
}
}
}
return data;
}
Usage example:
ConfigurationProvider.Instance.Configuration.Blah
Is there any perceived benefit to using the singleton pattern in this scenario, or instantiate the class regularly would be ok?
I think that the singleton pattern fits better in your case, and you won't need the object instance as well. Are you taking care of concurrency inside your HttpCache wrapper? It's important in order to avoid that concurrent threads could make multiple WS requests when two or more access the cache object at the same time or before the WS request returns.
I would suggest you to use the double lock/check pattern:
public override T GetAndSet<T>(string key, ref int duration, Func<T> method) {
var data = _cache == null ? default(T) : (T) _cache[key];
if (data == null) { //check
lock (sync) { //lock
//this avoids that a waiting thread reloads the configuration again
data = _cache == null ? default(T) : (T) _cache[key];
if (data == null) { //check again
data = method();
if (duration > 0 && data != null) {
_cache.Insert(key, data, null, DateTime.Now.AddSeconds(duration), Cache.NoSlidingExpiration);
}
}
}
}
return data;
}