diff Source/ServiceModel/DataService.cs @ 0:f990fcb411a9

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Source/ServiceModel/DataService.cs	Thu Mar 27 21:46:09 2014 +0400
@@ -0,0 +1,467 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Services.Providers;
+using System.Linq;
+using System.Linq.Expressions;
+
+namespace BLToolkit.ServiceModel
+{
+	using Data.Linq;
+	using Data.Sql;
+	using Mapping;
+
+	public class DataService<T> : System.Data.Services.DataService<T>, IServiceProvider
+		where T : IDataContext
+	{
+		#region Init
+
+		public DataService()
+		{
+			if (_defaultMetadata == null)
+				_defaultMetadata = Tuple.Create(default(T), new MetadataInfo(Map.DefaultSchema));
+
+			_metadata = new MetadataProvider(_defaultMetadata.Item2);
+			_query    = new QueryProvider   (_defaultMetadata.Item2);
+			_update   = new UpdateProvider  (_defaultMetadata.Item2, _metadata, _query);
+		}
+
+		static Tuple<T,MetadataInfo> _defaultMetadata;
+
+		public DataService(MappingSchema mappingSchema)
+		{
+			lock (_cache)
+			{
+				Tuple<T,MetadataInfo> data;
+
+				if (!_cache.TryGetValue(mappingSchema, out data))
+					data = Tuple.Create(default(T), new MetadataInfo(mappingSchema));
+
+				_metadata = new MetadataProvider(data.Item2);
+				_query    = new QueryProvider   (data.Item2);
+				_update   = new UpdateProvider  (data.Item2, _metadata, _query);
+			}
+		}
+
+		readonly static Dictionary<MappingSchema,Tuple<T,MetadataInfo>> _cache =
+			new Dictionary<MappingSchema,Tuple<T,MetadataInfo>>();
+
+		readonly MetadataProvider _metadata;
+		readonly QueryProvider    _query;
+		readonly UpdateProvider   _update;
+
+		#endregion
+
+		#region Public Members
+
+		public object GetService(Type serviceType)
+		{
+			if (serviceType == typeof(IDataServiceMetadataProvider)) return _metadata;
+			if (serviceType == typeof(IDataServiceQueryProvider))    return _query;
+			if (serviceType == typeof(IDataServiceUpdateProvider))   return _update;
+
+			return null;
+		}
+
+		#endregion
+
+		#region MetadataInfo
+
+		class TypeInfo
+		{
+			public ResourceType Type;
+			public SqlTable     Table;
+			public ObjectMapper Mapper;
+		}
+
+		class MetadataInfo
+		{
+			public MetadataInfo(MappingSchema mappingSchema)
+			{
+				_mappingSchema = mappingSchema;
+				LoadMetadata();
+			}
+
+			readonly MappingSchema _mappingSchema;
+
+			readonly public Dictionary<Type,TypeInfo>                  TypeDic     = new Dictionary<Type,TypeInfo>();
+			readonly public Dictionary<string,ResourceType>            Types       = new Dictionary<string,ResourceType>();
+			readonly public Dictionary<string,ResourceSet>             Sets        = new Dictionary<string,ResourceSet>();
+			readonly public Dictionary<string,Func<object,IQueryable>> RootGetters = new Dictionary<string,Func<object,IQueryable>>();
+
+			void LoadMetadata()
+			{
+				var n = 0;
+				var list =
+				(
+					from p in typeof(T).GetProperties()
+					let t   = p.PropertyType
+					where t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Table<>)
+					let tt  = t.GetGenericArguments()[0]
+					let tbl = new SqlTable(_mappingSchema, tt)
+					where tbl.Fields.Values.Any(f => f.IsPrimaryKey)
+					let m   = _mappingSchema.GetObjectMapper(tt)
+					select new
+					{
+						p.Name,
+						ID     = n++,
+						Type   = tt,
+						Table  = tbl,
+						Mapper = m
+					}
+				).ToList();
+
+				var baseTypes = new Dictionary<Type,Type>();
+
+				foreach (var item in list)
+					foreach (var m in item.Mapper.InheritanceMapping)
+						if (!baseTypes.ContainsKey(m.Type))
+							baseTypes.Add(m.Type, item.Type);
+
+				list.Sort((x,y) =>
+				{
+					Type baseType;
+
+					if (baseTypes.TryGetValue(x.Type, out baseType))
+						if (y.Type == baseType)
+							return 1;
+
+					if (baseTypes.TryGetValue(y.Type, out baseType))
+						if (x.Type == baseType)
+							return -1;
+
+					return x.ID - y.ID;
+				});
+
+				foreach (var item in list)
+				{
+					Type baseType;
+					baseTypes.TryGetValue(item.Type, out baseType);
+
+					var type = GetTypeInfo(item.Type, baseType, item.Table, item.Mapper);
+					var set  = new ResourceSet(item.Name, type.Type);
+
+					set.SetReadOnly();
+
+					Sets.Add(set.Name, set);
+				}
+
+				foreach (var item in list)
+				{
+					foreach (var m in item.Mapper.InheritanceMapping)
+					{
+						if (!TypeDic.ContainsKey(m.Type))
+						{
+							GetTypeInfo(
+								m.Type,
+								item.Type,
+								new SqlTable(_mappingSchema, item.Type),
+								_mappingSchema.GetObjectMapper(item.Type));
+						}
+					}
+				}
+			}
+
+			TypeInfo GetTypeInfo(Type type, Type baseType, SqlTable table, ObjectMapper mapper)
+			{
+				TypeInfo typeInfo;
+
+				if (!TypeDic.TryGetValue(type, out typeInfo))
+				{
+					var baseInfo = baseType != null ? TypeDic[baseType] : null;
+
+					typeInfo = new TypeInfo
+					{
+						Type   = new ResourceType(
+							type,
+							ResourceTypeKind.EntityType,
+							baseInfo != null ? baseInfo.Type : null,
+							type.Namespace,
+							type.Name,
+							type.IsAbstract),
+						Table  = table,
+						Mapper = mapper,
+					};
+
+					foreach (var field in table.Fields.Values)
+					{
+						if (baseType != null && baseInfo.Table.Fields.ContainsKey(field.Name))
+							continue;
+
+						var kind  = ResourcePropertyKind.Primitive;
+						var ptype = ResourceType.GetPrimitiveResourceType(field.SystemType);
+
+						if (baseType == null && field.IsPrimaryKey)
+							kind |= ResourcePropertyKind.Key;
+
+						var p = new ResourceProperty(field.Name, kind, ptype);
+
+						typeInfo.Type.AddProperty(p);
+					}
+
+					typeInfo.Type.SetReadOnly();
+
+					Types.  Add(typeInfo.Type.FullName, typeInfo.Type);
+					TypeDic.Add(type, typeInfo);
+				}
+
+				return typeInfo;
+			}
+
+			public object CreateInstance(Type type)
+			{
+				return TypeDic[type].Mapper.CreateInstance();
+			}
+		}
+
+		#endregion
+
+		#region MetadataProvider
+
+		class MetadataProvider : IDataServiceMetadataProvider
+		{
+			public MetadataProvider(MetadataInfo data)
+			{
+				_data = data;
+			}
+
+			readonly MetadataInfo _data;
+
+			public bool TryResolveResourceSet(string name, out ResourceSet resourceSet)
+			{
+				return _data.Sets.TryGetValue(name, out resourceSet);
+			}
+
+			public ResourceAssociationSet GetResourceAssociationSet(ResourceSet resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
+			{
+				throw new InvalidOperationException();
+			}
+
+			public bool TryResolveResourceType(string name, out ResourceType resourceType)
+			{
+				return _data.Types.TryGetValue(name, out resourceType);
+			}
+
+			public IEnumerable<ResourceType> GetDerivedTypes(ResourceType resourceType)
+			{
+				return _data.TypeDic[resourceType.InstanceType].Mapper.InheritanceMapping.Select(m => _data.TypeDic[m.Type].Type);
+			}
+
+			public bool HasDerivedTypes(ResourceType resourceType)
+			{
+				return _data.TypeDic[resourceType.InstanceType].Mapper.InheritanceMapping.Count > 0;
+			}
+
+			public bool TryResolveServiceOperation(string name, out ServiceOperation serviceOperation)
+			{
+				serviceOperation = null;
+				return false;
+			}
+
+			public string                        ContainerNamespace { get { return typeof(T).Namespace; } }
+			public string                        ContainerName      { get { return typeof(T).Name;      } }
+			public IEnumerable<ResourceSet>      ResourceSets       { get { return _data.Sets.Values;   } }
+			public IEnumerable<ResourceType>     Types              { get { return _data.Types.Values;  } }
+			public IEnumerable<ServiceOperation> ServiceOperations  { get { yield break;                } }
+		}
+
+		#endregion
+
+		#region QueryProvider
+
+		class QueryProvider : IDataServiceQueryProvider
+		{
+			public QueryProvider(MetadataInfo data)
+			{
+				_data = data;
+			}
+
+			readonly MetadataInfo _data;
+
+			public IQueryable GetQueryRootForResourceSet(ResourceSet resourceSet)
+			{
+				Func<object,IQueryable> func;
+
+				lock (_data.RootGetters)
+				{
+					if (!_data.RootGetters.TryGetValue(resourceSet.Name, out func))
+					{
+						var p = Expression.Parameter(typeof(object), "p");
+						var l = Expression.Lambda<Func<object,IQueryable>>(
+							Expression.PropertyOrField(
+								Expression.Convert(p, typeof(T)),
+								resourceSet.Name),
+							p);
+
+						func = l.Compile();
+
+						_data.RootGetters.Add(resourceSet.Name, func);
+					}
+				}
+
+				return func(CurrentDataSource);
+			}
+
+			public ResourceType GetResourceType(object target)
+			{
+				return _data.TypeDic[target.GetType()].Type;
+			}
+
+			public object GetPropertyValue(object target, ResourceProperty resourceProperty)
+			{
+				throw new InvalidOperationException();
+			}
+
+			public object GetOpenPropertyValue(object target, string propertyName)
+			{
+				throw new InvalidOperationException();
+			}
+
+			public IEnumerable<KeyValuePair<string,object>> GetOpenPropertyValues(object target)
+			{
+				throw new InvalidOperationException();
+			}
+
+			public object InvokeServiceOperation(ServiceOperation serviceOperation, object[] parameters)
+			{
+				throw new InvalidOperationException();
+			}
+
+			public object CurrentDataSource         { get; set; }
+			public bool   IsNullPropagationRequired { get { return true; } }
+		}
+
+		#endregion
+
+		#region UpdateProvider
+
+		abstract class ResourceAction
+		{
+			public object Resource;
+
+			public class Create : ResourceAction {}
+			public class Delete : ResourceAction {}
+			public class Reset  : ResourceAction {}
+
+			public class Update : ResourceAction
+			{
+				public string Property;
+				public object Value;
+			}
+
+		}
+
+		class UpdateProvider : IDataServiceUpdateProvider
+		{
+			#region Init
+
+			public UpdateProvider(MetadataInfo data, MetadataProvider metadata, QueryProvider query)
+			{
+				_data     = data;
+				_metadata = metadata;
+				_query    = query;
+			}
+
+			readonly MetadataInfo         _data;
+			readonly MetadataProvider     _metadata;
+			readonly QueryProvider        _query;
+			readonly List<ResourceAction> _actions = new List<ResourceAction>();
+
+			#endregion
+
+			#region IDataServiceUpdateProvider
+
+			public void SetConcurrencyValues(object resourceCookie, bool? checkForEquality, IEnumerable<KeyValuePair<string,object>> concurrencyValues)
+			{
+				throw new InvalidOperationException();
+			}
+
+			public void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
+			{
+				throw new InvalidOperationException();
+			}
+
+			public void ClearChanges()
+			{
+				_actions.Clear();
+			}
+
+			public object CreateResource(string containerName, string fullTypeName)
+			{
+				ResourceType type;
+
+				if (_metadata.TryResolveResourceType(fullTypeName, out type)) 
+				{
+					var resource = _data.CreateInstance(type.InstanceType);
+					_actions.Add(new ResourceAction.Create { Resource = resource });
+					return resource; 
+				}
+
+				throw new Exception(string.Format("Type '{0}' not found", fullTypeName));
+			}
+
+			public void DeleteResource(object targetResource)
+			{
+				_actions.Add(new ResourceAction.Delete { Resource = targetResource });
+			}
+
+			public object GetResource(IQueryable query, string fullTypeName)
+			{
+				object resource = null;
+
+				foreach (var item in query)
+				{
+					if (resource != null)
+						throw new LinqException("Resource not uniquely identified");
+					resource = item;
+				}
+
+				return resource;
+			}
+
+			public object GetValue(object targetResource, string propertyName)
+			{
+				var m = _data.TypeDic[targetResource.GetType()].Mapper;
+				return m[propertyName, true].GetValue(targetResource);
+			}
+
+			public void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
+			{
+				throw new InvalidOperationException();
+			}
+
+			public object ResetResource(object resource)
+			{
+				_actions.Add(new ResourceAction.Reset { Resource = resource });
+				return resource;
+			}
+
+			public object ResolveResource(object resource)
+			{
+				return resource;
+			}
+
+			public void SaveChanges()
+			{
+				throw new InvalidOperationException();
+			}
+
+			public void SetReference(object targetResource, string propertyName, object propertyValue)
+			{
+				throw new InvalidOperationException();
+			}
+
+			public void SetValue(object targetResource, string propertyName, object propertyValue)
+			{
+				var m = _data.TypeDic[targetResource.GetType()].Mapper;
+
+				m[propertyName, true].SetValue(targetResource, propertyValue);
+
+				_actions.Add(new ResourceAction.Update { Resource = targetResource, Property = propertyName, Value = propertyValue });
+			}
+
+			#endregion
+		}
+
+		#endregion
+	}
+}