Related
I have a project in Asp.Net Core. This project has a ICacheService as below:
public interface ICacheService
{
T Get<T>(string key);
T Get<T>(string key, Func<T> getdata);
Task<T> Get<T>(string key, Func<Task<T>> getdata);
void AddOrUpdate(string key, object value);
}
The implementation is simply based on ConcurrentDictionary<string, object>, so its not that complicated, just storing and retrieving data from this dictionary. At one of my services I have a method as below:
public async Task<List<LanguageInfoModel>> GetLanguagesAsync(string frontendId, string languageId, string accessId)
{
async Task<List<LanguageInfoModel>> GetLanguageInfoModel()
{
var data = await _commonServiceProxy.GetLanguages(frontendId, languageId, accessId);
return data;
}
_scheduler.ScheduleAsync($"{CacheKeys.Jobs.LanguagesJob}_{frontendId}_{languageId}_{accessId}", async () =>
{
_cacheService.AddOrUpdate($"{CacheKeys.Languages}_{frontendId}_{languageId}_{accessId}", await GetLanguageInfoModel());
return JobStatus.Success;
}, TimeSpan.FromMinutes(5.0));
return await _cacheService.Get($"{CacheKeys.Languages}_{frontendId}_{languageId}_{accessId}", async () => await GetLanguageInfoModel());
}
The problem is that I have three params in this method that I use as a cache key. This works fine but the problem is that the combination of three params is pretty high so there will be so many duplication of objects in cache. I was thinking to create a cache without duplication like below:
To have a cache with a list as a key where I can store more than one key for one object. So when I get new elements I will check for each of them if it is in the cache, if it is in the cache I will only add a key in the key list otherwise insert a new element in the cache. The problem here is that testing if an object is in the cache is a big problem. I think it will consume a lot of resources and would need some serialization into a specific form to make the comparison possible which will make again the comparison consuming a lot of resources.
The cache might look something like this CustomDictionary<List<string>, object>
Does anybody know a good approach of solving this issue to not duplicate objects in the cache ?
EDIT 1:
My main concern is when I retrieve List<MyModel> from my webservices because they might have 80% of the objects with the same data which will drastically increase the size in memory. But this would be relevant for simple cases as well.
Lest suppose I have something like this:
MyClass o1 = new MyObject();
_cache.Set("key1", o1);
_cashe.Set("key2", o1);
In this case when trying to add the same object twice I would like to not duplicate it but to have key2 somehow pointing to the same object as key1. If this achieved it will be problem to invalidate them but I expect to have something like this:
_cache.Invalidate("key2");
This will check if there is another key pointing to same object. If so, it will only remove the key otherwise destroy the object itself.
Maybe we could reformulate this problem to two separate issues ...
executing the call for each combination and
storing n times the identical result, wasting tons of memory
For 1 I don't have any idea how we could prevent it, as we do not know prior to execution if we will fetch a duplicate in this setup. We would need more information that is based on when these values vary, which may or may not be possible.
For 2 one solution would be to override hashcode so it is based on the actual returned values. A good solution would be generic and walk through the object tree (which probably can be expensive). Would like to know if there are any pre-made solutions for this actually.
This answer is specifically for returning List<TItem>s, rather than just individual TItems, and it avoids duplication of any TItem as well as any List<T>. It uses arrays, because you're trying to save memory, and arrays will use less than a List.
Note that for this (and any solution really) to work, you MUST override Equals and GetHashCode on TItem, so that it knows what a duplicate item is. (Unless the data provider is returning the same object each time, which is unlikely.) If you don't have control of TItem, but you can yourself determine whether two TItems are equal, you can use an IEqualityComparer to do this, but the below solution would need to be modified very slightly in order to do that.
View the solution with a basic test at:
https://dotnetfiddle.net/pKHLQP
public class DuplicateFreeCache<TKey, TItem> where TItem : class
{
private ConcurrentDictionary<TKey, int> Primary { get; } = new ConcurrentDictionary<TKey, int>();
private List<TItem> ItemList { get; } = new List<TItem>();
private List<TItem[]> ListList { get; } = new List<TItem[]>();
private Dictionary<TItem, int> ItemDict { get; } = new Dictionary<TItem, int>();
private Dictionary<IntArray, int> ListDict { get; } = new Dictionary<IntArray, int>();
public IReadOnlyList<TItem> GetOrAdd(TKey key, Func<TKey, IEnumerable<TItem>> getFunc)
{
int index = Primary.GetOrAdd(key, k =>
{
var rawList = getFunc(k);
lock (Primary)
{
int[] itemListByIndex = rawList.Select(item =>
{
if (!ItemDict.TryGetValue(item, out int itemIndex))
{
itemIndex = ItemList.Count;
ItemList.Add(item);
ItemDict[item] = itemIndex;
}
return itemIndex;
}).ToArray();
var intArray = new IntArray(itemListByIndex);
if (!ListDict.TryGetValue(intArray, out int listIndex))
{
lock (ListList)
{
listIndex = ListList.Count;
ListList.Add(itemListByIndex.Select(ii => ItemList[ii]).ToArray());
}
ListDict[intArray] = listIndex;
}
return listIndex;
}
});
lock (ListList)
{
return ListList[index];
}
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"A cache with:");
sb.AppendLine($"{ItemList.Count} unique Items;");
sb.AppendLine($"{ListList.Count} unique lists of Items;");
sb.AppendLine($"{Primary.Count} primary dictionary items;");
sb.AppendLine($"{ItemDict.Count} item dictionary items;");
sb.AppendLine($"{ListDict.Count} list dictionary items;");
return sb.ToString();
}
//We have this to make Dictionary lookups on int[] find identical arrays.
//One could also just make an IEqualityComparer, but I felt like doing it this way.
public class IntArray
{
private readonly int _hashCode;
public int[] Array { get; }
public IntArray(int[] arr)
{
Array = arr;
unchecked
{
_hashCode = 0;
for (int i = 0; i < arr.Length; i++)
_hashCode = (_hashCode * 397) ^ arr[i];
}
}
protected bool Equals(IntArray other)
{
return Array.SequenceEqual(other.Array);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((IntArray)obj);
}
public override int GetHashCode() => _hashCode;
}
}
It occurred to me that a ReaderWriterLockSlim would be better than the lock(ListList), if the lock is causing performance to lag, but it's very slightly more complicated.
Similar to #MineR, this solution is performing a 'double caching' operation: it caches the key'ed lists (lookups) as well as the individual objects - performing an automatic deduplication.
It is a fairly simple solution using two ConcurrentDictionaries - one acting as a HashSet and one as a keyed lookup. This allows most of the threading concerns to be handled by the framework.
You can also pass in and share the hashset between multiple Cachedlookups allowing lookups with different keys.
Note that object equality or an IEqualityComparer are required to make any such solution function.
Class:
public class CachedLookup<T, TKey>
{
private readonly ConcurrentDictionary<T, T> _hashSet;
private readonly ConcurrentDictionary<TKey, List<T>> _lookup = new ConcurrentDictionary<TKey, List<T>>();
public CachedLookup(ConcurrentDictionary<T, T> hashSet)
{
_hashSet = hashSet;
}
public CachedLookup(IEqualityComparer<T> equalityComparer = default)
{
_hashSet = equalityComparer is null ? new ConcurrentDictionary<T, T>() : new ConcurrentDictionary<T, T>(equalityComparer);
}
public List<T> Get(TKey key) => _lookup.ContainsKey(key) ? _lookup[key] : null;
public List<T> Get(TKey key, Func<TKey, List<T>> getData)
{
if (_lookup.ContainsKey(key))
return _lookup[key];
var result = DedupeAndCache(getData(key));
_lookup.TryAdd(key, result);
return result;
}
public async ValueTask<List<T>> GetAsync(TKey key, Func<TKey, Task<List<T>>> getData)
{
if (_lookup.ContainsKey(key))
return _lookup[key];
var result = DedupeAndCache(await getData(key));
_lookup.TryAdd(key, result);
return result;
}
public void Add(T value) => _hashSet.TryAdd(value, value);
public List<T> AddOrUpdate(TKey key, List<T> data)
{
var deduped = DedupeAndCache(data);
_lookup.AddOrUpdate(key, deduped, (k,l)=>deduped);
return deduped;
}
private List<T> DedupeAndCache(IEnumerable<T> input) => input.Select(v => _hashSet.GetOrAdd(v,v)).ToList();
}
Example Usage:
public class ExampleUsage
{
private readonly CachedLookup<LanguageInfoModel, (string frontendId, string languageId, string accessId)> _lookup
= new CachedLookup<LanguageInfoModel, (string frontendId, string languageId, string accessId)>(new LanguageInfoModelComparer());
public ValueTask<List<LanguageInfoModel>> GetLanguagesAsync(string frontendId, string languageId, string accessId)
{
return _lookup.GetAsync((frontendId, languageId, accessId), GetLanguagesFromDB(k));
}
private async Task<List<LanguageInfoModel>> GetLanguagesFromDB((string frontendId, string languageId, string accessId) key) => throw new NotImplementedException();
}
public class LanguageInfoModel
{
public string FrontendId { get; set; }
public string LanguageId { get; set; }
public string AccessId { get; set; }
public string SomeOtherUniqueValue { get; set; }
}
public class LanguageInfoModelComparer : IEqualityComparer<LanguageInfoModel>
{
public bool Equals(LanguageInfoModel x, LanguageInfoModel y)
{
return (x?.FrontendId, x?.AccessId, x?.LanguageId, x?.SomeOtherUniqueValue)
.Equals((y?.FrontendId, y?.AccessId, y?.LanguageId, y?.SomeOtherUniqueValue));
}
public int GetHashCode(LanguageInfoModel obj) =>
(obj.FrontendId, obj.LanguageId, obj.AccessId, obj.SomeOtherUniqueValue).GetHashCode();
}
Notes:
The CachedLookup class is generic on both the value and key. The example use of ValueTuple makes it easy to have compound keys. I have also used ValueTuples to simplify the equality comparisons.
This usage of ValueTask fits nicely with its intended purpose, returning the cached list synchronously.
If you have access to the lower level data access layer, one optimization would be to move the deduplication to happen before the objects are instantiated (based on property value equality). This would reduce the allocations and load on the GC.
If you have control over your complete solution then you can do something like this.
Whatever object that is capable of storing in Cache. You have to identify that.
All Such object implement common interface.
public interface ICacheable
{
string ObjectId(); // This will implement logic to calculate each object identity. You can count hash code but you have to add some other value to.
}
Now when you store object in Cache. You do two thing.
Store Two way things. Like one cache store ObjectId to Key.
Another will contains ObjectId to Object.
Overall idea is that when you get object. You search in first cache and see that the key you want is there against ObjectId. If yes then no further action otherwise you have to create new entry in First Cache for ObjectId to Key Map.
If object is not present then you have to create entry in both cache
Note : You have to overcome performance issue. Because your keys is some kind of list so it create problem while searching.
It sound to me as though you need to implement some sort of index. Assuming that your model is fairly large, which is why you want to save memory then you could do this with two concurrent dictionaries.
The first would be ConcurrentDictionary<string, int> (or whatever unique id applies to your model object) and would contain your key values. Each key is obviously be different as per all your combinations, but you are only duplicating the int unique key for all of your objects, not the entire object.
The second dictionary would be a ConcurrentDictionary<int, object> or ConcurrentDictionary<int, T> and would contain your unique large objects indexed via their unique key.
When building the cache you would need to populate both dictionaries, the exact method would depend upon how you are doing it at the moment.
To retrieve an object you would build the key as you do at the moment, retrieve the hashcode value from the first dictionary, and then use that to locate the actual object from the second dictionary.
It is also possible to invalidate one key without invalidating the main object another key is also using it, although it does require you to iterate over the index dictionary to check if any other key is pointing to the same object.
I think this is not a caching concern where one key map to one and only one data. Yours is not in this case. You are trying to manipulate a local data repository in memory work as cached data.
You are trying to create mappers between keys and objects that loaded from remote. One key is able to map to many objects. One object can be mapped by many Keys, so the relationship is n <======> n
I have created a sample modal as following
Key, KeyMyModel and MyModel are classes for caching handler
RemoteModel is class that you got from remote service
With this models, you are able to meet the requirements. This utilizes entity Id to specify an object, does not need to hash to specify duplications. This is very basic that I have implemented set method. Invaildate a key is very similar. You must write code that ensure thread safe as well
public class MyModel
{
public RemoteModel RemoteModel { get; set; }
public List<KeyMyModel> KeyMyModels { get; set; }
}
public class RemoteModel
{
public string Id { get; set; } // Identity property this get from remote service
public string DummyProperty { get; set; } // Some properties returned by remote service
}
public class KeyMyModel
{
public string Key { get; set; }
public string MyModelId { get; set; }
}
public class Key
{
public string KeyStr { get; set; }
public List<KeyMyModel> KeyMyModels { get; set; }
}
public interface ICacheService
{
List<RemoteModel> Get(string key);
List<RemoteModel> Get(string key, Func<List<RemoteModel>> getdata);
Task<List<RemoteModel>> Get(string key, Func<Task<List<RemoteModel>>> getdata);
void AddOrUpdate(string key, object value);
}
public class CacheService : ICacheService
{
public List<MyModel> MyModels { get; private set; }
public List<Key> Keys { get; private set; }
public List<KeyMyModel> KeyMyModels { get; private set; }
public CacheService()
{
MyModels = new List<MyModel>();
Keys = new List<Key>();
KeyMyModels = new List<KeyMyModel>();
}
public List<RemoteModel> Get(string key)
{
return MyModels.Where(s => s.KeyMyModels.Any(t => t.Key == key)).Select(s => s.RemoteModel).ToList();
}
public List<RemoteModel> Get(string key, Func<List<RemoteModel>> getdata)
{
var remoteData = getdata();
Set(key, remoteData);
return MyModels.Where(s => s.KeyMyModels.Any(t => t.Key == key)).Select(t => t.RemoteModel).ToList();
}
public Task<List<RemoteModel>> Get(string key, Func<Task<List<RemoteModel>>> getdata)
{
throw new NotImplementedException();
}
public void AddOrUpdate(string key, object value)
{
throw new NotImplementedException();
}
public void Invalidate(string key)
{
}
public void Set(string key, List<RemoteModel> data)
{
var Key = Keys.FirstOrDefault(s => s.KeyStr == key) ?? new Key()
{
KeyStr = key
};
foreach (var remoteModel in data)
{
var exist = MyModels.FirstOrDefault(s => s.RemoteModel.Id == remoteModel.Id);
if (exist == null)
{
// add data to the cache
var myModel = new MyModel()
{
RemoteModel = remoteModel
};
var keyMyModel = new KeyMyModel()
{
Key = key,
MyModelId = remoteModel.Id
};
myModel.KeyMyModels.Add(keyMyModel);
Key.KeyMyModels.Add(keyMyModel);
Keys.Add(Key);
}
else
{
exist.RemoteModel = remoteModel;
var existKeyMyModel =
KeyMyModels.FirstOrDefault(s => s.Key == key && s.MyModelId == exist.RemoteModel.Id);
if (existKeyMyModel == null)
{
existKeyMyModel = new KeyMyModel()
{
Key = key,
MyModelId = exist.RemoteModel.Id
};
Key.KeyMyModels.Add(existKeyMyModel);
exist.KeyMyModels.Add(existKeyMyModel);
KeyMyModels.Add(existKeyMyModel);
}
}
}
// Remove MyModels if need
var remoteIds = data.Select(s => s.Id);
var currentIds = KeyMyModels.Where(s => s.Key == key).Select(s => s.MyModelId);
var removingIds = currentIds.Except(remoteIds);
var removingKeyMyModels = KeyMyModels.Where(s => s.Key == key && removingIds.Any(i => i == s.MyModelId)).ToList();
removingKeyMyModels.ForEach(s =>
{
KeyMyModels.Remove(s);
Key.KeyMyModels.Remove(s);
});
}
}
class CacheConsumer
{
private readonly CacheService _cacheService = new CacheService();
public List<RemoteModel> GetMyModels(string frontendId, string languageId, string accessId)
{
var key = $"{frontendId}_{languageId}_{accessId}";
return _cacheService.Get(key, () =>
{
// call to remote service here
return new List<RemoteModel>();
});
}
}
im currently expanding my knowledge a little, and wanted to Create a little game for myself.
The Structure is as Following:
Programm.cs creates an instance of Gamecontroller. This Gamecontroller is the lowest level i want to Access. It will create instaces of the Views, and from classes like config.
I want to implement an debug Console with Command Input. These Commands should always start at the Gamecontroller level, and should be able to interact with kinda everything i could do with C# code.
So i want to access the Objects, Member and methods withing Gamecontroller, or Within any nested object.
Currently i cant get to the Properties of an Child, because _member returns an "Type" which gets parsed to RuntimeProperty instead of the Class
Example on Parsing:
"objPlayer.name" > "GameController.objPlayer.name"
"objConfig.someSetting = 10" > "GameController.objConfig.someSetting=10"
"objConfig.functionCall()" > "GameController.objConfig.functionCall()"
"objConfig.objPlayer.setName("someName")" > "GameController.objConfig.objPlayer.setName("someName")"
"objPlayer.name" > "GameController.objPlayer.name"
this is what i got so far:
private void parseComamnd(string Command)
{
var actions = Command.Split('.');
var start = this.GetType();
var last = actions[actions.Length - 1];
foreach (var action in actions)
{
if (action.Contains("(") && action.Contains(")"))
{
_exec(start, action);
}
else
{
start = _member(start, action);
}
}
}
private Type _member(Type pHandle, string pStrMember)
{
return pHandle.GetProperty(pStrMember).GetType();
}
private void _exec(Type pHandle, string pStrFunction)
{
var Regex = new Regex(#"\(|,|\)");
var FunctionParams = Regex.Split(pStrFunction);
var FunctionName = FunctionParams[0];
FunctionParams[0] = "";
FunctionParams = FunctionParams.Where(val => val != "").ToArray();
pHandle.GetMethod(FunctionName).Invoke(FunctionName, FunctionParams);
}
If I understood right, you want to match some string commands with actions you want to perform. In this case you could use Dictionary as a storage for string-delgate couples to match your string commands to actions you want to perform. As an advantage of this approach, you can change matched couples during program runtime as you wish
class SomeClass
{
delegate void OperationDelegate(string value);
IDictionary<string, OperationDelegate> Operations = new Dictionary<string, OperationDelegate>();
public SomeClass()
{
Operations.Add("objPlayer.name", SetName);
Operations.Add("objConfig.someSetting", SetSetting);
}
public void HandleNewValue(string command, string value)
{
try
{
if (Operations.ContainsKey(command))
Operations[command](value);
}
catch (Exception e)
{
Logger.Error(e);
}
}
private void SetName(string value)
{
// Some logic there
}
private void SetSetting(string value)
{
// Some logic there
}
}
Say I have the following (simplified):
public class Item
{
public String Name { get; set; }
public String Type { get; set; }
}
public class Armor : Item
{
public int AC { get; set; }
public Armor () { Type = "Armor"; }
}
public class Weapon : Item
{
public int Damage { get; set; }
public Armor () { Type = "Weapon"; }
}
public class Actor
{
...
}
public class HasItem : Relationship<ItemProps>, IRelationshipAllowingSourceNode<Actor>, IRelationshipAllowingTargetNode<Item>
{
public readonly string TypeKey = "HasItem";
public HasItem ( NodeReference targetItem, int count = 1 )
: base(targetItem, new ItemProps { Count = count })
{
}
public override string RelationshipTypeKey
{
get { return TypeKey; }
}
}
With this setup I can easily create a heterogeneous list of Weapons, Armor, etc related to the Actor. But I can't seem to figure out how to get them out. I have this method (again simplified) to get a list of all the related items, but it gets them all out as Items. I can't figure out how to get them as their actual type. I can use the Type field to determine the type, but there doesn't seem to be anyway of dynamically building the return:
public IEnumerable<Item> Items
{
get
{
return
GameNode
.GraphClient
.Cypher
.Start(new { a = Node.ByIndexLookup("node_auto_index", "Name", Name) })
.Match("(a)-[r:HasItem]-(i)")
.Return<Item>("i") // Need something here to return Armor, Weapon, etc as needed based on the Type property
.Results;
}
}
I found a bad workaround where I return the Type and NodeID and run the list through a switch statement that does a .Get with the NodeID and casts it to the right type. but this is inflexible and inefficient. I could run one query for each derived class and concatenate them together, but the thought of that makes my skin crawl.
This seems like it would be a common problem, but I couldn't find anything online. Any ideas?
The problem is how the data is stored in Neo4J, and serialized back via Json.net.
Let's say I have a sword:
var sword = new Weapon{
Name = "Sword 12.32.rc1",
Type = "Sword"
Damage = 12
};
If I serialize this to neo4j: graphClient.Create(sword); all is fine, internally we now have a Json representation which will look something like this:
{ "Name" : "Sword 12.32.rc1", "Type": "Sword", "Damage": "12"}
There is no information here that the computer can use to derive that this is in fact of type 'Sword', so if you bring back a collection of type Item it can only bring back the two properties Name and Type.
So, there are two solutions that I can think of, neither one of which is great, but both do get you with a one query solution. The first (most sucky) is to create a 'SuperItem' which has all the properties from the derived classes together, so:
public class SuperItem { Name, Type, Damage, AC } //ETC
But that is horrible, and kind of makes having a hierarchy pointless. The 2nd option, which whilst not great is better - is to use a Dictionary to get the data:
var query = GraphClient
.Cypher
.Start(new {n = actorRef})
.Match("n-[:HasItem]->item")
.Return(
item => new
{
Item = item.CollectAs<Dictionary<string,string>>()
});
var results = query.Results.ToList();
Which if you run:
foreach (var data in results2.SelectMany(item => item.Item, (item, node) => new {item, node}).SelectMany(#t => #t.node.Data))
Console.WriteLine("Key: {0}, Value: {1}", data.Key, data.Value);
Would print out:
Key: Type, Value: Sword
Key: Damage, Value: 12
Key: Name, Value: 12.32.rc1
So, now we have a dictionary of the properties, we can create an extension class to parse it:
public static class DictionaryExtensions
{
public static Item GetItem(this Dictionary<string, string> dictionary)
{
var type = dictionary.GetTypeOfItem().ToLowerInvariant();
var json = dictionary.ToJson();
switch (type)
{
case "sword":
return GetItem<Weapon>(json);
case "armor":
return GetItem<Armor>(json);
default:
throw new ArgumentOutOfRangeException("dictionary", type, string.Format("Unknown type: {0}", type));
}
}
private static string GetTypeOfItem(this Dictionary<string, string> dictionary)
{
if(!dictionary.ContainsKey("Type"))
throw new ArgumentException("Not valid type!");
return dictionary["Type"];
}
private static string ToJson(this Dictionary<string, string> dictionary)
{
var output = new StringBuilder("{");
foreach (var property in dictionary.OrderBy(k => k.Key))
output.AppendFormat("\"{0}\":\"{1}\",", property.Key, property.Value);
output.Append("}");
return output.ToString();
}
private static Item GetItem<TItem>(string json) where TItem: Item
{
return JsonConvert.DeserializeObject<TItem>(json);
}
}
and use something like:
var items = new List<Item>();
foreach (var data in results)
foreach (Node<Dictionary<string, string>> item in data.Item)
items.Add(item.Data.GetItem());
Where items will be the types you're after.
I know this isn't great, but it does get you to one query.
I would like to get property name when I'm in it via reflection. Is it possible?
I have code like this:
public CarType Car
{
get { return (Wheel) this["Wheel"];}
set { this["Wheel"] = value; }
}
And because I need more properties like this I would like to do something like this:
public CarType Car
{
get { return (Wheel) this[GetThisPropertyName()];}
set { this[GetThisPropertyName()] = value; }
}
Since properties are really just methods you can do this and clean up the get_ returned:
class Program
{
static void Main(string[] args)
{
Program p = new Program();
var x = p.Something;
Console.ReadLine();
}
public string Something
{
get
{
return MethodBase.GetCurrentMethod().Name;
}
}
}
If you profile the performance you should find MethodBase.GetCurrentMethod() is miles faster than StackFrame. In .NET 1.1 you will also have issues with StackFrame in release mode (from memory I think I found it was 3x faster).
That said I'm sure the performance issue won't cause too much of a problem- though an interesting discussion on StackFrame slowness can be found here.
I guess another option if you were concerned about performance would be to create a Visual Studio Intellisense Code Snippet that creates the property for you and also creates a string that corresponds to the property name.
Slightly confusing example you presented, unless I just don't get it.
From C# 6.0 you can use the nameof operator.
public CarType MyProperty
{
get { return (CarType)this[nameof(MyProperty)]};
set { this[nameof(MyProperty)] = value]};
}
If you have a method that handles your getter/setter anyway, you can use the C# 4.5 CallerMemberName attribute, in this case you don't even need to repeat the name.
public CarType MyProperty
{
get { return Get<CarType>(); }
set { Set(value); }
}
public T Get<T>([CallerMemberName]string name = null)
{
return (T)this[name];
}
public void Set<T>(T value, [CallerMemberName]string name = null)
{
this[name] = value;
}
I'd like to know more about the context in which you need it since it seems to me that you should already know what property you are working with in the property accessor. If you must, though, you could probably use MethodBase.GetCurrentMethod().Name and remove anything after get_/set_.
Update:
Based on your changes, I would say that you should use inheritance rather than reflection. I don't know what data is in your dictionary, but it seems to me that you really want to have different Car classes, say Sedan, Roadster, Buggy, StationWagon, not keep the type in a local variable. Then you would have implementations of methods that do the proper thing for that type of Car. Instead of finding out what kind of car you have, then doing something, you then simply call the appropriate method and the Car object does the right thing based on what type it is.
public interface ICar
{
void Drive( decimal velocity, Orientation orientation );
void Shift( int gear );
...
}
public abstract class Car : ICar
{
public virtual void Drive( decimal velocity, Orientation orientation )
{
...some default implementation...
}
public abstract void Shift( int gear );
...
}
public class AutomaticTransmission : Car
{
public override void Shift( int gear )
{
...some specific implementation...
}
}
public class ManualTransmission : Car
{
public override void Shift( int gear )
{
...some specific implementation...
}
}
Use MethodBase.GetCurrentMethod() instead!
Reflection is used to do work with types that can't be done at compile time. Getting the name of the property accessor you're in can be decided at compile time so you probably shouldn't use reflection for it.
You get use the accessor method's name from the call stack using System.Diagnostics.StackTrace though.
string GetPropertyName()
{
StackTrace callStackTrace = new StackTrace();
StackFrame propertyFrame = callStackTrace.GetFrame(1); // 1: below GetPropertyName frame
string properyAccessorName = propertyFrame.GetMethod().Name;
return properyAccessorName.Replace("get_","").Replace("set_","");
}
FWIW I implemented a system like this:
[CrmAttribute("firstname")]
public string FirstName
{
get { return GetPropValue<string>(MethodBase.GetCurrentMethod().Name); }
set { SetPropValue(MethodBase.GetCurrentMethod().Name, value); }
}
// this is in a base class, skipped that bit for clairty
public T GetPropValue<T>(string propName)
{
propName = propName.Replace("get_", "").Replace("set_", "");
string attributeName = GetCrmAttributeName(propName);
return GetAttributeValue<T>(attributeName);
}
public void SetPropValue(string propName, object value)
{
propName = propName.Replace("get_", "").Replace("set_", "");
string attributeName = GetCrmAttributeName(propName);
SetAttributeValue(attributeName, value);
}
private static Dictionary<string, string> PropToAttributeMap = new Dictionary<string, string>();
private string GetCrmAttributeName(string propertyName)
{
// keyName for our propertyName to (static) CrmAttributeName cache
string keyName = this.GetType().Name + propertyName;
// have we already done this mapping?
if (!PropToAttributeMap.ContainsKey(keyName))
{
Type t = this.GetType();
PropertyInfo info = t.GetProperty(propertyName);
if (info == null)
{
throw new Exception("Cannot find a propety called " + propertyName);
}
object[] attrs = info.GetCustomAttributes(false);
foreach (object o in attrs)
{
CrmAttributeAttribute attr = o as CrmAttributeAttribute ;
if (attr != null)
{
// found it. Save the mapping for next time.
PropToAttributeMap[keyName] = attr.AttributeName;
return attr.AttributeName;
}
}
throw new Exception("Missing MemberOf attribute for " + info.Name + "." + propertyName + ". Could not auto-access value");
}
// return the existing mapping
string result = PropToAttributeMap[keyName];
return result;
}
There's also a custom attribute class called CrmAttributeAttribute.
I'd strongly recommend against using GetStackFrame() as part of your solution, my original version of the solution was originally the much neater:
return GetPropValue<string>();
But it was 600x slower than the version above.
Solution # 1
var a = nameof(SampleMethod); //a == SampleMethod
var b = nameof(SampleVariable); //b == SampleVariable
var c = nameof(SampleProperty); //c == SampleProperty
Solution # 2
MethodBase.GetCurrentMethod().Name; // Name of method in which you call the code
MethodBase.GetCurrentMethod().Name.Replace("set_", "").Replace("get_", ""); // current Property
Solution # 3
from StackTrace:
public static class Props
{
public static string CurrPropName =>
(new StackTrace()).GetFrame(1).GetMethod().Name.Replace("set_", "").Replace("get_", "");
public static string CurrMethodName =>
(new StackTrace()).GetFrame(1).GetMethod().Name;
}
you just need to call Props.CurrPropName or Props.CurrMethodName
Solution # 4
Solution for .NET 4.5+:
public static class Props
{
public static string GetCallerName([System.Runtime.CompilerServices.CallerMemberName] String propertyName = "")
{
return propertyName;
}
}
usage: Props.GetCallerName();
Yes, it is!
string test = "test string";
Type type = test.GetType();
PropertyInfo[] propInfos = type.GetProperties();
for (int i = 0; i < propInfos.Length; i++)
{
PropertyInfo pi = (PropertyInfo)propInfos.GetValue(i);
string propName = pi.Name;
}
Try using System.Diagnostics.StackTrace to reflect on the call stack. The property should be somewhere in the call stack (probably at the top if you're calling it directly from the property's code).
I have a set of 'dynamic data' that I need to bind to the GridControl. Up until now, I have been using the standard DataTable class that's part of the System.Data namespace. This has worked fine, but I've been told I cannot use this as it's too heavy for serialization across the network between client & server.
So I thought I could easy replicate a 'cut-down' version of the DataTable class by simply having a type of List<Dictionary<string, object>> whereby the List represents the collection of rows, and each Dictionary represents one row with the column names and values as a KeyValuePair type. I could set up the Grid to have the column DataField properties to match those of the keys in the Dictionary (just like I was doing for the DataTable's column names.
However after doing
gridControl.DataSource = table;
gridControl.RefreshDataSource();
The grid has no data...
I think I need to implement IEnumerator - any help on this would be much appreciated!
Example calling code looks like this:
var table = new List<Dictionary<string,object>>();
var row = new Dictionary<string, object>
{
{"Field1", "Data1"},
{"Field2", "Data2"},
{"Field3", "Data3"}
};
table.Add(row);
gridControl1.DataSource = table;
gridControl1.RefreshDataSource();
Welcome to the wonderful world of System.ComponentModel. This dark corner of .NET is very powerful, but very complex.
A word of caution; unless you have a lot of time for this - you may do well to simply serialize it in whatever mechanism you are happy with, but rehydrate it back into a DataTable at each end... what follows is not for the faint-hearted ;-p
Firstly - data binding (for tables) works against lists (IList/IListSource) - so List<T> should be fine (edited: I misread something). But it isn't going to understand that your dictionary is actually columns...
To get a type to pretend to have columns you need to use custom PropertyDescriptor implementations. There are several ways to do this, depending on whether the column definitions are always the same (but determined at runtime, i.e. perhaps from config), or whether it changes per usage (like how each DataTable instance can have different columns).
For "per instance" customisation, you need to look at ITypedList - this beast (implemented in addition to IList) has the fun task of presenting properties for tabular data... but it isn't alone:
For "per type" customisation, you can look at TypeDescriptionProvider - this can suggest dynamic properties for a class...
...or you can implement ICustomTypeDescriptor - but this is only used (for lists) in very occasional circumstances (an object indexer (public object this[int index] {get;}") and at least one row in the list at the point of binding). (this interface is much more useful when binding discrete objects - i.e. not lists).
Implementing ITypedList, and providing a PropertyDescriptor model is hard work... hence it is only done very occasionally. I'm fairly familiar with it, but I wouldn't do it just for laughs...
Here's a very, very simplified implementation (all columns are strings; no notifications (via descriptor), no validation (IDataErrorInfo), no conversions (TypeConverter), no additional list support (IBindingList/IBindingListView), no abstraction (IListSource), no other other metadata/attributes, etc):
using System.ComponentModel;
using System.Collections.Generic;
using System;
using System.Windows.Forms;
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
PropertyBagList list = new PropertyBagList();
list.Columns.Add("Foo");
list.Columns.Add("Bar");
list.Add("abc", "def");
list.Add("ghi", "jkl");
list.Add("mno", "pqr");
Application.Run(new Form {
Controls = {
new DataGridView {
Dock = DockStyle.Fill,
DataSource = list
}
}
});
}
}
class PropertyBagList : List<PropertyBag>, ITypedList
{
public PropertyBag Add(params string[] args)
{
if (args == null) throw new ArgumentNullException("args");
if (args.Length != Columns.Count) throw new ArgumentException("args");
PropertyBag bag = new PropertyBag();
for (int i = 0; i < args.Length; i++)
{
bag[Columns[i]] = args[i];
}
Add(bag);
return bag;
}
public PropertyBagList() { Columns = new List<string>(); }
public List<string> Columns { get; private set; }
PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
{
if(listAccessors == null || listAccessors.Length == 0)
{
PropertyDescriptor[] props = new PropertyDescriptor[Columns.Count];
for(int i = 0 ; i < props.Length ; i++)
{
props[i] = new PropertyBagPropertyDescriptor(Columns[i]);
}
return new PropertyDescriptorCollection(props, true);
}
throw new NotImplementedException("Relations not implemented");
}
string ITypedList.GetListName(PropertyDescriptor[] listAccessors)
{
return "Foo";
}
}
class PropertyBagPropertyDescriptor : PropertyDescriptor
{
public PropertyBagPropertyDescriptor(string name) : base(name, null) { }
public override object GetValue(object component)
{
return ((PropertyBag)component)[Name];
}
public override void SetValue(object component, object value)
{
((PropertyBag)component)[Name] = (string)value;
}
public override void ResetValue(object component)
{
((PropertyBag)component)[Name] = null;
}
public override bool CanResetValue(object component)
{
return true;
}
public override bool ShouldSerializeValue(object component)
{
return ((PropertyBag)component)[Name] != null;
}
public override Type PropertyType
{
get { return typeof(string); }
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type ComponentType
{
get { return typeof(PropertyBag); }
}
}
class PropertyBag
{
private readonly Dictionary<string, string> values
= new Dictionary<string, string>();
public string this[string key]
{
get
{
string value;
values.TryGetValue(key, out value);
return value;
}
set
{
if (value == null) values.Remove(key);
else values[key] = value;
}
}
}