Using JSON Patch to add values to a dictionary - c#

Overview
I'm trying to write a web service using ASP.NET Core that allows clients to query and modify the state of a microcontroller. This microcontroller contains a number of systems that I model within my application - for instance, a PWM system, an actuator input system, etc.
The components of these systems all have particular properties that can be queried or modified using a JSON patch request. For example, the 4th PWM on the micro can be enabled using an HTTP request carrying {"op":"replace", "path":"/pwms/3/enabled", "value":true}. To support this, I'm using the AspNetCore.JsonPatch library.
My problem is that I'm trying to implement JSON Patch support for a new "CAN database" system that logically should map a definition name to a particular CAN message definition, and I'm not sure how to go about this.
Details
The diagram below models the CAN database system. A CanDatabase instance should logically contain a dictionary of the form IDictionary<string, CanMessageDefinition>.
To support creating new message definitions, my application should allow users to send JSON patch requests like this:
{
"op": "add",
"path": "/candb/my_new_definition",
"value": {
"template": ["...", "..."],
"repeatRate": "...",
"...": "...",
}
}
Here, my_new_definition would define the definition name, and the object associated with value should be deserialised to a CanMessageDefinition object. This should then be stored as a new key-value pair in the CanDatabase dictionary.
The issue is that path should specify a property path which for statically-typed objects would be...well, static (an exception to this is that it allows for referencing array elements e.g. /pwms/3 as above).
What I've tried
A. The Leeroy Jenkins approach
Forget the fact that I know it won't work - I tried the implementation below (which uses static-typing only despite the fact I need to support dynamic JSON Patch paths) just to see what happens.
Implementation
internal sealed class CanDatabaseModel : DeviceComponentModel<CanDatabaseModel>
{
public CanDatabaseModel()
{
this.Definitions = new Dictionary<string, CanMessageDefinition>();
}
[JsonProperty(PropertyName = "candb")]
public IDictionary<string, CanMessageDefinition> Definitions { get; }
...
}
Test
{
"op": "add",
"path": "/candb/foo",
"value": {
"messageId": 171,
"template": [17, 34],
"repeatRate": 100,
"canPort": 0
}
}
Outcome
An InvalidCastException is thrown at the site where I try to apply the specified changes to the JsonPatchDocument.
Site:
var currentModelSnapshot = this.currentModelFilter(this.currentModel.Copy());
var snapshotWithChangesApplied = currentModelSnapshot.Copy();
diffDocument.ApplyTo(snapshotWithChangesApplied);
Exception:
Unable to cast object of type 'Newtonsoft.Json.Serialization.JsonDictionaryContract' to type 'Newtonsoft.Json.Serialization.JsonObjectContract'.
B. Relying on dynamic JSON Patching
A more promising plan of attack seemed to be relying on dynamic JSON patching, which involves performing patch operations on instances of ExpandoObject. This allows you to use JSON patch documents to add, remove or replace properties since you're dealing with a dynamically-typed object.
Implementation
internal sealed class CanDatabaseModel : DeviceComponentModel<CanDatabaseModel>
{
public CanDatabaseModel()
{
this.Definitions = new ExpandoObject();
}
[JsonProperty(PropertyName = "candb")]
public IDictionary<string, object> Definitions { get; }
...
}
Test
{
"op": "add",
"path": "/candb/foo",
"value": {
"messageId": 171,
"template": [17, 34],
"repeatRate": 100,
"canPort": 0
}
}
Outcome
Making this change allows this part of my test to run without exceptions being raised, but JSON Patch has no knowledge of what to deserialise value as, resulting in the data being stored in the dictionary as a JObject rather than a CanMessageDefinition:
Would it be possible to 'tell' JSON Patch how to deserialise the information by any chance? Perhaps something along the lines of using a JsonConverter attribute on Definitions?
[JsonProperty(PropertyName = "candb")]
[JsonConverter(...)]
public IDictionary<string, object> Definitions { get; }
Summary
I need to support JSON patch requests that add values to a dictionary
I've tried going down the purely-static route, which failed
I've tried using dynamic JSON patching
This partly worked, but my data was stored as a JObject type instead of the intended type
Is there an attribute (or some other technique) I can apply to my property to let it deserialise to the correct type (not an anonymous type)?

