I'm using the Massive micro-ORM with a new project. I added some caching, and it all appeared to work - until I realize that the cached objects (dynamic) are still querying the database. This kills the whole point of caching.
What would be the easiest way to disconnect the result set from the database. Nearly all calls are read-only.
I'm using code like this to query records:
public static dynamic GetActive()
{
return Caching.LoadFromCache("Units_GetActive", 120,
() =>
{
dynamic tbl = new Units();
return tbl.Find(Enabled: 1, orderby: "SortOrder");
});
}
My caching code looks like this:
public static dynamic LoadFromCache(string cacheKey, int secondsToCache, Func<object> query)
{
object tocache = null;
// result will always get the value here
// tocache will only have the value when it was pulled from our method
object result = MemoryCache.Default[cacheKey] ?? (tocache = query.Invoke());
if (secondsToCache > 0)
{
if (tocache != null) // only save to cache if it wasn't there
MemoryCache.Default.Add(cacheKey, tocache, DateTime.UtcNow.AddSeconds(secondsToCache));
}
else
{
// remove from cache only if secondsToCache was zero or less
MemoryCache.Default.Remove(cacheKey);
}
return result;
}
The caching code works. The problem is the dynamic object returned (IEnumerable<dynamic>) opens another connection to the database.
Ideas?
Tim Medora had the right idea.
I changed my caching code to this:
public static dynamic LoadFromCache(string cacheKey, int secondsToCache, Func<object> query)
{
object tocache = null;
// result will always get the value here
// tocache will only have the value when it was pulled from our method
object result = MemoryCache.Default[cacheKey] ?? (tocache = query.Invoke());
if (secondsToCache > 0)
{
// If this came from Massive, we need to get the list instead of opening a
// database connection each time we enumerate. I check the type, because other things
// are stored.
if (tocache is IEnumerable<dynamic>)
{
tocache = ((IEnumerable<dynamic>)tocache).ToList<dynamic>();
result = tocache;
}
if (tocache != null) // only save to cache if it wasn't there
MemoryCache.Default.Add(cacheKey, tocache, DateTime.UtcNow.AddSeconds(secondsToCache));
}
else
{
// remove from cache only if secondsToCache was zero or less
MemoryCache.Default.Remove(cacheKey);
}
return result;
}
The ToList<dynamic>() was the key part. It seems to work as expected now. ToList<T> is an extension method in the System.Linq namespace.
Related
Given any object, I want to be able to retrieve the value of a property and any "depth".
var name = myObject.GetValue<String>("Name")
or
var father_name = myObject.GetValue<String>("Father.Name")
This is easy and I'm able do achieve it with the following code:
public static T GetValue<T>(this Object obj, String fqname)
{
try
{
Object value = obj;
foreach (var prop in fqname.Split('.').Select(s => value.GetType().GetProperty(s)))
{
value = prop.GetValue(value, null);
}
if (value is T)
{
return (T)value;
}
else
{
// if the type requested is not the same as the stored, attempt a blind conversion
var converter = TypeDescriptor.GetConverter(typeof(T));
return (T)converter.ConvertFromInvariantString(value.ToString());
}
}
catch (NotSupportedException)
{
throw new InvalidCastException($"Cannot convert value to reuested type");
}
}
Now the problem is that this doesn't work for arrays, like:
var main_street = myObject.GetValue<String>("Addresses[0].StreetName")
Not even for the cases of arrays of arrays and so on...
I can start adding these conditions and special cases to my code but before that I figured, as C# already does this, maybe we could leverage some code parsing strategy, Roslyn, ... don't know, something that doesn't feel like reinventing the wheel and supports as many cases as possible.
Any ideas?
Right now I am working on a simple program, and this is a problem I've been thinking over many times. Many times I run my methods twice because of checking on the return value before running them, and I would like to know if there is a way I can prevent this, like using the returned value from the method I am checking against. It's quite hard to explain so here is a real life example from my program.
public class SFDBRepository
{
public static Domain.SF.SFObject GetSFOrder(string WorkOrd)
{
//As you can see here i'm checking on the output of this method, before trying to return it.
if (Domain.SF.SF.GetOrder(WorkOrd) != null)
{
//If the value is not null (My method returns null if no result), return the object
return Domain.SF.SF.GetOrder(WorkOrd);
}
//Same thing happens here. My method runs twice every time almost.
else if(Domain.Building_DeliveryPerformance.Building_DeliveryPerformance.GetObject(WorkOrd) != null)
{
return Domain.Building_DeliveryPerformance.Building_DeliveryPerformance.GetObject(WorkOrd);
}
else
{
return null;
}
}
}
You can simplify this down to the following code, which will only call those methods once and make the code much more readable:
public class ShopFloorDBRepository
{
public static Domain.ShopFloor.ShopFloorObject GetShopFloorOrder(string workOrd)
{
return Domain.ShopFloor.Shopfloor.GetOrder(workOrd) ??
Domain.DG9_DeliveryPerformance.DG9_DeliveryPerformance.GetObject(workOrd);
}
}
To explain why this works - the ?? operator (the null-coalescing operator!) basically says "if the returned value on the left hand side of the ?? is null, then return the value of the expression on the right hand side".
This way you only need to call your functions once.
public static Domain.ShopFloor.ShopFloorObject GetShopFloorOrder(string WorkOrd)
{
//As you can see here i'm checking on the output of this method, before trying to return it.
Domain.ShopFloor.ShopFloorObject wo = Domain.ShopFloor.Shopfloor.GetOrder(WorkOrd);
if (wo != null)
{
//If the value is not null (My method returns null if no result), return the object
return wo;
}
//Same thing happens here. My method runs twice every time almost.
Domain.ShopFloor.ShopFloorObject yowo = Domain.DG9_DeliveryPerformance.DG9_DeliveryPerformance.GetObject(WorkOrd);
if(yowo != null)
{
return yowo;
}
/* default return */
return null;
}
PS
You're kinda doing the "Factory Pattern"
See
http://www.dofactory.com/net/factory-method-design-pattern
Looks to me like you could be using a temporary variable to hold the result, which you can test and return.
public class ShopFloorDBRepository
{
public static Domain.ShopFloor.ShopFloorObject GetShopFloorOrder(string WorkOrd)
{
var result = Domain.ShopFloor.GetOrder(WorkOrd);
if (result != null) return result;
...
This is a common paradigm, especially when the method being called is expensive and/or has side effects you don't wish to incur twice.
Here, the "var" declaration sets the type of "result" to the type returned by the method being called; you could also use the name of the actual type.
If you wish to make two different kinds of tests like this, you'll need two different variables unless they have the same type (which in this case it appears they do).
An alternate mechanism that does require the full type, that you'll also see:
public static ShopFloorObject GetShopFloorOrder(string WorkOrd)
{
ShopFloorObject result;
if ( (result = Domain.ShopFloor.GetOrder(WorkOrd)) != null )
return result;
if ( (result = Domain.DG9_DeliveryPerformance.DG9_DeliveryPerformance.GetObject(WorkOrd)) != null)
return result;
return null;
Here you're explicitly declaring the type of the return value, then making the two calls you've indicated, testing the results against null, and returning the first non-null value.
I have an Outlook addin and am trying to avoid the world's largest switch. To get contact information, I am using items like: Microsoft.Office.Interop.Outlook.ContactItem.EmailAddress
Since there are many items, I would like to use a variable to select the item, like string myVariable="EmailAddress"; or string myVariable="FullName"; Then use ContactItem.myVariable. These items pulled from an xml file. I am drawing a blank and could use some help. Thank you.
I guess you could create an extension method that checks the properties, by using reflection.
Though keep in mind some of the remarks from your start post :)
using Outlook = Microsoft.Office.Interop.Outlook;
public static class ContactItemExtension
{
public static object Get(this Outlook.ContactItem item, string property)
{
object result = null;
if (item == null) {
// default null
return result;
}
var ctype = item.GetType();
var cprop = ctype.GetProperty(property);
if (cprop == null || cprop.GetGetMethod() == null) {
// no property found or it doesn't have a get
return result;
}
result = cprop.GetValue(item, null);
return result;
}
}
after which you can simple call, the Get method with the property name on top of any ContactItem
item.Get("EmailAddress1");
I have a function written in C# which return collection of business-entity (make) after checking and inserting in cache.
public static Collection<CProductMakesProps> GetCachedSmartPhoneMake(HttpContext context)
{
var allMake = context.Cache["SmartPhoneMake"] as Collection<CProductMakesProps>;
if (allMake == null)
{
context.Cache.Insert("SmartPhoneMake", new CModelRestrictionLogic().GetTopMakes(), null,
DateTime.Now.AddHours(Int32.Parse(ConfigurationManager.AppSettings["MakeCacheTime"])),
Cache.NoSlidingExpiration);
allMake = context.Cache["SmartPhoneMake"] as Collection<CProductMakesProps>;
}
return allMake;
}
I am using it in some other page as follows
var lobjprodMakeCol = CBrandCache.GetCachedSmartPhoneMake(Context);
//CBrandCache is the class which contain the method
Is it possible that I get null value in the lobjprodMakeCol
Thanks.
Edit
Note new CModelRestrictionLogic().GetTopMakes() is a function which fetches the records from database.
It will return a collection weather of count 0 or more.
There are few of possibilities when your function could return null - they are
GetTopMakes function it self returns null
The cache expiration time is zero (MakeCacheTime config entry has zero value)
The cast as Collection<CProductMakesProps> fails - possible if GetTopMakes
return some different type.
I would prefer below version which would not return null in all of above cases
var allMake = context.Cache["SmartPhoneMake"] as Collection<CProductMakesProps>;
if (allMake == null)
{
allMake = new CModelRestrictionLogic().GetTopMakes();
context.Cache.Insert("SmartPhoneMake", allMake,
null, DateTime.UtcNow.AddHours(Int32.Parse(
ConfigurationManager.AppSettings["MakeCacheTime"])), Cache.NoSlidingExpiration);
}
return allMake;
Also note that use of DateTime.UtcNow that would avoid any surprises such as day-light savings etc.
Yes it is possible, if the cast as Collection<CProductMakesProps> fails then a null will be assigned to allMake, so this depends heavily on what you are returning from new CModelRestrictionLogic().GetTopMakes().
Based on the assumption that most cache and/or dictionary collections will allow you to check for the presense of a specific key, I would suggest a slightly more streamlined way of writing this:
public static Collection<CProductMakesProps> GetCachedSmartPhoneMake(HttpContext context)
{
if (!context.Cache.ContainsKey("SmartPhoneMake") || context.Cache["SmartPhoneMake"] == null)
{
context.Cache.Insert("SmartPhoneMake"
, new CModelRestrictionLogic().GetTopMakes()
, null
, DateTime.Now.AddHours(Int32.Parse(ConfigurationManager.AppSettings["MakeCacheTime"]))
, Cache.NoSlidingExpiration);
}
return context.Cache["SmartPhoneMake"] as Collection<CProductMakesProps>;
}
I am writing tests against our Caching mechanism and I want to be sure that what goes into the cache is the same as what comes out, ie that all properties match. Here is a fictional example of how I would like it to work
[Test]
public void add_client_model_member_to_cache_then_retreve()
{
//arrange
MemcachedClient client = new MemcachedClient();
Member member = GetMember();
client.Store(StoreMode.Set, "membertest", member);
// act
var result = client.Get<Member>("membertest");
// assert
Assert.AreMatch(result, member);
}
It is not feasible to perform Assert.AreEqual on each property as there will be many of these tests with many properties in each.
Wow, thanks Jon. I think you answered that in under one minute. Here are my resulting solution for any interested parties. I implemented as Jon suggested (I think) however I got into a little trouble with array properties and as such my solution only handles arrays of ints (all I currently require).
It also got to be a fairly slippery slope if I try to check deeper than one level. I am sure this can be achieved however for my purposes it is not required.
private bool AreMatch(object initial, object result)
{
if (initial.Equals(result))
return true;
foreach (var property in initial.GetType().GetProperties())
{
var initialPropValue = property.GetValue(initial,null);
var resultPropValue = result.GetType().GetProperty(property.Name).GetValue(result,null);
if (property.PropertyType.IsArray)
{
if ((initialPropValue != null && resultPropValue != null) && !Enumerable.SequenceEqual((int[])initialPropValue, (int[])resultPropValue))
return false;
}
else if (!object.Equals(initialPropValue, resultPropValue))
{
return false;
}
else if (initialPropValue != null && property.PropertyType.IsClass)
{
// Don't go deeper than one level, this got me into trouble
//if (!AreMatch(initialPropValue, resultPropValue))
// return false;
}
}
return true;
}
The method above can be used in the following situations
[Test]
public void cached_result_is_same_as_original()
{
//arrange
Member member = GetMember();
client.Store(StoreMode.Set, "membertest", member);
// act
var result = client.Get<Member>("membertest");
// assert
Assert.IsTrue(AreMatch(member, result));
}
[Test]
public void cached_result_is_same_as_original()
{
//arrange
Member member = GetMember();
client.Store(StoreMode.Set, "membertest", member);
// act
var result = client.Get<Member>("membertest");
result.FirstName = "Result is different";
// assert
Assert.IsFalse(AreMatch(member, result));
}
Well, you could certainly write something to recurse through all the properties, and call object.Equals on the result of fetching the property value from the expected and actual ones. Use Type.GetProperties() to get the properties themselves, and PropertyInfo.GetValue to get the value.
It'll be somewhat crude, but you could always tweak it if necessary.