I have written one Caching class but i am confused how do i use it in application in MVC ?
using System;
using System.Configuration;
using System.Runtime.Caching;
namespace ABC
{
public class InMemoryCache : ICache
{
protected ObjectCache Cache
{
get
{
return MemoryCache.Default;
}
}
/// Insert value into the cache using key, Value pairs
public void Add<T>(T o, string key)
{
if (o == null)
return;
var policy = new CacheItemPolicy();
policy.AbsoluteExpiration = DateTime.Now.AddMinutes(Convert.ToInt32(ConfigurationManager.AppSettings["CacheExpirationTime"]));
Cache.Add(new CacheItem(key, o), policy);
}
public void Update<T>(T o, string key)
{
if (Cache.Contains(key))
{
Cache[key] = o;
}
else
{
Add(o, key);
}
}
/// Remove item from cache
public void Remove(string key)
{
Cache.Remove(key);
}
/// Check for item in cache
public bool Exists(string key)
{
return Cache[key] != null;
}
public void Clear()
{
foreach (var item in Cache)
Remove(item.Key);
}
/// Retrieve cached item.Default(T) if item doesn't exist
public bool Get<T>(string key, out T value)
{
try
{
if (!Exists(key))
{
value = default(T);
return false;
}
value = (T)Cache[key];
}
catch
{
value = default(T);
return false;
}
return true;
}
public T Get<T>(string key)
{
try
{
return (T)Cache[key];
}
catch
{
return default(T);
}
}
}
}
I have BaseController where i have written OnAuthentication()
//How can i use Caching here as it hits on every click made in application
protected override void OnAuthentication(AuthenticationContext filterContext)
{
Check.NotEmpty(SsoId, "Unable to determine your AmexWeb logon identity. Please contact the system administrator.");
var requestingUser = new RBACUser(SsoId, _sprocService);
//How can i use Caching here as it hits on every click made in application
UserPermissions = requestingUser.UserPermissions;
PermissionsMaster = requestingUser.PermissionsMaster;
var user = LoggedInUser = requestingUser.User;
if (user == null)
{
if (HttpContext.Session != null)
{
HttpContext.Session["User"] = null;
HttpContext.Session["UserName"] = null;
}
}
else
{
if (HttpContext.Session != null)
{
HttpContext.Session["User"] = user.ADSId;
HttpContext.Session["UserName"] = user.FullName;
}
_userId = user.UserId;
_regionCode = user.Segment.RegionCode;
}
}
You could try to implement caching in OnActionExecuting() method so it would work only once per action.
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Work with a cache here
base.OnActionExecuting(filterContext);
}
}
I need to build a cache of one of my custom class objects(security as in finance). The object of the class can be identified in multiple ways since it has multiple identifiers like PKey, Name, Long Name, or any other type of name in different language.
So the cache should be searchable on the basis of any of the identifiers i.e. multiple keys.
How can I create such a cache?
I am using C# 4.5 but I think this is a generic problem, so solutions in any way can help
One of the solutions which I think is fast and efficient is to keep different caches for different identifiers. For e.g. < Name,Pkey>, < Long Name,Pkey>, < identifier,Pkey> and a single cache with objects of type < Pkey,Object>
Are there newer and better ways to do this?
Edit: When I am accessing the cache all the keys are not known, so I
can not make a composite key
Maybe this code will be useful:
public class Test12 {
static Map<CacheKey, SecureObject> cache = new ConcurrentHashMap<CacheKey, SecureObject>();
public static void main(String[] args) {
SecureObject secureObject = new SecureObject(new CacheKey("pkey1", "name1", 1L));
cache.put(secureObject.getKey(), secureObject);
secureObject = new SecureObject(new CacheKey("pkey1", "name2", 2L));
if (cache.containsKey(secureObject.getKey())) {
System.out.println("Already in cache.");
}
}
}
class CacheKey {
String pkey;
String name;
Long longName;
CacheKey(String pkey, String name, Long longName) {
this.pkey = pkey;
this.name = name;
this.longName = longName;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CacheKey cacheKey = (CacheKey) o;
if (pkey != null && pkey.equals(cacheKey.pkey)) {
return true;
} else {
if (name != null && name.equals(cacheKey.name)) {
return true;
} else {
if (longName != null && longName.equals(cacheKey.longName)) {
return true;
} else {
return false;
}
}
}
}
#Override
public int hashCode() {
int result = 0;
if (pkey != null) {
result = pkey.hashCode();
} else if (name != null) {
result = name.hashCode();
} else if (longName != null) {
result = longName.hashCode();
}
return result;
}
}
class SecureObject {
CacheKey key;
SecureObject(CacheKey key) {
this.key = key;
}
public CacheKey getKey() {
return key;
}
public void setKey(CacheKey key) {
this.key = key;
}
}
Prints:
Already in cache.
This is not complete solution. This is direction only.
I have a simple form which takes a user data and save it to database. while it is working fine but seems pretty repetitive.. i am not sure if i can turn my code in some way that it checks for user input and save it in DB.
here is my code..
public void SaveUserData()
{
MWCompetitionContestantsDetails user = new MWCompetitionContestantsDetails();
MWCompetitionsEntryDetails entry = new MWCompetitionsEntryDetails();
if (!IsNullOrEmpty(firstNameText.Value))
user.FirstName = firstNameText.Value;
if (!IsNullOrEmpty(lastNameText.Value))
user.LastName = lastNameText.Value;
if (!IsNullOrEmpty(emailText.Value))
user.UserEmailAddress = emailText.Value;
if (!IsNullOrEmpty(address1Text.Value))
user.Address1= address1Text.Value;
if (!IsNullOrEmpty(address2Text.Value))
user.Address2 = address2Text.Value;
if (!IsNullOrEmpty(cityText.Value))
user.City = cityText.Value;
if (!IsNullOrEmpty(provinceText.Value))
user.Province= provinceText.Value;
if (!IsNullOrEmpty(postCodeText.Value))
user.PostalCode = postCodeText.Value;
if (!IsNullOrEmpty(countryText.SelectedItem.Text))
user.Country = countryText.SelectedItem.Text;
user.Save();
}
public static bool IsNullOrEmpty<T>(T value)
{
if (typeof(T) == typeof(string))
return string.IsNullOrEmpty(value as string);
return value.Equals(default(T));
}
rather then looking for IsNullOrEmpty(data) on every fields, are there any suggestion to improve my code using Linq or anything..
your time much appreciated...
You can check If null or empty at the setter of your properties:
public class MWCompetitionContestantsDetails
{
private string _firstName;
public string FirstName
{
get
{
return this._firstName;
}
set
{
if(!IsNullOrEmpty(value))
{
this._firstName = value;
}
}
}
.........
public static bool IsNullOrEmpty<T>(T value)
{
if (typeof(T) == typeof(string))
return string.IsNullOrEmpty(value as string);
return value.Equals(default(T));
}
}
First of all, I'd like to say that I like oniant's and Adil Mammadov's solutions as they are deadly simple.
However I've already written code of fancy-shmancy approach I like to use in similar situations and I'd like to share it:
class Save<T> : ISave
{
private readonly System.Action<T> _assignValue;
private readonly System.Func<T> _getValue;
public void Do()
{
T value = _getValue();
if (!IsNullOrEmpty(value))
{
_assignValue(value);
}
}
public static Save<T> Value<T>(System.Func<T> getValue, System.Action<T> assignValue)
{
return new Save<T>(getValue, assignValue);
}
private Save(System.Func<T> getValue, System.Action<T> assignValue)
{
_getValue = getValue;
_assignValue = assignValue;
}
private static bool IsNullOrEmpty<T>(T value)
{
if (typeof(T) == typeof(string))
return string.IsNullOrEmpty(value as string);
return value.Equals(default(T));
}
}
internal interface ISave
{
void Do();
}
public static void SaveUserData()
{
MWCompetitionContestantsDetails user = new MWCompetitionContestantsDetails();
MWCompetitionsEntryDetails entry = new MWCompetitionsEntryDetails();
new List<ISave>
{
Save<string>.Value( () => firstNameText.Value, x => user.FirstName = x),
Save<string>.Value( () => lastNameText.Value, x => user.LastName = x),
Save<int>.Value( () => age.Value, x => user.Age = x),// int's supported :)
// etc
}
.ForEach(x => x.Do());
user.Save();
}
As long as your properties are string, you may use the object initialization :
MWCompetitionContestantsDetails user = new MWCompetitionContestantsDetails(){
FirstName = firstNameText.Value,
LastName = lastNameText.Value,
UserEmailAddress = emailText.Value,
Address1= address1Text.Value,
Address2 = address2Text.Value,
City = cityText.Value,
Province= provinceText.Value,
PostalCode = postCodeText.Value,
Country = countryText.SelectedItem.Text
};
user.Save();
I'm writing a custom Route class and are then using return RedirectToRoute("MyRouteName").
How can I know that my own route was specially requested within the route class?
public bool IsRouteRequested()
{}
Would return true for RedirecToRoute("MyRoute") and false for RedirectToAction("SomeAction", "SomeController").
Is it also possible to detect if RedirectToAction is invoked from within an area?
You can just define a route value like this
return RedirectToRoute("myRoute", new { myVal = "val" });
and then check if it is available for ex in GetVirtualPath
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
if(values["myVal"] != null)....
Maybe you serve, I do not like much but it is the only thing I can think of:
public static class RedirectToRouteResultExtensions
{
private static IDictionary<RedirectToRouteResult, bool> typeRoute;
public static RedirectToRouteResult SetRouteRequested(this RedirectToRouteResult redirectToRouteResult, bool value)
{
if (typeRoute == null)
{
typeRoute = new Dictionary<RedirectToRouteResult, bool>();
}
typeRoute[redirectToRouteResult] = value;
return redirectToRouteResult;
}
public static bool IsRouteRequested(this RedirectToRouteResult redirectToRouteResult)
{
if (typeRoute == null)
{
return false;
}
return typeRoute.ContainsKey(redirectToRouteResult)
? typeRoute[redirectToRouteResult]
: false;
}
}
Example:
return RedirectToRoute("MyRouteName").SetRouteRequested(true);
return RedirectToAction("SomeAction", "SomeController");
* Edit solution II *
Prior to return RedirectToRoute("MyRouteName"), You can add information in the header of the response:
HttpContext.Response.Headers.Add("CustomRoute", "1");
return RedirectToRoute("MyRouteName");
...
public bool IsRouteRequested()
{
if (HttpContext.Response.Headers["CustomRoute"] != null &&
HttpContext.Response.Headers["CustomRoute"] == "1")
return true;
return false;
}
I'm trying to expose a model to be available for OData services. The approach I'm currently taking is along the lines of:
1) Defining a class in the model to expose IQueryable collections such as:
public class MyEntities
{
public IQueryable<Customer> Customers
{
get
{
return DataManager.GetCustomers().AsQueryable<Customer>();
}
}
public IQueryable<User> Users
{
get
{
return DataManager.GetUsers().AsQueryable<User>();
}
}
}
2) Set up a WCF DataService with the queryable collection class such as:
public class MyDataService : DataService<MyEntities>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Customers", EntitySetRights.All);
config.SetEntitySetAccessRule("Users", EntitySetRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
}
I'm running into 3 issues and/or limitations with this approach:
1) I'm unable to add any derived class collections to the IQueryable lists.
2) I must apply the IgnoreProperties attribute to hide any members that are derived from a base type.
3) I'm unable to prevent unwanted entities from being accessed by the OData service and causing errors. For example, I only want BLL layer objects to be exposed, but it seems like the model is being reflected far beyond the members of the classes I added to the queryable list, and picking up all the DAL classes, causing errors being undefined and also having the same name as the BLL classes. There are no links to DAL classes from BLL class members. At the very least, I would like to have these classes ignored altogether.
Any pointers on how to address any of these issues would be greatly appreciated. Should I be doing a different approach on this? For example, should I implement IQueryable directly in my model collections?
Thanks.
The reflection provider which you're using is designed to walk all public types/properties. So the #3 and probably even #2 (which I don't fully understand what's the problem) are by design because of that.
#1 is also by design but for a different reason - the reflection provider can only expose one entity set for each type hierarchy. It doesn't support so called "MEST" (Multiple Entity Sets per Type), because it would not know which one to pick. It needs a 1 to 1 mapping between entity types and entity sets.
The reflection provider is meant for simple services which are "Easy" to setup. It's definitely not designed for customizations.
If you want greater control, then you need custom provider, which can be either implemented directly (if it's based on existing CLR classes it's not that hard), or through some library, like the one suggested in the comments above.
The reflection provider is not designed to handle rich data models with a fair amount of inheritance and other dependences. I ended up building a custom provider that could handle queries, updates, inheritance, and relationships based on Alex James' excellent blog post on Creating a Data Service Provider.
An example implementation with 3 CLR classes: ResidentialCustomer, Customer, and User is provided below. ResidentialCustomer extends Customer, Customer has a list of Users, and User has a reference back to Customer.
An interface for DataContext classes such as:
public interface IODataContext
{
IQueryable GetQueryable(ResourceSet set);
object CreateResource(ResourceType resourceType);
void AddResource(ResourceType resourceType, object resource);
void DeleteResource(object resource);
void SaveChanges();
}
A class to implement IDataServiceMetadataProvider such as:
public class ODataServiceMetadataProvider : IDataServiceMetadataProvider
{
private Dictionary<string, ResourceType> resourceTypes = new Dictionary<string, ResourceType>();
private Dictionary<string, ResourceSet> resourceSets = new Dictionary<string, ResourceSet>();
private List<ResourceAssociationSet> _associationSets = new List<ResourceAssociationSet>();
public string ContainerName
{
get { return "MyDataContext"; }
}
public string ContainerNamespace
{
get { return "MyNamespace"; }
}
public IEnumerable<ResourceSet> ResourceSets
{
get { return this.resourceSets.Values; }
}
public IEnumerable<ServiceOperation> ServiceOperations
{
get { yield break; }
}
public IEnumerable<ResourceType> Types
{
get { return this.resourceTypes.Values; }
}
public bool TryResolveResourceSet(string name, out ResourceSet resourceSet)
{
return resourceSets.TryGetValue(name, out resourceSet);
}
public bool TryResolveResourceType(string name, out ResourceType resourceType)
{
return resourceTypes.TryGetValue(name, out resourceType);
}
public bool TryResolveServiceOperation(string name, out ServiceOperation serviceOperation)
{
serviceOperation = null;
return false;
}
public void AddResourceType(ResourceType type)
{
type.SetReadOnly();
resourceTypes.Add(type.FullName, type);
}
public void AddResourceSet(ResourceSet set)
{
set.SetReadOnly();
resourceSets.Add(set.Name, set);
}
public bool HasDerivedTypes(ResourceType resourceType)
{
if (resourceType.InstanceType == typeof(ResidentialCustomer))
{
return true;
}
return false;
}
public IEnumerable<ResourceType> GetDerivedTypes(ResourceType resourceType)
{
List<ResourceType> derivedResourceTypes = new List<ResourceType>();
if (resourceType.InstanceType == typeof(ResidentialCustomer))
{
foreach (ResourceType resource in Types)
{
if (resource.InstanceType == typeof(Customer))
{
derivedResourceTypes.Add(resource);
}
}
}
return derivedResourceTypes;
}
public void AddAssociationSet(ResourceAssociationSet associationSet)
{
_associationSets.Add(associationSet);
}
public ResourceAssociationSet GetResourceAssociationSet(ResourceSet resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
{
return resourceProperty.CustomState as ResourceAssociationSet;
}
public ODataServiceMetadataProvider() { }
}
A class to implement IDataServiceQueryProvider such as:
public class ODataServiceQueryProvider<T> : IDataServiceQueryProvider where T : IODataContext
{
T _currentDataSource;
IDataServiceMetadataProvider _metadata;
public object CurrentDataSource
{
get
{
return _currentDataSource;
}
set
{
_currentDataSource = (T)value;
}
}
public bool IsNullPropagationRequired
{
get { return true; }
}
public object GetOpenPropertyValue(object target, string propertyName)
{
throw new NotImplementedException();
}
public IEnumerable<KeyValuePair<string, object>> GetOpenPropertyValues(object target)
{
throw new NotImplementedException();
}
public object GetPropertyValue(object target, ResourceProperty resourceProperty)
{
throw new NotImplementedException();
}
public IQueryable GetQueryRootForResourceSet(ResourceSet resourceSet)
{
return _currentDataSource.GetQueryable(resourceSet);
}
public ResourceType GetResourceType(object target)
{
Type type = target.GetType();
return _metadata.Types.Single(t => t.InstanceType == type);
}
public object InvokeServiceOperation(ServiceOperation serviceOperation, object[] parameters)
{
throw new NotImplementedException();
}
public ODataServiceQueryProvider(IDataServiceMetadataProvider metadata)
{
_metadata = metadata;
}
}
A class to implement IDataServiceUpdateProvider such as:
public class ODataServiceUpdateProvider<T> : IDataServiceUpdateProvider where T : IODataContext
{
private IDataServiceMetadataProvider _metadata;
private ODataServiceQueryProvider<T> _query;
private List<Action> _actions;
public T GetContext()
{
return ((T)_query.CurrentDataSource);
}
public void SetConcurrencyValues(object resourceCookie, bool? checkForEquality, IEnumerable<KeyValuePair<string, object>> concurrencyValues)
{
throw new NotImplementedException();
}
public void SetReference(object targetResource, string propertyName, object propertyValue)
{
_actions.Add(() => ReallySetReference(targetResource, propertyName, propertyValue));
}
public void ReallySetReference(object targetResource, string propertyName, object propertyValue)
{
targetResource.SetPropertyValue(propertyName, propertyValue);
}
public void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
{
_actions.Add(() => ReallyAddReferenceToCollection(targetResource, propertyName, resourceToBeAdded));
}
public void ReallyAddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
{
var collection = targetResource.GetPropertyValue(propertyName);
if (collection is IList)
{
(collection as IList).Add(resourceToBeAdded);
}
}
public void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
{
_actions.Add(() => ReallyRemoveReferenceFromCollection(targetResource, propertyName, resourceToBeRemoved));
}
public void ReallyRemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
{
var collection = targetResource.GetPropertyValue(propertyName);
if (collection is IList)
{
(collection as IList).Remove(resourceToBeRemoved);
}
}
public void ClearChanges()
{
_actions.Clear();
}
public void SaveChanges()
{
foreach (var a in _actions)
a();
GetContext().SaveChanges();
}
public object CreateResource(string containerName, string fullTypeName)
{
ResourceType type = null;
if (_metadata.TryResolveResourceType(fullTypeName, out type))
{
var context = GetContext();
var resource = context.CreateResource(type);
_actions.Add(() => context.AddResource(type, resource));
return resource;
}
throw new Exception(string.Format("Type {0} not found", fullTypeName));
}
public void DeleteResource(object targetResource)
{
_actions.Add(() => GetContext().DeleteResource(targetResource));
}
public object GetResource(IQueryable query, string fullTypeName)
{
var enumerator = query.GetEnumerator();
if (!enumerator.MoveNext())
throw new Exception("Resource not found");
var resource = enumerator.Current;
if (enumerator.MoveNext())
throw new Exception("Resource not uniquely identified");
if (fullTypeName != null)
{
ResourceType type = null;
if (!_metadata.TryResolveResourceType(fullTypeName, out type))
throw new Exception("ResourceType not found");
if (!type.InstanceType.IsAssignableFrom(resource.GetType()))
throw new Exception("Unexpected resource type");
}
return resource;
}
public object ResetResource(object resource)
{
_actions.Add(() => ReallyResetResource(resource));
return resource;
}
public void ReallyResetResource(object resource)
{
var clrType = resource.GetType();
ResourceType resourceType = _metadata.Types.Single(t => t.InstanceType == clrType);
var resetTemplate = GetContext().CreateResource(resourceType);
foreach (var prop in resourceType.Properties
.Where(p => (p.Kind & ResourcePropertyKind.Key) != ResourcePropertyKind.Key))
{
var clrProp = clrType.GetProperties().Single(p => p.Name == prop.Name);
var defaultPropValue = clrProp.GetGetMethod().Invoke(resetTemplate, new object[] { });
clrProp.GetSetMethod().Invoke(resource, new object[] { defaultPropValue });
}
}
public object ResolveResource(object resource)
{
return resource;
}
public object GetValue(object targetResource, string propertyName)
{
var value = targetResource.GetType().GetProperties().Single(p => p.Name == propertyName).GetGetMethod().Invoke(targetResource, new object[] { });
return value;
}
public void SetValue(object targetResource, string propertyName, object propertyValue)
{
targetResource.GetType().GetProperties().Single(p => p.Name == propertyName).GetSetMethod().Invoke(targetResource, new[] { propertyValue });
}
public ODataServiceUpdateProvider(IDataServiceMetadataProvider metadata, ODataServiceQueryProvider<T> query)
{
_metadata = metadata;
_query = query;
_actions = new List<Action>();
}
}
A class to implement IServiceProvider such as:
public class ODataService<T> : DataService<T>, IServiceProvider where T : IODataContext
{
private ODataServiceMetadataProvider _metadata;
private ODataServiceQueryProvider<T> _query;
private ODataServiceUpdateProvider<T> _updater;
public object GetService(Type serviceType)
{
if (serviceType == typeof(IDataServiceMetadataProvider))
{
return _metadata;
}
else if (serviceType == typeof(IDataServiceQueryProvider))
{
return _query;
}
else if (serviceType == typeof(IDataServiceUpdateProvider))
{
return _updater;
}
else
{
return null;
}
}
public ODataServiceMetadataProvider GetMetadataProvider(Type dataSourceType)
{
ODataServiceMetadataProvider metadata = new ODataServiceMetadataProvider();
ResourceType customer = new ResourceType(
typeof(Customer),
ResourceTypeKind.EntityType,
null,
"MyNamespace",
"Customer",
false
);
ResourceProperty customerCustomerID = new ResourceProperty(
"CustomerID",
ResourcePropertyKind.Key |
ResourcePropertyKind.Primitive,
ResourceType.GetPrimitiveResourceType(typeof(Guid))
);
customer.AddProperty(customerCustomerID);
ResourceProperty customerCustomerName = new ResourceProperty(
"CustomerName",
ResourcePropertyKind.Primitive,
ResourceType.GetPrimitiveResourceType(typeof(string))
);
customer.AddProperty(customerCustomerName);
ResourceType residentialCustomer = new ResourceType(
typeof(ResidentialCustomer),
ResourceTypeKind.EntityType,
customer,
"MyNamespace",
"ResidentialCustomer",
false
);
ResourceType user = new ResourceType(
typeof(User),
ResourceTypeKind.EntityType,
null,
"MyNamespace",
"User",
false
);
ResourceProperty userUserID = new ResourceProperty(
"UserID",
ResourcePropertyKind.Key |
ResourcePropertyKind.Primitive,
ResourceType.GetPrimitiveResourceType(typeof(Guid))
);
user.AddProperty(userUserID);
ResourceProperty userCustomerID = new ResourceProperty(
"CustomerID",
ResourcePropertyKind.Primitive,
ResourceType.GetPrimitiveResourceType(typeof(Guid))
);
user.AddProperty(userCustomerID);
ResourceProperty userEmailAddress = new ResourceProperty(
"EmailAddress",
ResourcePropertyKind.Primitive,
ResourceType.GetPrimitiveResourceType(typeof(string))
);
user.AddProperty(userEmailAddress);
var customerSet = new ResourceSet("Customers", customer);
var residentialCustomerSet = new ResourceSet("ResidentialCustomers", residentialCustomer);
var userSet = new ResourceSet("Users", user);
var userCustomer = new ResourceProperty(
"Customer",
ResourcePropertyKind.ResourceReference,
customer
);
user.AddProperty(userCustomer);
var customerUserList = new ResourceProperty(
"UserList",
ResourcePropertyKind.ResourceSetReference,
user
);
customer.AddProperty(customerUserList);
metadata.AddResourceType(customer);
metadata.AddResourceSet(customerSet);
metadata.AddResourceType(residentialCustomer);
metadata.AddResourceSet(residentialCustomerSet);
metadata.AddResourceType(user);
metadata.AddResourceSet(userSet);
ResourceAssociationSet customerUserListSet = new ResourceAssociationSet(
"CustomerUserList",
new ResourceAssociationSetEnd(
customerSet,
customer,
customerUserList
),
new ResourceAssociationSetEnd(
userSet,
user,
userCustomer
)
);
customerUserList.CustomState = customerUserListSet;
userCustomer.CustomState = customerUserListSet;
metadata.AddAssociationSet(customerUserListSet);
return metadata;
}
public ODataServiceQueryProvider<T> GetQueryProvider(ODataServiceMetadataProvider metadata)
{
return new ODataServiceQueryProvider<T>(metadata);
}
public ODataServiceUpdateProvider<T> GetUpdateProvider(ODataServiceMetadataProvider metadata, ODataServiceQueryProvider<T> query)
{
return new ODataServiceUpdateProvider<T>(metadata, query);
}
public ODataService()
{
_metadata = GetMetadataProvider(typeof(T));
_query = GetQueryProvider(_metadata);
_updater = GetUpdateProvider(_metadata, _query);
}
}
The DataContext class holds the CLR collections and wires up the service operations such as:
public partial class MyDataContext: IODataContext
{
private List<Customer> _customers = null;
public List<Customer> Customers
{
get
{
if (_customers == null)
{
_customers = DataManager.GetCustomers);
}
return _customers;
}
}
private List<ResidentialCustomer> _residentialCustomers = null;
public List<ResidentialCustomer> ResidentialCustomers
{
get
{
if (_residentialCustomers == null)
{
_residentialCustomers = DataManager.GetResidentialCustomers();
}
return _residentialCustomers;
}
}
private List<User> _users = null;
public List<User> Users
{
get
{
if (_users == null)
{
_users = DataManager.GetUsers();
}
return _users;
}
}
public IQueryable GetQueryable(ResourceSet set)
{
if (set.Name == "Customers") return Customers.AsQueryable();
if (set.Name == "ResidentialCustomers") return ResidentialCustomers.AsQueryable();
if (set.Name == "Users") return Users.AsQueryable();
throw new NotSupportedException(string.Format("{0} not found", set.Name));
}
public object CreateResource(ResourceType resourceType)
{
if (resourceType.InstanceType == typeof(Customer))
{
return new Customer();
}
if (resourceType.InstanceType == typeof(ResidentialCustomer))
{
return new ResidentialCustomer();
}
if (resourceType.InstanceType == typeof(User))
{
return new User();
}
throw new NotSupportedException(string.Format("{0} not found for creating.", resourceType.FullName));
}
public void AddResource(ResourceType resourceType, object resource)
{
if (resourceType.InstanceType == typeof(Customer))
{
Customer i = resource as Customer;
if (i != null)
{
Customers.Add(i);
return;
}
}
if (resourceType.InstanceType == typeof(ResidentialCustomer))
{
ResidentialCustomeri = resource as ResidentialCustomer;
if (i != null)
{
ResidentialCustomers.Add(i);
return;
}
}
if (resourceType.InstanceType == typeof(User))
{
Useri = resource as User;
if (i != null)
{
Users.Add(i);
return;
}
}
throw new NotSupportedException(string.Format("{0} not found for adding.", resourceType.FullName));
}
public void DeleteResource(object resource)
{
if (resource.GetType() == typeof(Customer))
{
Customers.Remove(resource as Customer);
return;
}
if (resource.GetType() == typeof(ResidentialCustomer))
{
ResidentialCustomers.Remove(resource as ResidentialCustomer);
return;
}
if (resource.GetType() == typeof(User))
{
Users.Remove(resource as User);
return;
}
throw new NotSupportedException(string.Format("{0} not found for deletion.", resource.GetType().FullName));
}
public void SaveChanges()
{
foreach (var item in Customers.Where(i => i.IsModified == true))
item.Save();
foreach (var item in ResidentialCustomers.Where(i => i.IsModified == true))
item.Save();
foreach (var item in Users.Where(i => i.IsModified == true))
item.Save();
}
}
Then, create your data service using the custom data service class and your data context, such as:
public class MyDataService : ODataService<MyDataContext>
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("Customers", EntitySetRights.All);
config.SetEntitySetAccessRule("ResidentialCustomers", EntitySetRights.All);
config.SetEntitySetAccessRule("Users", EntitySetRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
config.DataServiceBehavior.AcceptProjectionRequests = true;
}
}
Lots of wiring up, but pretty straightforward once you've got the hang of it.