Since there doesn't seem to be any official way to do it, I've come up with a Temporary Solution™ (read: a solution that works well enough so I'll probably keep it forever).
In order to make it seem like JSON Patch handles dictionary-like operations, I created a class called DynamicDeserialisationStore which inherits from DynamicObject and makes use of JSON Patch's support for dynamic objects.
More specifically, this class overrides methods like TrySetMember, TrySetIndex, TryGetMember, etc. to essentially act like a dictionary, except that it delegates all these operations to callbacks provided to its constructor.
Implementation
The code below provides the implementation of DynamicDeserialisationStore. It implements IDictionary<string, object> (which is the signature JSON Patch requires to work with dynamic objects) but I only implement the bare minimum of the methods I require.
The problem with JSON Patch's support for dynamic objects is that it will set properties to JObject instances i.e. it won't automatically perform deserialisation like it would when setting static properties, as it can't infer the type. DynamicDeserialisationStore is parameterised on the type of object that it will try to automatically try to deserialise these JObject instances to when they're set.
The class accepts callbacks to handle basic dictionary operations instead of maintaining an internal dictionary itself, because in my "real" system model code I don't actually use a dictionary (for various reasons) - I just make it appear that way to clients.
internal sealed class DynamicDeserialisationStore<T> : DynamicObject, IDictionary<string, object> where T : class
{
private readonly Action<string, T> storeValue;
private readonly Func<string, bool> removeValue;
private readonly Func<string, T> retrieveValue;
private readonly Func<IEnumerable<string>> retrieveKeys;
public DynamicDeserialisationStore(
Action<string, T> storeValue,
Func<string, bool> removeValue,
Func<string, T> retrieveValue,
Func<IEnumerable<string>> retrieveKeys)
{
this.storeValue = storeValue;
this.removeValue = removeValue;
this.retrieveValue = retrieveValue;
this.retrieveKeys = retrieveKeys;
}
public int Count
{
get
{
return this.retrieveKeys().Count();
}
}
private IReadOnlyDictionary<string, T> AsDict
{
get
{
return (from key in this.retrieveKeys()
let value = this.retrieveValue(key)
select new { key, value })
.ToDictionary(it => it.key, it => it.value);
}
}
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
if (indexes.Length == 1 && indexes[0] is string && value is JObject)
{
return this.TryUpdateValue(indexes[0] as string, value);
}
return base.TrySetIndex(binder, indexes, value);
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
if (indexes.Length == 1 && indexes[0] is string)
{
try
{
result = this.retrieveValue(indexes[0] as string);
return true;
}
catch (KeyNotFoundException)
{
// Pass through.
}
}
return base.TryGetIndex(binder, indexes, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
return this.TryUpdateValue(binder.Name, value);
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
try
{
result = this.retrieveValue(binder.Name);
return true;
}
catch (KeyNotFoundException)
{
return base.TryGetMember(binder, out result);
}
}
private bool TryUpdateValue(string name, object value)
{
JObject jObject = value as JObject;
T tObject = value as T;
if (jObject != null)
{
this.storeValue(name, jObject.ToObject<T>());
return true;
}
else if (tObject != null)
{
this.storeValue(name, tObject);
return true;
}
return false;
}
object IDictionary<string, object>.this[string key]
{
get
{
return this.retrieveValue(key);
}
set
{
this.TryUpdateValue(key, value);
}
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return this.AsDict.ToDictionary(it => it.Key, it => it.Value as object).GetEnumerator();
}
public void Add(string key, object value)
{
this.TryUpdateValue(key, value);
}
public bool Remove(string key)
{
return this.removeValue(key);
}
#region Unused methods
bool ICollection<KeyValuePair<string, object>>.IsReadOnly
{
get
{
throw new NotImplementedException();
}
}
ICollection<string> IDictionary<string, object>.Keys
{
get
{
throw new NotImplementedException();
}
}
ICollection<object> IDictionary<string, object>.Values
{
get
{
throw new NotImplementedException();
}
}
void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
void ICollection<KeyValuePair<string, object>>.Clear()
{
throw new NotImplementedException();
}
bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
bool IDictionary<string, object>.ContainsKey(string key)
{
throw new NotImplementedException();
}
void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
bool IDictionary<string, object>.TryGetValue(string key, out object value)
{
throw new NotImplementedException();
}
#endregion
}
Tests
The tests for this class are provided below. I create a mock system model (see image) and perform various JSON Patch operations on it.
Here's the code:
public class DynamicDeserialisationStoreTests
{
private readonly FooSystemModel fooSystem;
public DynamicDeserialisationStoreTests()
{
this.fooSystem = new FooSystemModel();
}
[Fact]
public void Store_Should_Handle_Adding_Keyed_Model()
{
// GIVEN the foo system currently contains no foos.
this.fooSystem.Foos.ShouldBeEmpty();
// GIVEN a patch document to store a foo called "test".
var request = "{\"op\":\"add\",\"path\":\"/foos/test\",\"value\":{\"number\":3,\"bazzed\":true}}";
var operation = JsonConvert.DeserializeObject<Operation<FooSystemModel>>(request);
var patchDocument = new JsonPatchDocument<FooSystemModel>(
new[] { operation }.ToList(),
new CamelCasePropertyNamesContractResolver());
// WHEN we apply this patch document to the foo system model.
patchDocument.ApplyTo(this.fooSystem);
// THEN the system model should now contain a new foo called "test" with the expected properties.
this.fooSystem.Foos.ShouldHaveSingleItem();
FooModel foo = this.fooSystem.Foos["test"] as FooModel;
foo.Number.ShouldBe(3);
foo.IsBazzed.ShouldBeTrue();
}
[Fact]
public void Store_Should_Handle_Removing_Keyed_Model()
{
// GIVEN the foo system currently contains a foo.
var testFoo = new FooModel { Number = 3, IsBazzed = true };
this.fooSystem.Foos["test"] = testFoo;
// GIVEN a patch document to remove a foo called "test".
var request = "{\"op\":\"remove\",\"path\":\"/foos/test\"}";
var operation = JsonConvert.DeserializeObject<Operation<FooSystemModel>>(request);
var patchDocument = new JsonPatchDocument<FooSystemModel>(
new[] { operation }.ToList(),
new CamelCasePropertyNamesContractResolver());
// WHEN we apply this patch document to the foo system model.
patchDocument.ApplyTo(this.fooSystem);
// THEN the system model should be empty.
this.fooSystem.Foos.ShouldBeEmpty();
}
[Fact]
public void Store_Should_Handle_Modifying_Keyed_Model()
{
// GIVEN the foo system currently contains a foo.
var originalFoo = new FooModel { Number = 3, IsBazzed = true };
this.fooSystem.Foos["test"] = originalFoo;
// GIVEN a patch document to modify a foo called "test".
var request = "{\"op\":\"replace\",\"path\":\"/foos/test\", \"value\":{\"number\":6,\"bazzed\":false}}";
var operation = JsonConvert.DeserializeObject<Operation<FooSystemModel>>(request);
var patchDocument = new JsonPatchDocument<FooSystemModel>(
new[] { operation }.ToList(),
new CamelCasePropertyNamesContractResolver());
// WHEN we apply this patch document to the foo system model.
patchDocument.ApplyTo(this.fooSystem);
// THEN the system model should contain a modified "test" foo.
this.fooSystem.Foos.ShouldHaveSingleItem();
FooModel foo = this.fooSystem.Foos["test"] as FooModel;
foo.Number.ShouldBe(6);
foo.IsBazzed.ShouldBeFalse();
}
#region Mock Models
private class FooModel
{
[JsonProperty(PropertyName = "number")]
public int Number { get; set; }
[JsonProperty(PropertyName = "bazzed")]
public bool IsBazzed { get; set; }
}
private class FooSystemModel
{
private readonly IDictionary<string, FooModel> foos;
public FooSystemModel()
{
this.foos = new Dictionary<string, FooModel>();
this.Foos = new DynamicDeserialisationStore<FooModel>(
storeValue: (name, foo) => this.foos[name] = foo,
removeValue: name => this.foos.Remove(name),
retrieveValue: name => this.foos[name],
retrieveKeys: () => this.foos.Keys);
}
[JsonProperty(PropertyName = "foos")]
public IDictionary<string, object> Foos { get; }
}
#endregion
}

You could, for instance, deserialize your received Json into an object:
var dataDict = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
And iterate over it, casting and converting the values of the KeyValuePairs you want to patch into your destination type, CanMessageDefinition:
Dictionary<string, CanMessageDefinition> updateData = new Dictionary<string, CanMessageDefinition>();
foreach (var record in dataDict)
{
CanMessageDefinition recordValue = (CanMessageDefinition)record.Value;
if (yourExistingRecord.KeyAttributes.Keys.Contains(record.Key) && (!yourExistingRecord.KeyAttributes.Values.Equals(record.Value)))
{
updateData.Add(record.Key, recordValue);
}
}
And just save your object to your db.
An alternative would be to do this inside a JsonConverter as you mentioned. Cheers

Related

Call code once when controller is first accessed [duplicate]

I have read lots of information about page caching and partial page caching in a MVC application. However, I would like to know how you would cache data.
In my scenario I will be using LINQ to Entities (entity framework). On the first call to GetNames (or whatever the method is) I want to grab the data from the database. I want to save the results in cache and on the second call to use the cached version if it exists.
Can anyone show an example of how this would work, where this should be implemented (model?) and if it would work.
I have seen this done in traditional ASP.NET apps , typically for very static data.
Here's a nice and simple cache helper class/service I use:
using System.Runtime.Caching;
public class InMemoryCache: ICacheService
{
public T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class
{
T item = MemoryCache.Default.Get(cacheKey) as T;
if (item == null)
{
item = getItemCallback();
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(10));
}
return item;
}
}
interface ICacheService
{
T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class;
}
Usage:
cacheProvider.GetOrSet("cache key", (delegate method if cache is empty));
Cache provider will check if there's anything by the name of "cache id" in the cache, and if there's not, it will call a delegate method to fetch data and store it in cache.
Example:
var products=cacheService.GetOrSet("catalog.products", ()=>productRepository.GetAll())
Reference the System.Web dll in your model and use System.Web.Caching.Cache
public string[] GetNames()
{
string[] names = Cache["names"] as string[];
if(names == null) //not in cache
{
names = DB.GetNames();
Cache["names"] = names;
}
return names;
}
A bit simplified but I guess that would work. This is not MVC specific and I have always used this method for caching data.
I'm referring to TT's post and suggest the following approach:
Reference the System.Web dll in your model and use System.Web.Caching.Cache
public string[] GetNames()
{
var noms = Cache["names"];
if(noms == null)
{
noms = DB.GetNames();
Cache["names"] = noms;
}
return ((string[])noms);
}
You should not return a value re-read from the cache, since you'll never know if at that specific moment it is still in the cache. Even if you inserted it in the statement before, it might already be gone or has never been added to the cache - you just don't know.
So you add the data read from the database and return it directly, not re-reading from the cache.
For .NET 4.5+ framework
add reference: System.Runtime.Caching
add using statement:
using System.Runtime.Caching;
public string[] GetNames()
{
var noms = System.Runtime.Caching.MemoryCache.Default["names"];
if(noms == null)
{
noms = DB.GetNames();
System.Runtime.Caching.MemoryCache.Default["names"] = noms;
}
return ((string[])noms);
}
In the .NET Framework 3.5 and earlier versions, ASP.NET provided an in-memory cache implementation in the System.Web.Caching namespace. In previous versions of the .NET Framework, caching was available only in the System.Web namespace and therefore required a dependency on ASP.NET classes. In the .NET Framework 4, the System.Runtime.Caching namespace contains APIs that are designed for both Web and non-Web applications.
More info:
https://msdn.microsoft.com/en-us/library/dd997357(v=vs.110).aspx
https://learn.microsoft.com/en-us/dotnet/framework/performance/caching-in-net-framework-applications
Steve Smith did two great blog posts which demonstrate how to use his CachedRepository pattern in ASP.NET MVC. It uses the repository pattern effectively and allows you to get caching without having to change your existing code.
http://ardalis.com/Introducing-the-CachedRepository-Pattern
http://ardalis.com/building-a-cachedrepository-via-strategy-pattern
In these two posts he shows you how to set up this pattern and also explains why it is useful. By using this pattern you get caching without your existing code seeing any of the caching logic. Essentially you use the cached repository as if it were any other repository.
I have used it in this way and it works for me.
https://msdn.microsoft.com/en-us/library/system.web.caching.cache.add(v=vs.110).aspx
parameters info for system.web.caching.cache.add.
public string GetInfo()
{
string name = string.Empty;
if(System.Web.HttpContext.Current.Cache["KeyName"] == null)
{
name = GetNameMethod();
System.Web.HttpContext.Current.Cache.Add("KeyName", name, null, DateTime.Noew.AddMinutes(5), Cache.NoSlidingExpiration, CacheitemPriority.AboveNormal, null);
}
else
{
name = System.Web.HttpContext.Current.Cache["KeyName"] as string;
}
return name;
}
AppFabric Caching is distributed and an in-memory caching technic that stores data in key-value pairs using physical memory across multiple servers. AppFabric provides performance and scalability improvements for .NET Framework applications. Concepts and Architecture
Extending #Hrvoje Hudo's answer...
Code:
using System;
using System.Runtime.Caching;
public class InMemoryCache : ICacheService
{
public TValue Get<TValue>(string cacheKey, int durationInMinutes, Func<TValue> getItemCallback) where TValue : class
{
TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
if (item == null)
{
item = getItemCallback();
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
}
return item;
}
public TValue Get<TValue, TId>(string cacheKeyFormat, TId id, int durationInMinutes, Func<TId, TValue> getItemCallback) where TValue : class
{
string cacheKey = string.Format(cacheKeyFormat, id);
TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
if (item == null)
{
item = getItemCallback(id);
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
}
return item;
}
}
interface ICacheService
{
TValue Get<TValue>(string cacheKey, Func<TValue> getItemCallback) where TValue : class;
TValue Get<TValue, TId>(string cacheKeyFormat, TId id, Func<TId, TValue> getItemCallback) where TValue : class;
}
Examples
Single item caching (when each item is cached based on its ID because caching the entire catalog for the item type would be too intensive).
Product product = cache.Get("product_{0}", productId, 10, productData.getProductById);
Caching all of something
IEnumerable<Categories> categories = cache.Get("categories", 20, categoryData.getCategories);
Why TId
The second helper is especially nice because most data keys are not composite. Additional methods could be added if you use composite keys often. In this way you avoid doing all sorts of string concatenation or string.Formats to get the key to pass to the cache helper. It also makes passing the data access method easier because you don't have to pass the ID into the wrapper method... the whole thing becomes very terse and consistant for the majority of use cases.
Here's an improvement to Hrvoje Hudo's answer. This implementation has a couple of key improvements:
Cache keys are created automatically based on the function to update data and the object passed in that specifies dependencies
Pass in time span for any cache duration
Uses a lock for thread safety
Note that this has a dependency on Newtonsoft.Json to serialize the dependsOn object, but that can be easily swapped out for any other serialization method.
ICache.cs
public interface ICache
{
T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class;
}
InMemoryCache.cs
using System;
using System.Reflection;
using System.Runtime.Caching;
using Newtonsoft.Json;
public class InMemoryCache : ICache
{
private static readonly object CacheLockObject = new object();
public T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class
{
string cacheKey = GetCacheKey(getItemCallback, dependsOn);
T item = MemoryCache.Default.Get(cacheKey) as T;
if (item == null)
{
lock (CacheLockObject)
{
item = getItemCallback();
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.Add(duration));
}
}
return item;
}
private string GetCacheKey<T>(Func<T> itemCallback, object dependsOn) where T: class
{
var serializedDependants = JsonConvert.SerializeObject(dependsOn);
var methodType = itemCallback.GetType();
return methodType.FullName + serializedDependants;
}
}
Usage:
var order = _cache.GetOrSet(
() => _session.Set<Order>().SingleOrDefault(o => o.Id == orderId)
, new { id = orderId }
, new TimeSpan(0, 10, 0)
);
public sealed class CacheManager
{
private static volatile CacheManager instance;
private static object syncRoot = new Object();
private ObjectCache cache = null;
private CacheItemPolicy defaultCacheItemPolicy = null;
private CacheEntryRemovedCallback callback = null;
private bool allowCache = true;
private CacheManager()
{
cache = MemoryCache.Default;
callback = new CacheEntryRemovedCallback(this.CachedItemRemovedCallback);
defaultCacheItemPolicy = new CacheItemPolicy();
defaultCacheItemPolicy.AbsoluteExpiration = DateTime.Now.AddHours(1.0);
defaultCacheItemPolicy.RemovedCallback = callback;
allowCache = StringUtils.Str2Bool(ConfigurationManager.AppSettings["AllowCache"]); ;
}
public static CacheManager Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new CacheManager();
}
}
}
return instance;
}
}
public IEnumerable GetCache(String Key)
{
if (Key == null || !allowCache)
{
return null;
}
try
{
String Key_ = Key;
if (cache.Contains(Key_))
{
return (IEnumerable)cache.Get(Key_);
}
else
{
return null;
}
}
catch (Exception)
{
return null;
}
}
public void ClearCache(string key)
{
AddCache(key, null);
}
public bool AddCache(String Key, IEnumerable data, CacheItemPolicy cacheItemPolicy = null)
{
if (!allowCache) return true;
try
{
if (Key == null)
{
return false;
}
if (cacheItemPolicy == null)
{
cacheItemPolicy = defaultCacheItemPolicy;
}
String Key_ = Key;
lock (Key_)
{
return cache.Add(Key_, data, cacheItemPolicy);
}
}
catch (Exception)
{
return false;
}
}
private void CachedItemRemovedCallback(CacheEntryRemovedArguments arguments)
{
String strLog = String.Concat("Reason: ", arguments.RemovedReason.ToString(), " | Key-Name: ", arguments.CacheItem.Key, " | Value-Object: ", arguments.CacheItem.Value.ToString());
LogManager.Instance.Info(strLog);
}
}
I use two classes. First one the cache core object:
public class Cacher<TValue>
where TValue : class
{
#region Properties
private Func<TValue> _init;
public string Key { get; private set; }
public TValue Value
{
get
{
var item = HttpRuntime.Cache.Get(Key) as TValue;
if (item == null)
{
item = _init();
HttpContext.Current.Cache.Insert(Key, item);
}
return item;
}
}
#endregion
#region Constructor
public Cacher(string key, Func<TValue> init)
{
Key = key;
_init = init;
}
#endregion
#region Methods
public void Refresh()
{
HttpRuntime.Cache.Remove(Key);
}
#endregion
}
Second one is list of cache objects:
public static class Caches
{
static Caches()
{
Languages = new Cacher<IEnumerable<Language>>("Languages", () =>
{
using (var context = new WordsContext())
{
return context.Languages.ToList();
}
});
}
public static Cacher<IEnumerable<Language>> Languages { get; private set; }
}
I will say implementing Singleton on this persisting data issue can be a solution for this matter in case you find previous solutions much complicated
public class GPDataDictionary
{
private Dictionary<string, object> configDictionary = new Dictionary<string, object>();
/// <summary>
/// Configuration values dictionary
/// </summary>
public Dictionary<string, object> ConfigDictionary
{
get { return configDictionary; }
}
private static GPDataDictionary instance;
public static GPDataDictionary Instance
{
get
{
if (instance == null)
{
instance = new GPDataDictionary();
}
return instance;
}
}
// private constructor
private GPDataDictionary() { }
} // singleton
HttpContext.Current.Cache.Insert("subjectlist", subjectlist);
You can also try and use the caching built into ASP MVC:
Add the following attribute to the controller method you'd like to cache:
[OutputCache(Duration=10)]
In this case the ActionResult of this will be cached for 10 seconds.
More on this here

