I have a requirement where we need a plugin to retrieve a session id from an external system and cache it for a certain time. I use a field on the entity to test if the session is actually being cached. When I refresh the CRM form a couple of times, from the output, it appears there are four versions (at any time consistently) of the same key. I have tried clearing the cache and testing again, but still the same results.
Any help appreciated, thanks in advance.
Output on each refresh of the page:
20170511_125342:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125410:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125342:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125437:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125358:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
20170511_125437:1:55a4f7e6-a1d7-e611-8100-c4346bc582c0
To accomplish this, I have implemented the following code:
public class SessionPlugin : IPlugin
{
public static readonly ObjectCache Cache = MemoryCache.Default;
private static readonly string _sessionField = "new_sessionid";
#endregion
public void Execute(IServiceProvider serviceProvider)
{
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
try
{
if (context.MessageName.ToLower() != "retrieve" && context.Stage != 40)
return;
var userId = context.InitiatingUserId.ToString();
// Use the userid as key for the cache
var sessionId = CacheSessionId(userId, GetSessionId(userId));
sessionId = $"{sessionId}:{Cache.Select(kvp => kvp.Key == userId).ToList().Count}:{userId}";
// Assign session id to entity
var entity = (Entity)context.OutputParameters["BusinessEntity"];
if (entity.Contains(_sessionField))
entity[_sessionField] = sessionId;
else
entity.Attributes.Add(new KeyValuePair<string, object>(_sessionField, sessionId));
}
catch (Exception e)
{
throw new InvalidPluginExecutionException(e.Message);
}
}
private string CacheSessionId(string key, string sessionId)
{
// If value is in cache, return it
if (Cache.Contains(key))
return Cache.Get(key).ToString();
var cacheItemPolicy = new CacheItemPolicy()
{
AbsoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration,
Priority = CacheItemPriority.Default
};
Cache.Add(key, sessionId, cacheItemPolicy);
return sessionId;
}
private string GetSessionId(string user)
{
// this will be replaced with the actual call to the external service for the session id
return DateTime.Now.ToString("yyyyMMdd_hhmmss");
}
}
This has been greatly explained by Daryl here: https://stackoverflow.com/a/35643860/7708157
Basically you are not having one MemoryCache instance per whole CRM system, your code simply proves that there are multiple app domains for every plugin, so even static variables stored in such plugin can have multiple values, which you cannot rely on. There is no documentation on MSDN that would explain how the sanboxing works (especially app domains in this case), but certainly using static variables is not a good idea.Of course if you are dealing with online, you cannot be sure if there is only single front-end server or many of them (which will also result in such behaviour)
Class level variables should be limited to configuration information. Using a class level variable as you are doing is not supported. In CRM Online, because of multiple web front ends, a specific request may be executed on a different server by a different instance of the plugin class than another request. Overall, assume CRM is stateless and that unless persisted and retrieved nothing should be assumed to be continuous between plugin executions.
Per the SDK:
The plug-in's Execute method should be written to be stateless because
the constructor is not called for every invocation of the plug-in.
Also, multiple system threads could execute the plug-in at the same
time. All per invocation state information is stored in the context,
so you should not use global variables or attempt to store any data in
member variables for use during the next plug-in invocation unless
that data was obtained from the configuration parameter provided to
the constructor.
Reference: https://msdn.microsoft.com/en-us/library/gg328263.aspx
Related
In an ASP.NET MVC application, I'm trying to use SQL Server's CONTEXT_INFO to pass the currently logged in user so my audit triggers record not only the web server login, but also the login of the site.
I'm having trouble being certain that the current user will always be fed into the database server context though.
On the backend I have everything set up, a sproc to set the context, a function to pull it and DML triggers to record, no problem.
The app end is a bit more involved. I subscribe to the Database.Connection.StateChange event so I can catch each newly opened connection and set this context accordingly.
Additionally, to be able to retrieve the current login ID of the MVC site in the data layer (which has no access to the web project), I supply a delegate to the EF constructor that will return the user ID. This also means that any other peripheral projects I have set up require this dependency as well, and it keeps most of the implementation detail out of my hair during the web dev:
public class CoreContext : DbContext
{
Func<int> _uidObtainer;
public CoreContext(Func<int> uidObtainer) : base(nameof(CoreContext)) { construct(uidObtainer); }
public CoreContext(Func<int> uidObtainer, string connection) : base(connection) { construct(uidObtainer); }
void construct(Func<int> uidObtainer) {
// disallow updates of the db from our models
Database.SetInitializer<CoreContext>(null);
// catch the connection change so we can update for our userID
_uidObtainer = uidObtainer;
Database.Connection.StateChange += connectionStateChanged;
}
private void connectionStateChanged(object sender, System.Data.StateChangeEventArgs e) {
// set our context info for logging
if (e.OriginalState == System.Data.ConnectionState.Open ||
e.CurrentState != System.Data.ConnectionState.Open) {
return;
}
int uid = _uidObtainer();
var conn = ((System.Data.Entity.Core.EntityClient.EntityConnection)sender).StoreConnection;
var cmd = conn.CreateCommand();
cmd.CommandText = "audit.SetContext";
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new System.Data.SqlClient.SqlParameter("#DomainUserID", uid));
cmd.ExecuteNonQuery();
}
// etc etc...
In my MVC project, I'll have code that looks like this:
context = new Data.CoreContext(() => AppService.UserID());
(making use of a readily accessible method to pass as delegate, which in turn reads from HttpContext.Current.User)
This is all shaping up nicely, except one unknown:
I know that it's possible for a EF Context instance to span multiple logged in users as this lives as part of the IIS app pool and not per HttpContext
What I don't know is enough about connection pooling and how connections are opened/re-opened to be safe in knowing that for each time my StateChange handler runs, I'll actually be retrieving the new UserID from the delegate.
Said differently: is it possible for a single connection to be open and used over the span of two separate HttpContext instances? I believe yes, seeing as how there's nothing to enforce otherwise (at least not that I'm aware of).
What can I do to ensure that each connection is getting the current HttpContext?
(possibly pertinent notes: There's no UoW/Repository pattern outside of EF itself, and data contexts are generally instantiated once per controller)
I see: the one context per controller is generally incorrect. Instead I should be using one context per request, which (besides other advantages), ensures my scenario operates correctly as well.
I found this answer, which explains the reasoning behind it: One DbContext per web request... why?
And I found this answer, which explains quite succinctly how to implement via BeginRequest and EndRequest: One DbContext per request in ASP.NET MVC (without IOC container)
(code from second answer pasted below to prevent linkrot)
protected virtual void Application_BeginRequest()
{
HttpContext.Current.Items["_EntityContext"] = new EntityContext();
}
protected virtual void Application_EndRequest()
{
var entityContext = HttpContext.Current.Items["_EntityContext"] as EntityContext;
if (entityContext != null)
entityContext.Dispose();
}
And in your EntityContext class...
public class EntityContext
{
public static EntityContext Current
{
get { return HttpContext.Current.Items["_EntityContext"] as EntityContext; }
}
}
I am using the following code in order to ensure that I only go to the database once for my Agent data and for the cached data to be refereshed when the contractId being passed in changes.
public static AgentCacher
{
private IAgentDal AgentDal;
private readonly ObjectCache AgentObjectCache;
private string LastContractId;
public AgentCacher(IAgentDal agentDal)
{
this.AgentDal = agentDal;
// Get the instance of the cache
this.AgentObjectCache = MemoryCache.Default;
}
public List<Agent> GetAgentsForContract(int contractId)
{
// Set the key to be used for the cache
var cacheKey = contractId.ToString(CultureInfo.InvariantCulture);
// Has the contract ID changed?
if (this.LastContractId !== cacheKey)
{
// Remove the item from the cache
this.AgentObjectCache.Remove(this.LastContractId);
}
// Are the agents for this contract ID already in the cache?
else if (this.AgentObjectCache.Contains(cacheKey))
{
// Return agents list from the cache
return
this.AgentObjectCache.Get(cacheKey) as
List<Agent>;
}
// Go to the database and get the agents
var agentsFromDatabase = this.AgentDal.GetAgentsForContract(contractId);
// Add the values to the cache
this.AgentObjectCache.Add(cacheKey, agentsFromDatabase, DateTimeOffset.MaxValue);
// Set the contract Id for checking next time
this.LastContractId = cacheKey;
// Return the agents
return agentsFromDatabase;
}
}
This works OK, but I feel like I'm probably not using the MemoryCache in the way it was intended to be used.
How can I trigger the removal of the values that I add to the cache to clear out the old values when the contractId changes, do I have to use ChangeMonitor or CacheItemPolicy that can be passed in when adding to the cache?
I've been struggling to find examples as to how it should be used properly.
Your logic looks right. However you are managing cache lifetime yourself instead of relying on built in expiration system technics. For instance instead of you to check if there is a new contractId, remove old one and add new one, I think you should cache for as many contractIds as needed, but to have for example absolute expiration for 1 hour. For example if there is contractId == 1 then you will have cache with cache key 1 and if another request asks for contractId == 2 then you will go to db pull contract information for id == 2 and store it in the cache for another absolute expiration 1 hour or so. I think this will be more efficient instead of you manage cache (add, remove) yourself.
You also need to consider locking data when you add and remove data from the cache in order to eliminate race condition.
You can find good example on how to do it:
Working With Caching in C#
Using MemoryCache in .NET 4.0
I have multiple partial views that must be shown if a flag is set to true accross the entire site.
I have that flag hardcoded inside appSettings on my web.config file and is working nice. But now this flag must be set trough our back-end.
The site has a lot of traffic and I need a proper way to reach that, I feel like making a SQL request just to check this flag is an overkill.
I've though about reading a simple txt file containing the flag, but I dont know if it's still "too much".
How would be the most optimized way?
Check out MemoryCache. You can create a basic static class with a static property to return the cached flag value, and then you can define an absolute expiration to whatever comfort level you can live with (5 second or 60 minutes or any timespan) upon which you'd update the value in the cache.
Here is a very quick example to handle threading.
public static class CacheStore
{
private static readonly string _keyMySharedFlag = "shared.flag";
private static readonly object _lockMySharedFlag = new object();
public static bool MySharedFlag
{
get
{
var cachedFlag = (bool?)MemoryCache.Default.Get(_keyMySharedFlag);
if (cachedFlag != null)
return cachedFlag.Value;
lock (_lockMySharedFlag)
{
// Confirm no other threads wrote to cache while we waited
cachedFlag = (bool?)MemoryCache.Default.Get(_keyMySharedFlag);
if (cachedFlag != null)
return cachedFlag.Value;
bool? newFlag = true; // Set to your database value
var cachePolicy = new CacheItemPolicy();
cachePolicy.AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(5); // 5 minutes
MemoryCache.Default.Set(_keyMySharedFlag, newFlag, cachePolicy);
return newFlag.Value;
}
}
}
}
I would suggest define a global configuration class which has all the flags, common data in it and use Dependency Injection to inject it to where ever you need. This will result in a more testable solution I believe.
I basically have created a class which when a user logs into a website it then queries the database and stores some settings in a List (So I have key/pair values).
The reason for this is because I want to always be able to access these settings without going to the database again.
I put these in a class and loop through the fields via a SQL query and add them to the list.
How can I then access these variables from another part of the application? or is there a better way to do this? I'm talking server side and not really client side.
Here is an example of what I had at the moment:
public static void createSystemMetaData()
{
string constring = ConfigurationManager.ConnectionStrings["Test"].ConnectionString;
SqlConnection sql = new SqlConnection(constring);
sql.Open();
SqlCommand systemMetaData = new SqlCommand("SELECT * FROM SD_TABLES", sql);
//Set Modules
using (SqlDataReader systemMetaDataReader = systemMetaData.ExecuteReader())
{
while (systemMetaDataReader.Read())
{
var name = systemMetaDataReader.GetOrdinal("Sequence").ToString();
var value = systemMetaDataReader.GetOrdinal("Property").ToString();
var Modules = new List<KeyValuePair<string, string>>();
Modules.Add(new KeyValuePair<string, string>(name, value));
}
}
}
Thanks
Any static properties of a class will be preserved for the lifetime of the application pool, assuming you're using ASP.NET under IIS.
So a very simple class might look like:
public static class MyConfigClass
{
public static Lazy<Something> MyConfig = new Lazy<Something>(() => GetSomethings());
public static Something GetSomethings()
{
// this will only be called once in your web application
}
}
You can then consume this by simply calling
MyConfigClass.MyConfig.Value
For less users you can go with the SessionState as Bob suggested,however with more users you might need to move to a state server or load it from Data Base each time.
As others have pointed out, the risk of holding these values in global memory is that the values might change. Also, global variables are a bad design decision as you can end up with various parts of your application reading and writing to these values, which makes debugging problems harder than it need be.
A commonly adopted solution is to wrap your database access inside a facade class. This class can then cache the values if you wish to avoid hitting the database for each request. In addition, as changes are routed through the facade too, it knows when the data has changed and can empty its cache (forcing a database re-read) when this occurs. As an added bonus, it becomes possible to mock the facade in order to test code without touching the database (database access is notoriously difficult to unit test).
From the looks of things you are using universal values irrespective of users so an SqlCacheDependency would be useful here:
Make sure you setup a database dependency in web.config for the name Test
public static class CacheData {
public static List<KeyValuePair<string,string>> GetData() {
var cache = System.Web.HttpContext.Current.Cache;
SqlCacheDependency SqlDep = null;
var modules = Cache["Modules"] as List<KeyValuePair<string,string>>;
if (modules == null) {
// Because of possible exceptions thrown when this
// code runs, use Try...Catch...Finally syntax.
try {
// Instantiate SqlDep using the SqlCacheDependency constructor.
SqlDep = new SqlCacheDependency("Test", "SD_TABLES");
}
// Handle the DatabaseNotEnabledForNotificationException with
// a call to the SqlCacheDependencyAdmin.EnableNotifications method.
catch (DatabaseNotEnabledForNotificationException exDBDis) {
SqlCacheDependencyAdmin.EnableNotifications("Test");
}
// Handle the TableNotEnabledForNotificationException with
// a call to the SqlCacheDependencyAdmin.EnableTableForNotifications method.
catch (TableNotEnabledForNotificationException exTabDis) {
SqlCacheDependencyAdmin.EnableTableForNotifications("Test", "SD_TABLES");
}
finally {
// Assign a value to modules here before calling the next line
Cache.Insert("Modules", modules, SqlDep);
}
}
return modules;
}
My MVC application allows a subset of users to insert/edit records in a table, and since I'm using Windows authentication I get their samaccountnames "for free" and can insert these in a "Last Updated By" field in the mentioned records.
One of the most important (and frequently used) views in my application will display lists of 50-100 records per page, but I don't want to display their samaccountnames. I want their more user-friendly display names that I want to get from Active Directory.
I've seen several posts here suggesting linking AD to SQL, but that requires installing components on the SQL server which I'd rather not do. Instead, I was thinking of creating the following interface and derived class:
public interface IUserInformationStore
{
UserInformation FindBySamAccountName(string samAccountName)
}
public class ActiveDirectoryStore
{
HashSet<UserInformation> _cache;
public UserInformation FindBySamAccountName(string samAccountName)
{
// Look for samaccountname in _cache and if not found
// retrieve information from AD with DirectorySearcher.
// Store information in _cache and return correct user.
}
My problem now is how to access this information. I was thinking of using Ninject's ToSingleton, but I suspect that might be "Singleton Per Worker process". So maybe the Cache would be a better place for it. But what would be the best way of accessing the object? Static class with static property that checks if it's in the Cache already, initializes it otherwise, and returns the object?
Or is there a completely better way of solving this problem?
I tried 2 solutions eventually:
1
kernel.Bind<IUserRepository>().To<ActiveDirectoryUserRepository>().InSingletonScope().WithConstructorArgument("rootPath", "LDAP://dc=tessdata,dc=no");
public static MvcHtmlString GetDisplayNameSingleton(this HtmlHelper htmlHelper, string samAccountName)
{
var userRepository = DependencyResolver.Current.GetService<IUserRepository>();
return new MvcHtmlString(userRepository != null ? userRepository.FindByUsername(samAccountName).DisplayName : "Ukjent");
}
2
kernel.Bind<IUserRepository>().To<ActiveDirectoryUserRepository>().WithConstructorArgument("rootPath", "LDAP://dc=tessdata,dc=no");
public static MvcHtmlString GetDisplayName(this HtmlHelper htmlHelper, string samAccountName)
{
if (HttpRuntime.Cache["UserRepository"] == null)
{
var newUserRepository = DependencyResolver.Current.GetService<IUserRepository>();
HttpRuntime.Cache.Add("UserRepository", newUserRepository, null, DateTime.MaxValue,
TimeSpan.FromMinutes(20), CacheItemPriority.Default, null);
}
var userRepository = HttpRuntime.Cache["UserRepository"] as IUserRepository;
return new MvcHtmlString(userRepository != null ? userRepository.FindByUsername(samAccountName).DisplayName : "Ukjent");
}
The second method was noticeably faster, especially after the first call when the repository was cached. You get better control over caching too. The first method will stay in memory until the application is restarted.
I'm not sure about best practice in either case though.