I have a OData service that returns a list of sharepoint lists.
I am unable to enumerate this list.
Is it possible to enumerate this to be able to loop through the lists in this Context in a foreach loop?
public class ODService
{
private DataContext _Context;
private NetworkCredential _nc;
public DataContext Context { get { return _Context; } }
/// <summary>
/// Constructor creates the context for access to sharepoint
/// </summary>
/// <param name="UserName">Service Account User Name</param>
/// <param name="Password">Service Account Password</param>
/// <param name="uri">Service uri</param>
public ODService(string UserName, string Password, string uri)
{
SetNetworkCredentials(UserName, Password);
SetContext(uri);
}
private void SetContext(string uri)
{
_Context = new DataContext(new Uri(uri));
_Context.Credentials = _nc;
_Context.MergeOption = MergeOption.NoTracking;
}
private void SetNetworkCredentials(string UserName, string Password)
{
_nc = new NetworkCredential(UserName, Password);
}
}
Got it:
public IList<string> EntityList()
{
IList<string> PropertyList = new List<string>();
var Properties = typeof(DataContext).GetProperties();
foreach (var property in Properties)
{
PropertyList.Add(property.Name);
}
return PropertyList;
}
Related
I am using cosmos DB and I have created below generic repository for CRUD operations.
public class CosmosDBRepository : ICosmosDBRepository
{
private readonly DocumentClient _client;
private readonly string DatabaseId = "FleetHub";
public CosmosDBRepository()
{
var endpoint = CloudConfigurationManager.GetSetting("CosmoDbEndpoint");
var authkey = CloudConfigurationManager.GetSetting("CosmoDbAuthKey");
try
{
if (endpoint == null || authkey == null)
{
throw new ArgumentNullException("CosmoDbEndpoint or CosmoDbAuthKey could not be found in the config file, check your settings.");
}
if (_client == null)
{
_client = new DocumentClient(new Uri(endpoint), authkey, connectionPolicy: new ConnectionPolicy { EnableEndpointDiscovery = false });
}
CreateDatabaseIfNotExistsAsync().Wait();
CreateCollectionIfNotExistsAsync().Wait();
}
catch (Exception e)
{
throw new Exception($"Initialise failed CosmoDbEndpoint {endpoint} or CosmoDbAuthKey {authkey} could not be found in the config file, check your settings. {e.Message}");
}
}
/// <summary>
///
/// </summary>
/// <param name="id"></param>
/// <param name="partitionkey"></param>
/// <returns></returns>
public async Task<T> GetItemAsync<T>(string id, string partitionkey) where T : class
{
try
{
string CollectionId = GetAttributeCosmoDbCollection<T>(typeof(T));
Document document = await _client.ReadDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), new RequestOptions { PartitionKey = new PartitionKey(partitionkey) });
return (T)(dynamic)document;
}
catch (DocumentClientException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return null;
}
else
{
throw;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
public async Task<Document> CreateItemAsync<T>(T item) where T : class
{
string CollectionId = GetAttributeCosmoDbCollection<T>(typeof(T));
return await _client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), item);
}
public async Task<IEnumerable<T>> GetItemsAsync<T>(Expression<Func<T, bool>> predicate, Expression<Func<T, object>> orderByDesc, int takeCount = -1)
where T : class
{
string CollectionId = GetAttributeCosmoDbCollection<T>(typeof(T));
var criteria = _client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), new FeedOptions { EnableCrossPartitionQuery = true })
.Where(predicate)
.OrderByDescending(orderByDesc)
.AsDocumentQuery();
IDocumentQuery<T> query = criteria;
List<T> results = new List<T>();
while (query.HasMoreResults)
{
if (takeCount > -1 && results.Count >= takeCount)
{
break;
}
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
/// <summary>
///
/// </summary>
/// <param name="id"></param>
/// <param name="item"></param>
/// <returns></returns>
public async Task<Document> UpdateItemAsync<T>(string id, T item) where T : class
{
string CollectionId = GetAttributeCosmoDbCollection<T>(typeof(T));
return await _client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), item);
}
#region private methods
private async Task CreateDatabaseIfNotExistsAsync()
{
try
{
await _client.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(DatabaseId));
}
catch (DocumentClientException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
await _client.CreateDatabaseAsync(new Database { Id = DatabaseId });
}
else
{
throw;
}
}
}
/// <summary>
///
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
private string GetPartitionKeyAttributeCosmoDbCollection(Type t)
{
// Get instance of the attribute.
CosmoDBCollection attribute =
(CosmoDBCollection)Attribute.GetCustomAttribute(t, typeof(CosmoDBCollection));
if (attribute == null)
{
throw new Exception("The attribute CosmoDbCollection was not found.");
}
return attribute.PartitionKey;
}
private string GetAttributeCosmoDbCollection<T>(Type t) where T : class
{
// Get instance of the attribute.
CosmoDBCollection attribute =
(CosmoDBCollection)Attribute.GetCustomAttribute(t, typeof(CosmoDBCollection));
if (attribute == null)
{
throw new Exception("The attribute CosmoDbCollection was not found.");
}
return attribute.Name;
}
#endregion
}
Here is CosmoDBCollection class:
public class CosmoDBCollection : Attribute
{
public string Name { get; set; }
public string PartitionKey { get; set; }
}
I am calling CreateCollectionIfNotExistsAsync().Wait(); in the constructor and this method required collectionId. How Can I pass CollectionId to this method? As this is the generic repository.
Do I need to create a Generic CreateCollectionIfNotExistsAsync() method?
If your collection name is based off <T>, why not simply have CosmosDBRepository<T> as the actual class. That way you can get the value also on the constructor.
Ideally it would also be a readonly private property that you only calculate once (on the constructor) and reuse on all operations to avoid paying the cost to construct it later on (since it doesn't change).
I have a dictionary in a singleton class. I am saving there the pair , every time I access to that dictionary from the method that is creating the token it shows all the credentials that I've stored there.
But when I access from another class in another project of the solutions it displays the dictionary empty. Can anybody tell me why this happens?
This is the class that manage the dictionary:
public class UserAccessToken
{
public Dictionary<string, string> UserDictionary { get; set; }
private static UserAccessToken _instance;
private UserAccessToken() { }
public static UserAccessToken Instance
{
get
{
if (_instance == null)
_instance = new UserAccessToken
{
UserDictionary = new Dictionary<string, string>()
};
return _instance;
}
}
}
This is the method that insert the key,value pair in the dictionary:
public override Task TokenEndpointResponse(OAuthTokenEndpointResponseContext context)
{
var accessToken = context.AccessToken;
if (context.Properties.Dictionary.ContainsKey("userName"))
{
var username = context.Properties.Dictionary["userName"];
// If I access here multiple times the singleton works
UserAccessToken.Instance.UserDictionary[username] = accessToken;
}
return Task.FromResult<object>(null);
}
This is the method where I access the dictionary, from here I can see that it's empty:
private bool IsTokenValid(HttpContextBase httpContext)
{
var username = httpContext.User.Identity.Name;
var userTokens = UserAccessToken.Instance.UserDictionary;
var tokenToAccess = httpContext.Request.Headers["Authorization"];
tokenToAccess = tokenToAccess.Replace("Bearer ", "");
if (userTokens.ContainsKey(username))
{
var token = userTokens[username];
if (token == tokenToAccess) return true;
}
return true;
}
I already solved my problem, but I'll let my solution here in case could be useful for somebody.
The problem is that if you are running two different projects, that will mean two different process, so, what I wanted to do is pretty impossible. I used Redis for this and it is working well.
This is an example of Redis use:
public class CallManagerCache : ICallManagerMethods{
private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer
.Connect(CloudConfigurationManager.GetSetting("RedisConnectionString")));
public static ConnectionMultiplexer cacheConnection
{
get
{
return lazyConnection.Value;
}
}
/// <summary>
/// Here you save the value in cache, you get the connection, then StringSetAsync is in charge of saving the value
/// </summary>
/// <param name="name"></param>
/// <param name="template"></param>
public async Task UpdateCallInstance(int id, byte[] data, bool instanceForCallback = false, TimeSpan? timespan = null)
{
var cache = cacheConnection.GetDatabase();
await cache.StringSetAsync(instanceForCallback ? $"Call_{id}" : id.ToString(), data, timespan ?? new TimeSpan(0, 0, 15, 0));
}
/// <summary>
/// Here you get the value in cache, you get the connection, then StringGetAsync is in charge of getting the value
/// </summary>
/// <param name="name"></param>
/// <param name="template"></param>
public async Task<CallInstance> GetById(int id, bool isForCallback)
{
var cache = cacheConnection.GetDatabase();
var callInstance = new CallInstance
{
Id = id,
InstanceData = await cache.StringGetAsync(isForCallback ? $"Call_{id}" : id.ToString())
};
return callInstance;
}
}
I am not able to get advantage from the pub/sub StackExchange.Redis features.
This is my code.
Console application
using StackExchange.Redis;
using System;
using System.Data;
namespace RedisHashSets
{
class Program
{
static void Main(string[] args)
{
#region Prepare Data
var exampleData = new DataTable();
exampleData.Columns.Add("session_guid", typeof(String));
exampleData.Columns.Add("status_code", typeof(String));
exampleData.Columns.Add("date_open", typeof(DateTime));
exampleData.Columns.Add("date_expiry", typeof(DateTime));
exampleData.Columns.Add("extra_id", typeof(long));
exampleData.Columns.Add("current_real_balance", typeof(Decimal));
DataRow row = exampleData.NewRow();
row[0] = Guid.NewGuid();
row[1] = 1;
row[2] = DateTime.Now;
row[3] = DateTime.Now.AddMinutes(30);
row[4] = 44567;
row[5] = 10.34;
exampleData.Rows.Add(row);
#endregion
var redisClient = RedisProvider.Instance;
string guid = Guid.NewGuid().ToString();
// Add an Entry
redisClient.Set(guid, RedisDataMapper.GetHashSet(exampleData)[0]);
// Modify an Entry
HashEntry entry = new HashEntry("exrta_id", 338349);
redisClient.Set(guid, entry);
// Get an Entry
HashSet val = redisClient.Get(guid);
//Invalidate the key
redisClient.redisPubSub.Publish("InvalidateBalances", guid);
}
}
}
Redis Singletone Client
namespace RedisHashSets
{
/// <summary>
/// Provide access to the Redis capabilites used to store sesion. Used as a Singleton
/// </summary>
public class RedisProvider
{
#region Private Fields
private readonly static Lazy<RedisProvider> lazyObj = new Lazy<RedisProvider>(() => new RedisProvider());
private ConnectionMultiplexer redisClient;
private IDatabase redisDatabase;
private IServer currentServer;
private EndPoint[] endPoints;
#endregion Private Fields
#region Public Fields
public static RedisProvider Instance { get { return lazyObj.Value; } }
public ISubscriber redisPubSub;
public bool IsRedisEnableByConfig { get; set; }
#endregion Public Fields
#region Public Methods
private RedisProvider()
{
if (!lazyObj.IsValueCreated)
{
var conf = RedisConfig.GetConfiguration();
if (conf is null)
{
IsRedisEnableByConfig = false;
}
else
{
IsRedisEnableByConfig = true;
redisClient = ConnectionMultiplexer.Connect(conf);
redisDatabase = redisClient.GetDatabase(0);
endPoints = redisClient.GetEndPoints();
currentServer = redisClient.GetServer(endPoints.First());
ManageSubscriber();
}
}
}
private void ManageSubscriber()
{
redisPubSub = redisClient.GetSubscriber();
redisPubSub.Subscribe(new RedisChannel("InvalidateBalances", RedisChannel.PatternMode.Pattern), (channel, message) => MessageAction(message));
}
private void MessageAction(RedisValue message)
{
HashEntry updatedEntry = new HashEntry(HashSet.IsUpdatedFlag, false);
Set(message, updatedEntry);
}
public void Set(string key, HashSet value)
{
redisDatabase.HashSet(key, value.Entries);
Set(key, value.IsUpdated);
}
public void Set(string key, HashEntry value)
{
redisDatabase.HashSet(key, value.Name, value.Value);
}
/// <summary>
/// Get a value from Redis.
/// </summary>
/// <param name="key">The key of the vaule to retrive</param>
/// <returns>Cast the value and retrurn a strongly typed object</returns>
public HashSet Get(string key)
{
HashEntry[] value = redisDatabase.HashGetAll(key);
return new HashSet(value.Length) { Entries = value };
}
/// <summary>
/// Check if a value is in stored or not expired. Time consuming. Use onky if necessary.
/// </summary>
/// <param name="key">The key of the value</param>
public bool IsInCache(string key)
{
return redisDatabase.KeyExists(key);
}
/// <summary>
/// Remove a value from the cache.
/// </summary>
/// <param name="key">The key to remove</param>
public bool Remove(string key)
{
return redisDatabase.KeyDelete(key);
}
/// <summary>
/// Return the number of active keys stored inside the redis databaase.
/// </summary>
public int GetKeysNumber()
{
var retVal = 0;
retVal = currentServer.Keys().Count();
return retVal;
}
/// <summary>
/// Return an enumerable of all the Keys.
/// </summary>
public IEnumerable<string> GetKeys()
{
return currentServer.Keys().Select(k => k.ToString());
}
#endregion
}
}
When I publish a message, it is not catched by the subscriber.
Can you help me?
Try to check you configuration.
Your code is working for me with the "localhost" as the connection string i.e
redisClient = ConnectionMultiplexer.Connect("localhost");
This is the minimal version of your code which is working for me in a console app.
using StackExchange.Redis;
using System;
using System.Linq;
using System.Net;
namespace RedisHashSets
{
class Program
{
static void Main(string[] args)
{
var redisClient = RedisProvider.Instance;
for (int i = 0; i < 10; i++)
{
//Thread.Sleep(100);
redisClient.redisPubSub.Publish("InvalidateBalances", Guid.NewGuid().ToString());
}
Console.ReadLine();
}
}
public class RedisProvider
{
#region Private Fields
private static readonly Lazy<RedisProvider> lazyObj = new Lazy<RedisProvider>(() => new RedisProvider());
private ConnectionMultiplexer redisClient;
private IDatabase redisDatabase;
private IServer currentServer;
private EndPoint[] endPoints;
public static RedisProvider Instance => lazyObj.Value;
public ISubscriber redisPubSub;
public bool IsRedisEnableByConfig { get; set; }
private RedisProvider()
{
if (!lazyObj.IsValueCreated)
{
IsRedisEnableByConfig = true;
redisClient = ConnectionMultiplexer.Connect("localhost");
endPoints = redisClient.GetEndPoints();
currentServer = redisClient.GetServer(endPoints.First());
ManageSubscriber();
}
}
private void ManageSubscriber()
{
redisPubSub = redisClient.GetSubscriber();
redisPubSub.Subscribe(new RedisChannel("InvalidateBalances", RedisChannel.PatternMode.Pattern), (channel, message) => MessageAction(message));
}
private void MessageAction(RedisValue message)
{
Console.WriteLine("msg arrived: " + message);
}
#endregion
}
}
Also please keep your code examples minimal complete and verifiable.
I am working on a project where I plan on using Redis as persistent data storage, however the task at hand, I am working on a generic Object cache. and as a huge fan of LINQ I have started designing a cache which does support this.
public ConcurrentBag<Object> Cache = new ConcurrentBag<object>();
public List<T> FindBy<T>(Func<T, bool> predicate) where T : class
{
var tmp = new List<T>();
foreach (var i in Cache)
{
try
{
T obj = i as T;
if (obj != null)
tmp.Add(obj);
}
catch { }
}
return tmp.Where(predicate).ToList();
}
I am afraid that when the object cache grows large it will become inefficient. (I estimate 500k-1m objects)
I was hoping that it would be possible to use something like this
public ConcurrentBag<Object> Cache = new ConcurrentBag<object>();
public List<T> FindBy<T>(Func<T, bool> predicate) where T : class
{
return Cache.Where<T>(predicate).ToList();
}
Hopefully I am not all off-track here? Any suggestions are welcome :)
Hash your genric type and save list of specific type..
Something like:
Dictionary<Type,List<T>>
Then get value by type key and query as you wanted
Since you estimate a lot of items in the cache and the operations on the cache will be type specific, you could use multiple bags wrapped into a dictionary. That would speed up finding the subset of the cache of type of interest and would be ideal if the cache contained many minimal subsets of different types.
readonly IDictionary<Type, ConcurrentBag<object>> _cache = new ConcurrentDictionary<Type, ConcurrentBag<object>>();
public List<T> FindBy<T>(Func<T, bool> predicate) where T : class
{
// Check if items of type {T} exist in the cache.
ConcurrentBag<object> bag;
if (_cache.TryGetValue(typeof (T), out bag))
{
// Cast, apply predicate and return.
return bag.Cast<T>().Where(predicate).ToList();
}
// Return an empty list.
return new List<T>();
}
Of course now you also need to handle adding items properly to the cache to ensure that different types will be put into their corresponding bags.
Big thanks to both Discosultan and user1190916 Whom both pointed me in the right direction in what I needed to get a CRUD cached-object repository with full LINQ support using Redis for persistent storage (Client ServiceStack.Redis), this is what I have managed to conjure up thus far.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Collections.Concurrent;
using ServiceStack.Redis;
namespace RedisTestRepo
{
class Program
{
//public static DataRepository Db;
static void Main(string[] args)
{
Repo r = new Repo();
// We do not touch sequence, by running example we can see that sequence will give Users new unique Id.
// Empty data store.
Console.WriteLine("Our User Data store should be empty.");
Console.WriteLine("Users In \"Database\" : {0}\n", r.All<User>().Count);
// Add imaginary users.
Console.WriteLine("Adding 100 imaginairy users.");
for (int i = 0; i < 99; i++)
r.Create<User>(new User { Id = r.Next<User>(), Name = "Joachim Nordvik" });
// We should have 100 users in data store.
Console.WriteLine("Users In \"Database\" : {0}\n", r.All<User>().Count);
// Lets print 10 users from data store.
Console.WriteLine("Order by Id, Take (10) and print users.");
foreach (var u in r.All<User>().OrderBy(z => z.Id).Take(10))
{
Console.WriteLine("ID:{0}, Name: {1}", u.Id, u.Name);
// Lets update an entity.
u.Name = "My new Name";
r.Update<User>(x=>x.Id == u.Id, u);
}
// Lets print 20 users from data store, we already edited 10 users.
Console.WriteLine("\nOrder by Id, Take (20) and print users, we previously edited the users that we printed lets see if it worked.");
foreach (var u in r.All<User>().OrderBy(z => z.Id).Take(20))
{
Console.WriteLine("ID:{0}, Name: {1}", u.Id, u.Name);
}
// Clean up data store.
Console.WriteLine("\nCleaning up Data Store.\n");
foreach (var u in r.All<User>())
r.Delete<User>(u);
// Confirm that we no longer have any users.
Console.WriteLine("Confirm that we no longer have User entities in Data Store.");
Console.WriteLine("Users In \"Database\" : {0}\n\n", r.All<User>().Count);
Console.WriteLine("Hit return to exit!");
Console.Read();
}
}
public class Repo
{
private static readonly PooledRedisClientManager m = new PooledRedisClientManager();
public Repo()
{
// Spool Redis Database into our object cache.
LoadIntoCache<User>();
}
readonly IDictionary<Type, List<object>> _cache = new ConcurrentDictionary<Type, List<object>>();
/// <summary>
/// Load {T} into object cache from Data Store.
/// </summary>
/// <typeparam name="T">class</typeparam>
private void LoadIntoCache<T>() where T : class
{
_cache[typeof(T)] = GetAll<T>().Cast<object>().ToList();
}
/// <summary>
/// Add single {T} into cache and Data Store.
/// </summary>
/// <typeparam name="T">class</typeparam>
/// <param name="entity">class object</param>
public void Create<T>(T entity) where T : class
{
List<object> list;
if (!_cache.TryGetValue(typeof(T), out list))
{
list = new List<object>();
}
list.Add(entity);
_cache[typeof(T)] = list;
Store<T>(entity);
}
/// <summary>
/// Delete single {T} from cache and Data Store.
/// </summary>
/// <typeparam name="T">class</typeparam>
/// <param name="entity">class object</param>
public void Delete<T>(T entity) where T : class
{
List<object> list;
if (_cache.TryGetValue(typeof(T), out list))
{
list.Remove(entity);
_cache[typeof(T)] = list;
RedisDelete<T>(entity);
}
}
/// <summary>
/// Tries to update or Add entity to object cache and Data Store.
/// </summary>
/// <typeparam name="T">class</typeparam>
/// <param name="predicate">linq expression</param>
/// <param name="entity">entity</param>
public void Update<T>(Func<T, bool> predicate, T entity) where T : class
{
List<object> list;
if (_cache.TryGetValue(typeof(T), out list))
{
// Look for old entity.
var e = list.Cast<T>().Where(predicate).FirstOrDefault();
if(e != null)
{
list.Remove(e);
}
// Regardless if object existed or not we add it to our Cache / Data Store.
list.Add(entity);
_cache[typeof(T)] = list;
Store<T>(entity);
}
}
/// <summary>
/// Find List<T>(predicate) in cache.
/// </summary>
/// <typeparam name="T">class</typeparam>
/// <param name="predicate">linq statement</param>
/// <returns></returns>
public List<T> FindBy<T>(Func<T, bool> predicate) where T : class
{
List<object> list;
if (_cache.TryGetValue(typeof(T), out list))
{
return list.Cast<T>().Where(predicate).ToList();
}
return new List<T>();
}
/// <summary>
/// Find All {T}
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns>List<T></returns>
public List<T> All<T>() where T : class
{
return GetAll<T>().ToList();
}
/// <summary>
/// Find Single {T} in object cache.
/// </summary>
/// <typeparam name="T">class</typeparam>
/// <param name="predicate">linq statement</param>
/// <returns></returns>
public T Read<T>(Func<T, bool> predicate) where T : class
{
List<object> list;
if (_cache.TryGetValue(typeof(T), out list))
{
return list.Cast<T>().Where(predicate).FirstOrDefault();
}
return null;
}
public long Next<T>() where T : class
{
long id = 1;
using (var ctx = m.GetClient())
{
try
{
id = ctx.As<T>().GetNextSequence();
}
catch(Exception ex)
{
// Add exception handler.
}
}
return id;
}
private void RedisDelete<T>(T entity) where T : class
{
using (var ctx = m.GetClient())
ctx.As<T>().Delete(entity);
}
private T Find<T>(long id) where T : class
{
using (var ctx = m.GetClient())
return ctx.As<T>().GetById(id);
}
private IList<T> GetAll<T>() where T : class
{
using(var ctx = m.GetClient())
{
try
{
return ctx.As<T>().GetAll();
}
catch
{
return new List<T>();
}
}
}
private void Store<T>(T entity) where T : class
{
using (var ctx = m.GetClient())
ctx.Store<T>(entity);
}
}
public class User
{
public long Id { get; set; }
public string Name { get; set; }
}
}
I have recently updated PostSharp to v4.1.13 and I have started receiving this error when I try to build my solution:
The custom attribute 'True.Kentico.Caching.KenticoCacheAttribute' constructor threw the exception EntryPointNotFoundException: Entry point was not found.
This attribute implements caching, and has worked before, so I'm wondering what would be the cause of it breaking. I am using this attribute in other assemblies referencing the aspect's containing assembly, and I get that error in there as well.
The following is the code for the attribute. Apologies, it contains a number of constructors.
[Serializable]
public class KenticoCacheAttribute : MethodInterceptionAspect
{
public int CacheMinutes { get; set; }
/// <summary>
/// the string value of the cache dependency key. If it uses parameters from the method, include a {0} to format with the method parameter
/// </summary>
public string CacheDependency { get; set; }
public string[] CacheDependencyStrings { get; set; }
public KenticoCacheDependencyObtainFrom ObtainCacheDependencyFrom { get; set; }
/// <summary>
/// whether caching is enabled - default from app settings
/// </summary>
public bool CacheEnabled { get; set; }
/// <summary>
/// this is the index of the parameter that will be used to format the cachedependency, if required
/// </summary>
public int CacheDependencyParameterIndex { get; set; }
public string CacheDependencyObjectProperty { get; set; }
private string _methodName;
/// <summary>
/// initializes the cache attribute to use a static dependency
/// </summary>
/// <param name="cacheDependency"></param>
public KenticoCacheAttribute(string cacheDependency)
{
CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
CacheDependencyParameterIndex = -1;
CacheDependency = cacheDependency;
if (String.IsNullOrEmpty(cacheDependency))
ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.NoDependency;
else
{
ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.Static;
}
}
/// <summary>
/// initializes the cache attribute to use a static dependency using multiple depndencies
/// </summary>
/// <param name="cacheDependencystrings">an array of strings</param>
public KenticoCacheAttribute(string[] cacheDependencystrings)
{
CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
CacheDependencyParameterIndex = -1;
CacheDependencyStrings = cacheDependencystrings;
ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.MultipleDependenciesStatic;
}
/// <summary>
/// initializes the cache attribute to take the dependency from the input parameter
/// </summary>
/// <param name="cacheDependency">the static string to be used for the cache dependency. you may include {CurrentSiteName} to be replaced with the current site name, and {0} to be replaced with the value passed in as one of the method parameters</param>
/// <param name="cacheDependencyParameterIndex">the index of the parameter in the method parameters that will be used to create the cache dependency key</param>
public KenticoCacheAttribute(string cacheDependency, int cacheDependencyParameterIndex)
{
CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
CacheDependencyParameterIndex = cacheDependencyParameterIndex;
CacheDependency = cacheDependency;
if (String.IsNullOrEmpty(cacheDependency))
{
ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.MultipleDependenciesFromParameter;
}
else
{
ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.FromMethodParameter;
}
}
/// <summary>
/// initialize the cache attribute to obtain the cache dependency from the named property of the return object
/// </summary>
/// <param name="cacheDependency">the static string to be used for the cache dependency. you may include {CurrentSiteName} to be replaced with the current site name, and {0} to be replaced with the value in the named parameter </param>
/// <param name="cacheDependencyObjectPropertyName">the name of the property of the return object that will be used to replace the placeholder in the static string to build the cache dependency string</param>
public KenticoCacheAttribute(string cacheDependency, string cacheDependencyObjectPropertyName)
{
CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
CacheDependencyParameterIndex = -1;
CacheDependency = cacheDependency;
ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.FromReturnObject;
CacheDependencyObjectProperty = cacheDependencyObjectPropertyName;
}
/// <summary>
/// initialize the cache attribute to obtain the cache dependency from the named property of the return object
/// </summary>
/// <param name="cacheDependency">the static string to be used for the cache dependency. you may include {CurrentSiteName} to be replaced with the current site name, and {0} to be replaced with the value in the named parameter </param>
/// <param name="cacheDependencyParameterIndex"></param>
/// <param name="cacheDependencyObjectPropertyName">the name of the property of the return object that will be used to replace the placeholder in the static string to build the cache dependency string</param>
public KenticoCacheAttribute(string cacheDependency, int cacheDependencyParameterIndex, string cacheDependencyObjectPropertyName)
{
CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
CacheDependencyParameterIndex = cacheDependencyParameterIndex;
CacheDependency = cacheDependency;
ObtainCacheDependencyFrom = KenticoCacheDependencyObtainFrom.FromMethodParameterObjectProperty;
CacheDependencyObjectProperty = cacheDependencyObjectPropertyName;
}
public override void CompileTimeInitialize(MethodBase method, AspectInfo aspectInfo)
{
_methodName = method.Name;
}
public override void OnInvoke(MethodInterceptionArgs args)
{
var methodInfo = args.Method as MethodInfo;
if (methodInfo != null && (methodInfo.ReturnType != typeof(void) && CacheEnabled))
{
var cacheKey = BuildCacheKey(args.Arguments);
var cacheSettings = new CacheSettings(CacheMinutes, cacheKey);
var data = CacheHelper.Cache(cs => GetData(cs, args), cacheSettings);
args.ReturnValue = data;
}
else
base.OnInvoke(args);
}
private object GetData(CacheSettings cs, MethodInterceptionArgs args)
{
var data = args.Invoke(args.Arguments);
// Checks whether data was loaded and whether the data should be cached (based on the CacheSettings)
if ((data != null) && cs.Cached)
{
// Sets a cache dependency for the data
// The data is removed from the cache if the objects represented by the dummy key are modified (all user objects in this case)
var dependencyResolver = CacheDependencyFactory.GetDependecyFormatter(ObtainCacheDependencyFrom);
var dependencyString = dependencyResolver.Format(new CacheDependencyFormatParameters()
{
CacheDependencyBase = CacheDependency,
CacheDependencybaseString = CacheDependencyStrings,
InputParameterIndex = CacheDependencyParameterIndex,
ReturnParameterName = CacheDependencyObjectProperty,
InputParameterData = args,
ReturnParameterData = data
});
cs.CacheDependency = CacheHelper.GetCacheDependency(dependencyString);
}
return data;
}
private string BuildCacheKey(Arguments arguments)
{
var sb = new StringBuilder();
sb.Append(_methodName);
foreach (var argument in arguments.ToArray())
{
sb.Append(argument == null ? "_" : argument.ToString());
}
sb.Append(String.Format("{0}_{1}", SiteContext.CurrentSiteName,
SiteContext.CurrentSite.DefaultVisitorCulture));
return sb.ToString();
}
}
Any help would be appreciated!
So it turns out that the issue is with these lines of code in the constructors:
CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
When PostSharp runs, The dependencyInjection framework has not yet been initialised, so the execution fails. It's strange that it was working before, but hey. I moved this code into the OnInvoke method, which should only run when the DI Enginecontext has been initialised :
public override void OnInvoke(MethodInterceptionArgs args)
{
CacheMinutes = EngineContext.Current.Resolve<AppSettings>().Cache.TTL.GlobalSetting;
CacheEnabled = !EngineContext.Current.Resolve<AppSettings>().Cache.IgnoreCache;
var methodInfo = args.Method as MethodInfo;
......