How can I hook into all property setters of derived classes from a base class?

I have a .net web application that for all intents and purposes of this question is CRUD with many different domain objects.
A common theme across theses objects is the need to know which value properties have been modified as well as child domain model properties. Currently we have two different systems in place for this.
The value properties is the one I am trying to sort out with this question.
Right now the models all inherit from the PersistableModel base that has these fields and methods of note:
private readonly List<string> _modifiedProperties = new List<string>();
public virtual ModelState State { get; set; }
public IEnumerable<string> ModifiedProperties { get { return _modifiedProperties; } }
protected bool HasModifiedProperties { get { return 0 < _modifiedProperties.Count; } }
public bool WasModified(string propertyName)
{
return _modifiedProperties.Contains(propertyName);
}
public void WasModified(string propertyName, bool modified)
{
if (modified)
{
if (!WasModified(propertyName)) _modifiedProperties.Add(propertyName);
}
else
{
_modifiedProperties.Remove(propertyName);
}
}
Then within each individual model whenever a property is set we also need to call WasModified with a string of the property name and a boolean value.
Obviously this is very tedious and error prone, what I want to do is redesign this base class to automatically add entries to the dictionary when a derived class's property is set.
In my research the closest I've been able to get is to use PostSharp which is out of the question.
While working on a different project I came up with a solution that gets most of the way towards my original goal.
Note that this solution is reliant upon the Dev Express ViewModelBase as its base class, but it wouldn't be hard to make a new base class with the features being used for non Dev Express projects:
Edit: I found an issue with the reset state logic, this update removes that issue.
public abstract class UpdateableModel : ViewModelBase
{
private static readonly MethodInfo GetPropertyMethod;
private static readonly MethodInfo SetPropertyMethod;
private readonly bool _trackingEnabled;
private readonly Dictionary<string, Tuple<Expression, object>> _originalValues;
private readonly List<string> _differingFields;
static UpdateableModel()
{
GetPropertyMethod = typeof(UpdateableModel).GetMethod("GetProperty", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
SetPropertyMethod = typeof(UpdateableModel).GetMethod("SetProperty");
}
protected UpdateableModel(bool isNewModel)
{
_originalValues = new Dictionary<string, Tuple<Expression, object>>();
_differingFields = new List<string>();
if (isNewModel) return;
State = ModelState.Unmodified;
_trackingEnabled = true;
}
public ModelState State
{
get { return GetProperty(() => State); }
set { base.SetProperty(() => State, value); }
}
public new bool SetProperty<T>(Expression<Func<T>> expression, T value)
{
var wasUpdated = base.SetProperty(expression, value);
if (_trackingEnabled && wasUpdated)
{
UpdateState(expression, value);
}
return wasUpdated;
}
/// <summary>
/// Reset State is meant to be called when discarding changes, it will reset the State value to Unmodified and set all modified values back to their original value.
/// </summary>
public void ResetState()
{
if (!_trackingEnabled) return;
foreach (var differingField in _differingFields)
{
var type = GetFuncType(_originalValues[differingField].Item1);
var genericPropertySetter = SetPropertyMethod.MakeGenericMethod(type);
genericPropertySetter.Invoke(this, new[]{_originalValues[differingField].Item2});
}
}
/// <summary>
/// Update State is meant to be called after changes have been persisted, it will reset the State value to Unmodified and update the original values to the new values.
/// </summary>
public void UpdateState()
{
if (!_trackingEnabled) return;
foreach (var differingField in _differingFields)
{
var type = GetFuncType(_originalValues[differingField].Item1);
var genericPropertySetter = GetPropertyMethod.MakeGenericMethod(type);
var value = genericPropertySetter.Invoke(this, new object[] { _originalValues[differingField].Item1 });
var newValue = new Tuple<Expression, object>(_originalValues[differingField].Item1,value);
_originalValues[differingField] = newValue;
}
_differingFields.Clear();
State = ModelState.Unmodified;
}
private static Type GetFuncType(Expression expr)
{
var lambda = expr as LambdaExpression;
if (lambda == null)
{
return null;
}
var member = lambda.Body as MemberExpression;
return member != null ? member.Type : null;
}
private void UpdateState<T>(Expression<Func<T>> expression, T value)
{
var propertyName = GetPropertyName(expression);
if (!_originalValues.ContainsKey(propertyName))
{
_originalValues.Add(propertyName, new Tuple<Expression,object>(expression, value));
}
else
{
if (!Compare(_originalValues[propertyName].Item2, value))
{
_differingFields.Add(propertyName);
}
else if (_differingFields.Contains(propertyName))
{
_differingFields.Remove(propertyName);
}
State = _differingFields.Count == 0
? ModelState.Unmodified
: ModelState.Modified;
}
}
private static bool Compare<T>(T x, T y)
{
return EqualityComparer<T>.Default.Equals(x, y);
}
Another quick note, the IsNewModel constructor flag serves to differentiate objects create on the UI level which don't need any kind of state tracking, and objects generated from the data access level which will need the state tracking.

Creating a local instance of an object being referenced from the AppDomain

Im trying to find out if there is a way to create a local instance of my object that is being referenced from the app domain, reason for this is due to the high amount of chatter I get during all the execution of the method. So instead of having to call the remote object the whole time I'd like to just call a local instance created inside the method.
I've been looking at RemotingServices Marshal and GetObjectData methods but haven't been able to figure out if they will work or not and google hasn't helped either
so the class definition looks as follows
[XmlRoot("SI")]
public class SI : MarshalByRefObject, IXmlSerializable
And then runtime an instance of the class looks like this.
Name: Service
Value: {System.Runtime.Remoting.Proxies.__TransparentProxy}
Type: SI {System.Runtime.Remoting.Proxies.__TransparentProxy}
I was hoping to accomplish what I needed along the lines of the following
var uri =RemotingServices.GetObjectUri(Service);
var serv = RemotingServices.Marshal(Service, uri, typeof(SI)); //Service is the object I described above
SerializationInfo info = new SerializationInfo(typeof(SI), new FormatterConverter());
StreamingContext context = new StreamingContext(StreamingContextStates.All);
serv.GetObjectData(info, context);
var t2 = serv.GetRealObject(context);
I get the following error when calling GetRealObject
"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I still haven't found any way to implement this, anyone perhaps have some suggestions?
Okay. So either install Unity or create your own resource locator (object dictionary).
The following is a resource locator I wrote:
using System;
using System.Collections.Generic;
namespace Bizmonger.Client.Infrastructure
{
public class ServiceLocator
{
#region Members
Dictionary<Type, object> _dictionary = new Dictionary<Type, object>();
static ServiceLocator _serviceLocator = null;
#endregion
public static ServiceLocator Instance
{
get
{
if (_serviceLocator == null)
{
_serviceLocator = new ServiceLocator();
}
return _serviceLocator;
}
}
public object this[Type key]
{
get
{
if (!_dictionary.ContainsKey(key))
{
_dictionary.Add(key, Activator.CreateInstance(key));
}
return _dictionary[key];
}
set
{
_dictionary[key] = value;
}
}
public bool ContainsKey(Type type)
{
return _dictionary.ContainsKey(type);
}
public void Load(object data)
{
if (data == null) { return; }
RemoveExisting(data);
_dictionary.Add(data.GetType(), data);
}
public void Load(Type type, object data)
{
if (data == null) { return; }
RemoveExisting(data);
_dictionary.Add(type, data);
}
#region Helpers
private void RemoveExisting(object data)
{
bool found = _dictionary.ContainsKey(data.GetType());
if (found)
{
_dictionary.Remove(data.GetType());
}
}
#endregion
}
}
Then you can do this in your client:
var uri =RemotingServices.GetObjectUri(Service);
var serv = RemotingServices.Marshal(Service, uri, typeof(SI));
ServiceLocator.Instance.Load(serv);
You can retrieve this object like this:
var server = ServiceLocator.Instance[typeof(some_class)] as some_class;

How to correctly update partially submitted (to a REST API) objects with LINQ to SQL

Our C# application communicates with a database through a LINQ-to-SQL Database Model, specifically using the MVC4 libraries.
I've been assigned the task of implementing a RESTful API. We thought it would be a good idea to version the API. That way, changes to the API can be introduced in a new version, and existing API clients won't break. To support this, every version of the API has its own set of Data Transfer Objects (DTOs) that it exposes and accepts. Some mapping is done (using AutoMapper) to translate between the API and Database Model.
Currently I'm working on updating and creating functionality. That is, if a client POSTs an Item object to the ItemsController, and the Item does not exist in the database yet (given its unique identifier), a new Item should be created. If the identifier is already present in the database, the existing data should be updated. So far so good.
Now, I'm converting a legacy code base to communicate with the RESTful API instead of with the database directly. Some parts of this codebase update a single property on a resource, and send just the identifier and the new value of that single property. The rest of the object should remain as it is in the database.
I'm having trouble implementing this using LINQ-to-SQL, specifically because of the DTO layer. This is the controller method:
[HttpPut]
[HttpPost]
public void UpdateOrCreateItem(ItemDTO data)
{
Item submittedItem = Map(data);
ItemRepository.UpdateOrCreateItem(submittedItem);
}
Now, instead of receiving a fully filled data object, only the identifier and one other property are filled. When the LINQ-to-SQL processes this data as follows:
public static void UpdateOrCreateItem(Item submittedItem)
{
if (submittedItem.Id > 0)
{
// update
using (DatabaseAccessor db = new DatabaseAccessor())
{
db.context.Items.Attach(submittedItem);
db.context.Refresh(RefreshMode.KeepCurrentValues, submittedItem);
db.context.SubmitChanges();
}
} else {
// create
// omitted...
}
}
Refreshing marks all the empty (missing) properties as changed, and its all saved to the database. Instead, only the properties that were submitted at the REST API level should be stored. What would be an elegant solution to this problem?
In the end I wrote some code to accept json-patch requests (see https://www.rfc-editor.org/rfc/rfc6902).
You need to add the media type "application/json-patch" to the collection of accepted formats.
You need to accept an identifier and an array of JsonPatchOperation objects as input to a HTTP PATCH method on your ApiController
The ApiController method:
[HttpPatch]
public void UpdatePartially(int id, JsonPatchOperation[] patchOperations)
{
if (id > 0)
{
// DatabaseAccessor is just a wrapper around my DataContext object
using (DatabaseAccessor db = new DatabaseAccessor())
{
SetDataLoadOptions(db); // optional of course
var item = db.context.Items.Single(i => i.id == id);
foreach (JsonPatchOperation patchOperation in patchOperations)
{
// when you want to set a foreign key identifier, LINQ-to-SQL throw a ForeignKeyReferenceAlreadyHasValueException
// the patchOperation will then use GetForeignKeyObject to fetch the object that it requires to set the foreign key object instead
patchOperation.GetForeignKeyObject = (PropertyInfo property, object identifier) =>
{
// this is just example code, make sure to correct this for the possible properties of your object...
if (property == typeof(Item).GetProperty("JobStatus", typeof(JobStatus)))
{
return db.context.JobStatus.Single(js => js.StatusId == (int)identifier);
}
else if (property == typeof(Item).GetProperty("User", typeof(User)))
{
return db.context.Users.Single(u => u.UserId == (Guid)identifier);
}
throw new ArgumentOutOfRangeException("property", String.Format("Missing getter for property '{0}'.", property.Name));
};
patchOperation.ApplyTo(item);
}
db.context.SubmitChanges();
}
}
}
And here are the dependencies of the above method:
/// <summary>
/// Add this to the global configuration Formatters collection to accept json-patch requests
/// </summary>
public class JsonPatchMediaTypeFormatter : JsonMediaTypeFormatter
{
public JsonPatchMediaTypeFormatter() : base()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json-patch"));
}
}
/// <summary>
/// All possible values for the "op" property of a json-patch object
/// docs: https://www.rfc-editor.org/rfc/rfc6902#section-4
/// </summary>
public enum JsonPatchOperationType
{
add,
remove,
replace,
move,
copy,
test
}
/// <summary>
/// json-patch is a partial update format for HTTP PATCH requests
/// docs: https://www.rfc-editor.org/rfc/rfc6902
/// </summary>
public class JsonPatchOperation
{
public string op { get; set; }
public string from { get; set; }
public string path { get; set; }
public string value { get; set; }
public Func<PropertyInfo, object, object> GetForeignKeyObject { get; set; }
public JsonPatchOperationType Operation
{
get
{
return (JsonPatchOperationType)Enum.Parse(typeof(JsonPatchOperationType), op);
}
}
public void ApplyTo(object document)
{
switch (Operation)
{
case JsonPatchOperationType.add:
Add(document, path, value);
break;
case JsonPatchOperationType.remove:
Remove(document, path);
break;
case JsonPatchOperationType.replace:
Replace(document, path, value);
break;
case JsonPatchOperationType.move:
Move(document, path, from);
break;
case JsonPatchOperationType.copy:
Copy(document, path, from);
break;
case JsonPatchOperationType.test:
Test(document, path, value);
break;
}
}
private void Add(object document, string path, string value)
{
Type documentType = document.GetType();
PathInfo pathInfo = GetPathInfo(documentType, path);
object convertedValue = ConvertToType(value, pathInfo.PropertyInfo.PropertyType);
pathInfo.PropertyInfo.SetValue(document, convertedValue, pathInfo.Indexes);
}
private void Replace(object document, string path, string value)
{
Type documentType = document.GetType();
PathInfo pathInfo = GetPathInfo(documentType, path);
object convertedValue = ConvertToType(value, pathInfo.PropertyInfo.PropertyType);
try
{
pathInfo.PropertyInfo.SetValue(document, convertedValue, pathInfo.Indexes);
}
// gnarly hack for setting foreign key properties
catch (TargetInvocationException tie)
{
if (tie.InnerException is ForeignKeyReferenceAlreadyHasValueException)
{
PropertyInfo matchingProperty = documentType.GetProperties().Single(p => p.GetCustomAttributes(typeof(AssociationAttribute), true).Any(attr => ((AssociationAttribute)attr).ThisKey == pathInfo.PropertyInfo.Name));
matchingProperty.SetValue(document, GetForeignKeyObject(matchingProperty, convertedValue), null);
}
else
{
throw tie;
}
}
}
private void Remove(object document, string path)
{
Type documentType = document.GetType();
PathInfo pathInfo = GetPathInfo(documentType, path);
pathInfo.PropertyInfo.SetValue(document, GetDefaultValue(pathInfo.PropertyInfo.PropertyType), pathInfo.Indexes);
}
private void Copy(object document, string path, string from)
{
throw new NotImplementedException();
}
private void Move(object document, string path, string from)
{
throw new NotImplementedException();
}
private void Test(object document, string path, string value)
{
throw new NotImplementedException();
}
#region Util
private class PathInfo
{
public PropertyInfo PropertyInfo { get; set; }
public object[] Indexes { get; set; }
}
private PathInfo GetPathInfo(Type documentType, string path)
{
object[] indexes = null;
PropertyInfo propertyInfo = documentType.GetProperty(path);
return new PathInfo { PropertyInfo = propertyInfo, Indexes = indexes };
}
private object GetDefaultValue(Type t)
{
if (t.IsValueType)
return Activator.CreateInstance(t);
return null;
}
private object ConvertToType(string value, Type type)
{
TypeConverter typeConverter = TypeDescriptor.GetConverter(type);
return typeConverter.ConvertFromString(value);
}
#endregion
}
It should be obvious that this is not finished, mature or elegant. But it works.

c# programming code optimization

I have about 20 classes which are derived from ConvertApi classes. Every class share Convert method from parent class and has unique property SupportedFiles. I use these classes for file manipulation and my code looks like
if (fileEx=="txt")
new Text2Pdf().Convert(file)
else
if (fileEx=="doc")
new Word2Pdf().Convert(file)
else
//and so on..
I know that these code can be optimized because 20 times if operator is repeated and that looks bad, but can't find a way to do that. Could anyone help me?
class Text2Pdf : ConvertApi
{
enum SupportedFiles { txt, log };
}
class Word2Pdf : ConvertApi
{
enum SupportedFiles { doc, docx };
}
class Excel2Pdf : ConvertApi
{
enum SupportedFiles { xls, xlsx };
}
class ConvertApi
{
public void Convert(....);
}
In your base class, have something like this:
public abstract class ConvertApi
{
protected abstract string[] SupportedFilesImpl();
public bool Supports(string ext)
{
return SupportedFilesImpl.Contains(ext);
}
}
Now, your derived classes can implement this method:
public class Text2PDF : ConvertApi
{
protected override string[] SupportedFilesImpl { return new string[] { "txt", "log"}; }
}
public class Doc2PDF : ConvertApi
{
protected override string[] SupportedFilesImpl { return new string[] { "doc", "docx"}; }
}
... and so on for the rest of the converters. Then put these in a list...
List<ConvertApi> converters = new List<ConvertApi>();
converters.Add(new Text2PDF());
converters.Add(new Doc2PDF());
(Note, I'd probably have a class containing these rather than just a list, but anyway). Now, to find a converter:
foreach(ConvertApi api in converters)
{
if(api.Supports(fileExt))
{
// woo!
break;
}
}
Assuming each converter is stateless, it sounds like you just want a Dictionary<string, ConvertApi>:
private static readonly Dictionary<string, ConvertApi> ConverterByType =
new Dictionary<string, ConvertApi>
{
{ "txt", new Text2PdfConverter() },
{ "doc", new Word2PdfConverter() },
...
};
...
ConvertApi converter;
if (!ConverterByType.TryGetValue(fileEx, out converter))
{
// No converter available... do whatever
}
else
{
converter.Convert(file);
}
(The dictionary initialization will end up creating more converters than you really need for any converter that supports multiple extensions, but that's a different matter.)
If you need a new converter each time, make it a Dictionary<string, Func<ConvertApi>> and populate it as:
{ "txt", () => new Text2PdfConverter() },
{ "doc", () => new Word2PdfConverter() },
... then call the delegate to get the converter when you need it.
Of course, this puts all the initialization in one place - you may want a way of getting the converters to register the extensions they understand with a "converter provider" of some type.
C# supports switch-case operator for strings, i.e. your code coud be rewritten as
switch (fileEx)
{
case "txt" : new Text2Pdf().Convert(file); break;
case "doc": new Word2Pdf().Convert(file); break;
...
}
If you change your classes' names to correspond to supported extensions then you will be able to construct them using reflection, like this (error checking omitted for brevity):
var t = Type.GetType(fileEx + "2Pdf");
var tConstructor = t.GetConstructor(Type.EmptyTypes);
var tInstance = tConstructor.Invoke(new object[0]);
((ConvertApi)tInstance).Convert(...);
This might require some additional work (i.e. create a separate class for each extension, deriving them from some base class - for example Doc2Pdf and Docx2Pdf both deriving from Word2Pdf).
The advantage is that you will not have to touch this part of code anymore. If you're planning to write some interface for plugins then it may come in handy.
The code above also has an assumption that your ConvertApi classes all have default parameterless constuctor.
You need to use an abstract factory pattern, here. All your text converters should derive from a common interface ITextConverter implementing your Convert method. The file extension would be a parameter of your factory. Here is a sample below (typed "on the fly", sometimes copy-pasting code from my sources, so there might be typos. The goal here is to give you a general idea for a flexible implementation).
public interface IFileConverter
{
bool Convert(string filePath);
}
public static class FileConverterFactory
{
public static IFileConverter Create(string extension)
{
extension = type.ToUpper();
Dictionary<string, ConverterConfig> availableConverters = GetConvertersConfig();
if (!availableConverters.ContainsKey(extension))
throw new ArgumentException(string.Format("Unknown extenstion type '{0}'. Check application configuration file.", extension));
ConverterConfig cc = availableConverters[extension];
Assembly runnerAssembly = Assembly.LoadFrom(cc.Assembly);
Type converterType = runnerAssembly.GetType(cc.Class);
IFileConverter converter = (IFileConverter) Activator.CreateInstance(converterType);
return converter;
}
private static Dictionary<string, ConverterConfig> GetConvertersConfig()
{
var configs = (Dictionary<string, ConverterConfig>) ConfigurationManager.GetSection("ConvertersConfig");
return configs;
}
}
public class ConvertersConfigHandler : IConfigurationSectionHandler
{
public object Create(object parent, object configContext, XmlNode section)
{
Dictionary<string, ConverterConfig> converters = new KeyedList<string, ConverterConfig>();
XmlNodeList converterList = section.SelectNodes("Converter");
foreach (XmlNode converterNode in converterList)
{
XmlNode currentConverterNode = converterNode;
ConverterConfig cc = new ConverterConfig();
cc.Type = XML.GetAttribute(ref currentConverterNode, "Type").ToUpper();
cc.Assembly = XML.GetAttribute(ref currentConverterNode, "Assembly");
cc.Class = XML.GetAttribute(ref currentConverterNode, "Class");
converters[cc.Type] = cc;
}
return converters;
}
}
public class ConverterConfig
{
public string Type = "";
public string Assembly = "";
public string Class = "";
}
public class TextConverter : IFileConverter
{
bool Convert(string filePath) { ... }
}
public class PdfConverter : IFileConverter
{
bool Convert(string filePath) { ... }
}
In your app.config file, you add this to the configSections:
<section name = "ConvertersConfig" type = "ConvertersConfigConfigHandler, MyAssembly" />
and this below your configSections:
<ConvertersConfig>
<Converter Type="txt" Assembly="MyAssembly" Class="MyAssembly.TextConverter" />
<Converter Type="pdf" Assembly="MyAssembly" Class="MyAssembly.PdfConverter" />
</ConvertersConfig>
The call would then be like this:
IFileConverter converter = FileConverterFactory.Create("txt");
converter.Convert("c:\temp\myfile");
EDITED the code for giving a solution that is more "generic".
You can use some reflection:
ConvertApi FindMatchingConverter(string _FileExt)
{
//Get all the converters available.
List<ConvertApi> converters = this.GetType()
.Assembly
.GetExportedTypes()
.Where(type => type.IsAssignableFrom(typeof(ConvertApi)))
.ToList();
//Loop and find which converter to use
foreach (var converter in converters)
{
if (converter.SupportedFiles.Contains(_FileExt))
return Activator.CreateInstance(converter);
}
throw new Exception("No converter found");
}
Then you just have to call Convert() on the ConvertApi returned.
Of course, this requires you to add a virtual List<String> named SupportedFiles in your base class.
This makes it look like
public abstract class ConvertApi
{
public abstract void Convert();
public virtual List<String> SupportedFiles {get;set;}
}
What about using a switch?
switch(fileEx){
case "txt": new Text2Pdf().Convert(file); break;
case "doc": new Word2Pdf().Convert(file); break;
}
Use dependency injection where you pass in the supported files in the constructor of the class.
Slower, but more dynamic... use an Object Factory. Here's a good article that seems to fit your needs.
http://www.codeproject.com/Articles/12986/Generic-Object-Factory
The important stuff from the article:
using System.Collections.Generic;
public struct Factory < KeyType, GeneralProduct >
{
//Register a object with the factory
public void> Register< SpecificProduct >(KeyType key)
where SpecificProduct : GeneralProduct, new()
{
if( m_mapProducts == null )
{
m_mapProducts = new SortedList< KeyType, CreateFunctor >();
}
CreateFunctor createFunctor = Creator<SpecificProduct>;
m_mapProducts.Add(key, createFunctor);
}
//Create a registered object
public GeneralProduct Create( KeyType key )
{
CreateFunctor createFunctor = m_mapProducts[ key ];
return createFunctor();
}
private GeneralProduct Creator < SpecificProduct >()
where SpecificProduct : GeneralProduct, new()
{
return new SpecificProduct();
}
private delegate GeneralProduct CreateFunctor();
private SortedList<KeyType, CreateFunctor> m_mapProducts;
}
Usage:
class Fruit
{
}
class Apple : Fruit
{
}
class Orange : Fruit
{
}
class TestClass
{
static void Main(string[] args)
{
General.Factory< string, Fruit > factory;
//Register
factory.Register< Apple >( "Apple" );
factory.Register< Orange>( "Orange" );
//Create
Fruit fruit1 = factory.Create("Apple");
Fruit fruit2 = factory.Create("Orange");
}
}

Categories

Resources