I have a class that should track the last time it was accessed and I have a public property that exposes that last access time value which is set privately.
My class is defined as follows:
internal class DeviceModelCacheItem
{
private int _cacheId;
private List<DeviceModel> _deviceModels;
private DateTime _lastAccess;
public DeviceModelCacheItem(int cacheId, List<DeviceModel> deviceModels)
{
_cacheId = cacheId;
_deviceModels = deviceModels;
_lastAccess = DateTime.UtcNow;
}
...
public List<DeviceModel> DeviceModels
{
get
{
_lastAccess = DateTime.UtcNow;
return _deviceModels;
}
set
{
_lastAccess = DateTime.UtcNow;
_deviceModels = value;
}
}
public DateTime LastAccess
{
get
{
return _lastAccess;
}
}
}
I am accessing this value in a seperate class method as follows:
var cacheItem = _deviceModelCache.SingleOrDefault(x => x.CacheId == deviceTypeId);
if(cacheItem != null && DateTime.UtcNow.Subtract(cacheItem.LastAccess).TotalSeconds > 120)
{
...
}
Where _deviceModelCache is a collection of DeviceModelCacheItem instances.
I'm finding that whenever I try and access this value, the private value will always be updated making it impossible to use it as a tracking value. Why is this being updated?
EDIT
I've included more code showing how I'm access the DateTime value. The collection of DeviceModelCacheItem instances are held in a singleton as follows:
EDIT 2
I've as also added the db access code and also included where I'm updating the _lastAccess property. I figure I may have a deferred execution problem.
public class DeviceModelRepository
{
private List<DeviceModelCacheItem> _deviceModelCache;
private static readonly Lazy<DeviceModelRepository> instance = new Lazy<DeviceModelRepository>(() => new DeviceModelRepository());
public static DeviceModelRepository Instance
{
get
{
return instance.Value;
}
}
private DeviceModelRepository()
{
_deviceModelCache = new List<DeviceModelCacheItem>();
}
public IEnumerable<DeviceModel> GetAllDeviceModelsByDeviceTypeId(int deviceTypeId)
{
return GetAllDeviceModelsByDeviceTypeId(GlobalConfiguration.APP_CACHE_ENABLED, deviceTypeId);
}
private IEnumerable<DeviceModel> GetAllDeviceModelsByDeviceTypeId(bool enableCache, int deviceTypeId)
{
var cacheItem = _deviceModelCache.SingleOrDefault(x => x.CacheId == deviceTypeId);
//Debugger attached here
if(cacheItem != null && DateTime.UtcNow.Subtract(cacheItem.LastAccess).TotalSeconds > 120)
{
...
using (GadgetContext db = new GadgetContext())
{
var deviceModels = db.DeviceModels.Where(x => x.DeviceTypeId == deviceTypeId).ToList();
if (enableCache && deviceModels != null && deviceModels.Count > 0)
{
try
{
if(cacheItem == null)
{
_deviceModelCache.Add(new DeviceModelCacheItem(deviceTypeId, deviceModels));
}
else
{
cacheItem.DeviceModels = deviceModels;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.ToString());
}
}
}
...
}
...
}
}
As pointed out by #JamesLucas the issue of the DateTime.UtcNow value always being updated was down to a deferred execution error in my code. My date time value was being updated every time entity framework deferred execution to grab items from the DB.
Related
I need to migrate some data in an Orchard module running within a Orchard 1.9.0 installation. The issue here is that the data is stored in a foreign DB on another server, not in the Orchard DB. So when the migration class methods get called, internally Orchard uses the Orchard.Data.ISessionLocator interface to retrieve the DB connection. Sadly overriding this behavior is not possible but i had the idea to hook into the session locator thing by creating a custom session locator.
The custom session locator looks like this and is based on the existing class Orchard.Data.SessionLocator:
public class CustomSessionLocator : Orchard.Data.ISessionLocator, Orchard.Data.ITransactionManager, System.IDisposable
{
// public
public CustomSessionLocator(Orchard.Data.ISessionFactoryHolder aSessionFactoryHolder)
{
Logger = Orchard.Logging.NullLogger.Instance;
IsolationLevel = System.Data.IsolationLevel.ReadCommitted;
mSessionFactoryHolder = aSessionFactoryHolder;
mSessions = new System.Collections.Generic.Dictionary<SessionScope, NHibernate.ISession>();
if (mForeignDBConnection == null)
{
string lConnectionString = "data source=myServer;initial catalog=myDB;persist security info=True;user id=xxx;password=xxx;MultipleActiveResultSets=True;";
mForeignDBConnection = new System.Data.SqlClient.SqlConnection(lConnectionString);
}
}
public NHibernate.ISession For(System.Type aEntityType)
{
Logger.Debug("Acquiring session for {0}", aEntityType);
Demand();
return mSessions[CurrentSessionScope];
}
public void Demand()
{
EnsureSession(IsolationLevel);
}
public void RequireNew()
{
RequireNew(IsolationLevel);
}
public void RequireNew(System.Data.IsolationLevel aLevel)
{
DisposeSession();
EnsureSession(aLevel);
}
public void Cancel()
{
NHibernate.ISession lSession;
if (mSessions.TryGetValue(CurrentSessionScope, out lSession) && lSession != null && !lSession.Transaction.WasRolledBack && lSession.Transaction.IsActive)
{
Logger.Debug("Rolling back transaction");
lSession.Transaction.Rollback();
}
}
public void Dispose()
{
DisposeSession();
}
public enum SessionScope
{
OrchardDefault,
ForeignDB
}
public Orchard.Logging.ILogger Logger { get; set; }
public System.Data.IsolationLevel IsolationLevel { get; set; }
public SessionScope CurrentSessionScope { private get; set; }
// private
private void DisposeSession()
{
NHibernate.ISession lSession;
if (mSessions.TryGetValue(CurrentSessionScope, out lSession) && lSession != null)
{
try
{
if (!lSession.Transaction.WasRolledBack && lSession.Transaction.IsActive)
{
Logger.Debug("Committing transaction");
lSession.Transaction.Commit();
}
}
finally
{
Logger.Debug("Disposing session");
var lConnection = lSession.Connection;
lSession.Close();
lSession.Dispose();
lSession = null;
mSessions[CurrentSessionScope] = null;
}
}
}
private void EnsureSession(System.Data.IsolationLevel aLevel)
{
NHibernate.ISession lSession;
if (mSessions.TryGetValue(CurrentSessionScope, out lSession) && lSession != null)
return;
var lSessionFactory = mSessionFactoryHolder.GetSessionFactory();
Logger.Debug("Opening NHibernate session");
if (CurrentSessionScope == SessionScope.ForeignDB)
{
lSession = lSessionFactory.OpenSession(mForeignDBConnection);
// open connection otherwise the following lSession.BeginTransaction() fails with exception
if (mForeignDBConnection.State == System.Data.ConnectionState.Closed)
mForeignDBConnection.Open();
}
else
lSession = lSessionFactory.OpenSession();
mSessions[CurrentSessionScope] = lSession;
lSession.BeginTransaction(aLevel);
}
private readonly Orchard.Data.ISessionFactoryHolder mSessionFactoryHolder;
private System.Collections.Generic.Dictionary<SessionScope, NHibernate.ISession> mSessions;
private static System.Data.SqlClient.SqlConnection mForeignDBConnection;
}
Then i have a migration data interpreter that looks like this:
public class ForeignDataMigrationInterpreter : Orchard.Data.Migration.Interpreters.DefaultDataMigrationInterpreter
{
public ForeignDataMigrationInterpreter(
Orchard.Environment.Configuration.ShellSettings aShellSettings,
Orchard.Data.ISessionLocator aSessionLocator,
System.Collections.Generic.IEnumerable<Orchard.Data.Migration.Interpreters.ICommandInterpreter> aCommandInterpreters,
Orchard.Data.ISessionFactoryHolder aSessionFactoryHolder,
Orchard.Reports.Services.IReportsCoordinator aReportsCoordinator)
: base(aShellSettings, aSessionLocator, aCommandInterpreters, aSessionFactoryHolder, aReportsCoordinator)
{
mSessionLocator = aSessionLocator as CustomSessionLocator;
}
public override void Visit(Orchard.Data.Migration.Schema.CreateTableCommand aCommand)
{
#if LIVE
if (IsForeignDBCommand(aCommand.Name, ""))
mSessionLocator.CurrentSessionScope = CustomSessionLocator.SessionScope.ForeignDB;
else
mSessionLocator.CurrentSessionScope = CustomSessionLocator.SessionScope.OrchardDefault;
#endif
base.Visit(aCommand);
}
...
private bool IsForeignDBCommand(...)
{
return ...;
}
private CustomSessionLocator mSessionLocator;
}
As you can see, the basic procedure with foreign data is
Start Orchard
Migration class method is called which contains SchemaBuilder.CreateTable()
ForeignDataMigrationInterpreter.Visit(CreateTableCommand) is called
CurrentSessionScope of custom session locator is updated to SessionScope.ForeignDB
CreateTableCommand is passed into base class
CustomSessionLocator.For() is called which
ends in CustomSessionLocator.EnsureSession() which
returns the session X for scope SessionScope.ForeignDB
base class enlists CreateTableCommand to transaction of session X
fast forward some unrelated additional steps and the transaction is commited BUT it never returns and a timeout exception occures
My questions are
Is it even possible to migrate foreign data this way?
Why does timeout occur?
How to avoid recursion call in property getter? Here is my simple code
public class UploadAttribute : Attribute
{
private Type _resourceType;
private string _select;
private string _change;
private string _remove;
public Type ResourceType
{
get { return _resourceType; }
set { _resourceType = value; }
}
public string Select
{
get { return GetResourceText(m => m.Select, "Select..."); }
set { _select = value; }
}
public string Change
{
get { return GetResourceText(m => m.Change, "Change..."); }
set { _change = value; }
}
public string Remove
{
get { return GetResourceText(m => m.Remove, "Remove"); }
set { _remove = value; }
}
private string GetResourceText(Expression<Func<UploadAttribute, string>> expression, string #default)
{
var value = expression.Compile().Invoke(this); // here .net is creating new UploadAttribute instance and use it for expression fnc
var result = value ?? #default;
if (_resourceType != null && !string.IsNullOrEmpty(value))
{
ResourceManager rm = new ResourceManager(_resourceType);
try
{
result = rm.GetString(value);
}
catch
{
// if string wasn't found in resource file than use what user specify; don't by big brother.
}
}
return result;
}
}
But if you look at method GetResourceText, there is line where I need to compile and invoke expression to get value for given property. Unfortunately this operation creates new instace of UploadAttribute. In that moment .net goes thru all properties and call getter, if I'm not mistaken, and in getter .net compiles and invokes expression to get value or given property, again, again and again to StackOverlowException.
Can you advice me how to avoid this behavior but with simplicity of this solution?
Edit: The porpose of this class is provide caption for buttons - what user sets with ability to use multi language caption from resource manager.
In example above is button Select translated from Resources, for Change button is used default text "Change..." and for Remove button caption "Destroy this #&#!". So if user doesn't specify property value, application use default text, otherwise try to find text in resources and if match is found than use text from resources else use what user set.
[Required]
[Upload(ResourceType = typeof(Resource), Select = "UploadSelect", Remove = "Destroy this #&#!")]
public HttpPostedFileBase Logo { get; set; }
It appears that what you are trying to achieve is to have some way of initializing these properties if they are not explicitly set. The manner in which you are doing that, will not work.
The m => m.Remove type expressions will cause calling the property getter again in infinite recursion until a stack overflow occurs.
You could use a lazy construct as illustrated below. It works as follows:
If the user does not specify a value for a property, it will return the hard-coded default value when the property getter is invoked.
If the user specifies a value for a property, that value it will be used first as a key in an attempt to retrieve a corresponding string value from the resources. If the resource is not found, it is used as the value for the property, provided that it is not empty, otherwise it will fall back on the hard-coded default value.
Note that this dual purpose for an attribute property value leads to a rather brittle design solution. If a resource with key "UploadSelect" is not found, that will become the caption on the button.
public class UploadAttribute : Attribute
{
private static readonly string kSelectDefaultCaption = "Select...";
private static readonly string kChangeDefaultCaption = "Change...";
private static readonly string kRemoveDefaultCaption = "Remove...";
private Type _resourceType;
private Lazy<string> _select = new Lazy<string>(() => kSelectDefaultCaption);
private Lazy<string> _change = new Lazy<string>(() => kChangeDefaultCaption);
private Lazy<string> _remove = new Lazy<string>(() => kRemoveDefaultCaption);
public Type ResourceType
{
get { return _resourceType; }
set { _resourceType = value; }
}
public string Select
{
get { return _select.Value; }
set { _select = new Lazy<string>(() => GetResourceText(value, kSelectDefaultCaption)); }
}
public string Change
{
get { return _change.Value; }
set { _change = new Lazy<string>(() => GetResourceText(value, kChangeDefaultCaption)); }
}
public string Remove
{
get { return _remove.Value; }
set { _remove = new Lazy<string>(() => GetResourceText(value, kRemoveDefaultCaption)); }
}
private string GetResourceText(string key, string #default)
{
// initialize to default.
var result = #default;
if (_resourceType != null && !string.IsNullOrEmpty(key))
{
// initialize to the value of the key,
// that could be a user supplied string literal
result = key;
// attempt to retrieve it from the resources.
ResourceManager rm = new ResourceManager(_resourceType);
try
{
result = rm.GetString(key);
}
catch
{
// could not retrieve key, using the key value as the result.
}
}
return result;
}
}
I'm pretty dump. It doesn't creates new instances at all; I answered my question in moment when I asked.
return GetResourceText(m => m.Select, "Select..."); is recursion without end, but return GetResourceText(m => m._select, "Select..."); isn't, because I'm not calling method GetResourceText again.
I'm sorry boys for silly question.
An slight modification to Alex's answer, if you wish to keep your compile-time safety on using the property names:
public class UploadAttribute : Attribute
{
private Type _resourceType;
private Lazy<string> _select;
private Lazy<string> _change;
private Lazy<string> _remove;
UploadAttribute()
{
_select = new Lazy<string>(() => GetResourceText(m => m.Select, "Select..."));
_change = new Lazy<string>(() => GetResourceText(m => m.Change, "Change..."));
_remove = new Lazy<string>(() => GetResourceText(m => m.Remove, "Remove..."));
}
public Type ResourceType
{
get { return _resourceType; }
set { _resourceType = value; }
}
public string Select
{
get { return _select.Value; }
set { _select = new Lazy<string>(() => value); }
}
public string Change
{
get { return _change.Value; }
set { _change = new Lazy<string>(() => value); }
}
public string Remove
{
get { return _remove.Value; }
set { _remove = new Lazy<string>(() => value); }
}
private string GetResourceText(Expression<Func<UploadAttribute, string>> expression, string #default)
{
var result = #default;
var memberExpression = expression.Body as MemberExpression;
if (_resourceType != null && memberExpression != null)
{
ResourceManager rm = new ResourceManager(_resourceType);
try
{
result = rm.GetString(memberExpression.Member.Name);
}
catch
{
// if string wasn't found in resource file than use what user specify; don't by big brother.
}
}
return result;
}
}
Let's assume I have an instance of this class.
public class MyClass
{
public string LocationCode;
public string PickUpCode
}
There is another class that takes a List<MyClass> as input and saves to the DB.
Now I need to apply some business rules:
For example if LocationCode is null this item in the List<MyClass> must be skipped and the foreach loop must continue to the next item in the list.
I've written the following code and the items with null LocationCode are indeed skipped but the var instance = new SomeClass(); somehow remains in memory so when the loop reaches an valid item and proceeds to save it in the DB, it also saves all the previously skipped instances of var instance = new SomeClass();. Which means I have null entries in the DB.
I'm using NHibernate and Evictdoesn't seam to be doing the trick. Any suggestions?
public void Save(List<MyClass> listOfItems)
{
using (UnitOfWork.Start())
{
var repository = new Repository();
try
{
foreach (var item in listOfItems.Select(i => i.Item).Where(item => item != null))
{
var instance = new SomeClass();
if (pickUpCode != null)
{
instance.PickUpCode = pickUpCode;
}
else
{
instance.PickUpCode = null;
}
if (locationCode != null)
{
instance.StartLocation = locationCode
}
else
{
UnitOfWork.CurrentSession.Evict(instance);
continue;
}
repository.SaveSomeClass(instance);
}
}
catch (Exception ex)
{
_log.Error(" Unhandled error", ex);
}
}
}
** Because someone asked, here's some code on UnitOfWork.Start()
public static class UnitOfWork
{
public static IUnitOfWork Start();
}
public interface IUnitOfWork : IDisposable
{
bool IsInActiveTransaction { get; }
IUnitOfWorkFactory SessionFactory { get; }
IGenericTransaction BeginTransaction();
IGenericTransaction BeginTransaction(IsolationLevel isolationLevel);
void Flush();
void TransactionalFlush();
void TransactionalFlush(IsolationLevel isolationLevel);
}
Why don't you fail first and avoid all of this?
Example being:
foreach (var item in listOfItems.Select(i => i.Item).Where(item => item != null))
{
if (item.LocationCode == null){
continue;
}
var instance = new SomeClass();
if (pickUpCode != null)
{
instance.PickUpCode = pickUpCode;
}
else
{
instance.PickUpCode = null;
}
// if we reach here, location code is definitley not null, no need for the check
instance.StartLocation = locationCode
repository.SaveSomeClass(instance);
}
Alternatively, you could add the check to you LINQ where clause
foreach (var item in listOfItems.where(item=> item != null && item.LocationCode != null)
Without more code on how UnitofWork.Start works its hard to suggest. But, It's worth trying by implementing IDisposable on SomeClass.
This is a basically a class library project which is somehow exposed as a WCF service. The code below is a part of the Data Access Layer. 'db' is an object of a DataContext class. To save a file, we do the following-
public static Guid SaveFile(FileDetails fileDetails)
{
System.Nullable<Guid> id = null;
SystemDataContext.UsingWrite(db =>
{
db.SaveFileData(fileDetails.RunId, fileDetails.FileData, fileDetails.FileExtension, ref id);
});
return id ?? Guid.Empty;
}
Then, the below would execute-
public static void UsingWrite(Action<SoftCashCreditDBDataContext> action)
{
using (var context = new SystemDataContext())
{
try
{
action(context.Write);
}
catch (Exception ex)
{
DataAccessExceptionHandler.HandleExcetion(ex, Config.DataLayerPolicy);
}
}
}
public SystemDataContext()
{
if (_stack == null)
{
_stack = new Stack<SystemDataContext>();
this.Depth = 1;
this.Read = new SoftCashCreditDBDataContext(Config.ReadDatabaseConnection);
this.Write = new SoftCashCreditDBDataContext(Config.WriteDatabaseConnection);
}
else
{
var parent = _stack.Peek();
/// Increment level of node.
this.Depth = parent.Depth + 1;
/// Copy data context from the parent
this.Read = parent.Read;
this.Write = parent.Write;
}
_stack.Push(this);
}
public int Depth { get; private set; }
public bool IsRoot { get { return this.Depth == 1; } }
[ThreadStatic]
private static Stack<SystemDataContext> _stack = null;
public SoftCashCreditDBDataContext Read { get; private set; }
public SoftCashCreditDBDataContext Write { get; private set; }
#region IDisposable Members
public void Dispose()
{
var context = _stack.Pop();
if (context.IsRoot == true)
{
context.Read.Dispose();
context.Write.Dispose();
_stack = null;
}
}
#endregion
}
They have implemented LINQ to SQL here, and created a DBContext class. The 'SaveFileData()' method is actually part of that class, where it just calls an SP inside to save the file.
What I did not follow-
What exactly does the call to UsingWrite() do here? What is passed to the 'Action action' parameter, and what is it doing?
I understand your confusion. They use 2 delegates.
This is passed to the action parameter:
db =>
{
db.SaveFileData(fileDetails.RunId, fileDetails.FileData, fileDetails.FileExtension, ref id);
}
So when UsingWrite is called, the SoftCashCreditDBDataContext delegate which was set in the Write delegate will call SaveFileData.
A simplified example to help you understand Action:
public void Main()
{
Test(x => Debug.Write(x));
}
private void Test(Action<string> testAction)
{
testAction("Bla");
}
This function will call Debug.Write with the argument x, which is a string that is passed to the test action function.
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;
}