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

Копия текущей версии из github
author cin
date Thu, 27 Mar 2014 21:46:09 +0400
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:f990fcb411a9
1 using System;
2 using System.Collections.Generic;
3 using System.Data.Services.Providers;
4 using System.Linq;
5 using System.Linq.Expressions;
6
7 namespace BLToolkit.ServiceModel
8 {
9 using Data.Linq;
10 using Data.Sql;
11 using Mapping;
12
13 public class DataService<T> : System.Data.Services.DataService<T>, IServiceProvider
14 where T : IDataContext
15 {
16 #region Init
17
18 public DataService()
19 {
20 if (_defaultMetadata == null)
21 _defaultMetadata = Tuple.Create(default(T), new MetadataInfo(Map.DefaultSchema));
22
23 _metadata = new MetadataProvider(_defaultMetadata.Item2);
24 _query = new QueryProvider (_defaultMetadata.Item2);
25 _update = new UpdateProvider (_defaultMetadata.Item2, _metadata, _query);
26 }
27
28 static Tuple<T,MetadataInfo> _defaultMetadata;
29
30 public DataService(MappingSchema mappingSchema)
31 {
32 lock (_cache)
33 {
34 Tuple<T,MetadataInfo> data;
35
36 if (!_cache.TryGetValue(mappingSchema, out data))
37 data = Tuple.Create(default(T), new MetadataInfo(mappingSchema));
38
39 _metadata = new MetadataProvider(data.Item2);
40 _query = new QueryProvider (data.Item2);
41 _update = new UpdateProvider (data.Item2, _metadata, _query);
42 }
43 }
44
45 readonly static Dictionary<MappingSchema,Tuple<T,MetadataInfo>> _cache =
46 new Dictionary<MappingSchema,Tuple<T,MetadataInfo>>();
47
48 readonly MetadataProvider _metadata;
49 readonly QueryProvider _query;
50 readonly UpdateProvider _update;
51
52 #endregion
53
54 #region Public Members
55
56 public object GetService(Type serviceType)
57 {
58 if (serviceType == typeof(IDataServiceMetadataProvider)) return _metadata;
59 if (serviceType == typeof(IDataServiceQueryProvider)) return _query;
60 if (serviceType == typeof(IDataServiceUpdateProvider)) return _update;
61
62 return null;
63 }
64
65 #endregion
66
67 #region MetadataInfo
68
69 class TypeInfo
70 {
71 public ResourceType Type;
72 public SqlTable Table;
73 public ObjectMapper Mapper;
74 }
75
76 class MetadataInfo
77 {
78 public MetadataInfo(MappingSchema mappingSchema)
79 {
80 _mappingSchema = mappingSchema;
81 LoadMetadata();
82 }
83
84 readonly MappingSchema _mappingSchema;
85
86 readonly public Dictionary<Type,TypeInfo> TypeDic = new Dictionary<Type,TypeInfo>();
87 readonly public Dictionary<string,ResourceType> Types = new Dictionary<string,ResourceType>();
88 readonly public Dictionary<string,ResourceSet> Sets = new Dictionary<string,ResourceSet>();
89 readonly public Dictionary<string,Func<object,IQueryable>> RootGetters = new Dictionary<string,Func<object,IQueryable>>();
90
91 void LoadMetadata()
92 {
93 var n = 0;
94 var list =
95 (
96 from p in typeof(T).GetProperties()
97 let t = p.PropertyType
98 where t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Table<>)
99 let tt = t.GetGenericArguments()[0]
100 let tbl = new SqlTable(_mappingSchema, tt)
101 where tbl.Fields.Values.Any(f => f.IsPrimaryKey)
102 let m = _mappingSchema.GetObjectMapper(tt)
103 select new
104 {
105 p.Name,
106 ID = n++,
107 Type = tt,
108 Table = tbl,
109 Mapper = m
110 }
111 ).ToList();
112
113 var baseTypes = new Dictionary<Type,Type>();
114
115 foreach (var item in list)
116 foreach (var m in item.Mapper.InheritanceMapping)
117 if (!baseTypes.ContainsKey(m.Type))
118 baseTypes.Add(m.Type, item.Type);
119
120 list.Sort((x,y) =>
121 {
122 Type baseType;
123
124 if (baseTypes.TryGetValue(x.Type, out baseType))
125 if (y.Type == baseType)
126 return 1;
127
128 if (baseTypes.TryGetValue(y.Type, out baseType))
129 if (x.Type == baseType)
130 return -1;
131
132 return x.ID - y.ID;
133 });
134
135 foreach (var item in list)
136 {
137 Type baseType;
138 baseTypes.TryGetValue(item.Type, out baseType);
139
140 var type = GetTypeInfo(item.Type, baseType, item.Table, item.Mapper);
141 var set = new ResourceSet(item.Name, type.Type);
142
143 set.SetReadOnly();
144
145 Sets.Add(set.Name, set);
146 }
147
148 foreach (var item in list)
149 {
150 foreach (var m in item.Mapper.InheritanceMapping)
151 {
152 if (!TypeDic.ContainsKey(m.Type))
153 {
154 GetTypeInfo(
155 m.Type,
156 item.Type,
157 new SqlTable(_mappingSchema, item.Type),
158 _mappingSchema.GetObjectMapper(item.Type));
159 }
160 }
161 }
162 }
163
164 TypeInfo GetTypeInfo(Type type, Type baseType, SqlTable table, ObjectMapper mapper)
165 {
166 TypeInfo typeInfo;
167
168 if (!TypeDic.TryGetValue(type, out typeInfo))
169 {
170 var baseInfo = baseType != null ? TypeDic[baseType] : null;
171
172 typeInfo = new TypeInfo
173 {
174 Type = new ResourceType(
175 type,
176 ResourceTypeKind.EntityType,
177 baseInfo != null ? baseInfo.Type : null,
178 type.Namespace,
179 type.Name,
180 type.IsAbstract),
181 Table = table,
182 Mapper = mapper,
183 };
184
185 foreach (var field in table.Fields.Values)
186 {
187 if (baseType != null && baseInfo.Table.Fields.ContainsKey(field.Name))
188 continue;
189
190 var kind = ResourcePropertyKind.Primitive;
191 var ptype = ResourceType.GetPrimitiveResourceType(field.SystemType);
192
193 if (baseType == null && field.IsPrimaryKey)
194 kind |= ResourcePropertyKind.Key;
195
196 var p = new ResourceProperty(field.Name, kind, ptype);
197
198 typeInfo.Type.AddProperty(p);
199 }
200
201 typeInfo.Type.SetReadOnly();
202
203 Types. Add(typeInfo.Type.FullName, typeInfo.Type);
204 TypeDic.Add(type, typeInfo);
205 }
206
207 return typeInfo;
208 }
209
210 public object CreateInstance(Type type)
211 {
212 return TypeDic[type].Mapper.CreateInstance();
213 }
214 }
215
216 #endregion
217
218 #region MetadataProvider
219
220 class MetadataProvider : IDataServiceMetadataProvider
221 {
222 public MetadataProvider(MetadataInfo data)
223 {
224 _data = data;
225 }
226
227 readonly MetadataInfo _data;
228
229 public bool TryResolveResourceSet(string name, out ResourceSet resourceSet)
230 {
231 return _data.Sets.TryGetValue(name, out resourceSet);
232 }
233
234 public ResourceAssociationSet GetResourceAssociationSet(ResourceSet resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
235 {
236 throw new InvalidOperationException();
237 }
238
239 public bool TryResolveResourceType(string name, out ResourceType resourceType)
240 {
241 return _data.Types.TryGetValue(name, out resourceType);
242 }
243
244 public IEnumerable<ResourceType> GetDerivedTypes(ResourceType resourceType)
245 {
246 return _data.TypeDic[resourceType.InstanceType].Mapper.InheritanceMapping.Select(m => _data.TypeDic[m.Type].Type);
247 }
248
249 public bool HasDerivedTypes(ResourceType resourceType)
250 {
251 return _data.TypeDic[resourceType.InstanceType].Mapper.InheritanceMapping.Count > 0;
252 }
253
254 public bool TryResolveServiceOperation(string name, out ServiceOperation serviceOperation)
255 {
256 serviceOperation = null;
257 return false;
258 }
259
260 public string ContainerNamespace { get { return typeof(T).Namespace; } }
261 public string ContainerName { get { return typeof(T).Name; } }
262 public IEnumerable<ResourceSet> ResourceSets { get { return _data.Sets.Values; } }
263 public IEnumerable<ResourceType> Types { get { return _data.Types.Values; } }
264 public IEnumerable<ServiceOperation> ServiceOperations { get { yield break; } }
265 }
266
267 #endregion
268
269 #region QueryProvider
270
271 class QueryProvider : IDataServiceQueryProvider
272 {
273 public QueryProvider(MetadataInfo data)
274 {
275 _data = data;
276 }
277
278 readonly MetadataInfo _data;
279
280 public IQueryable GetQueryRootForResourceSet(ResourceSet resourceSet)
281 {
282 Func<object,IQueryable> func;
283
284 lock (_data.RootGetters)
285 {
286 if (!_data.RootGetters.TryGetValue(resourceSet.Name, out func))
287 {
288 var p = Expression.Parameter(typeof(object), "p");
289 var l = Expression.Lambda<Func<object,IQueryable>>(
290 Expression.PropertyOrField(
291 Expression.Convert(p, typeof(T)),
292 resourceSet.Name),
293 p);
294
295 func = l.Compile();
296
297 _data.RootGetters.Add(resourceSet.Name, func);
298 }
299 }
300
301 return func(CurrentDataSource);
302 }
303
304 public ResourceType GetResourceType(object target)
305 {
306 return _data.TypeDic[target.GetType()].Type;
307 }
308
309 public object GetPropertyValue(object target, ResourceProperty resourceProperty)
310 {
311 throw new InvalidOperationException();
312 }
313
314 public object GetOpenPropertyValue(object target, string propertyName)
315 {
316 throw new InvalidOperationException();
317 }
318
319 public IEnumerable<KeyValuePair<string,object>> GetOpenPropertyValues(object target)
320 {
321 throw new InvalidOperationException();
322 }
323
324 public object InvokeServiceOperation(ServiceOperation serviceOperation, object[] parameters)
325 {
326 throw new InvalidOperationException();
327 }
328
329 public object CurrentDataSource { get; set; }
330 public bool IsNullPropagationRequired { get { return true; } }
331 }
332
333 #endregion
334
335 #region UpdateProvider
336
337 abstract class ResourceAction
338 {
339 public object Resource;
340
341 public class Create : ResourceAction {}
342 public class Delete : ResourceAction {}
343 public class Reset : ResourceAction {}
344
345 public class Update : ResourceAction
346 {
347 public string Property;
348 public object Value;
349 }
350
351 }
352
353 class UpdateProvider : IDataServiceUpdateProvider
354 {
355 #region Init
356
357 public UpdateProvider(MetadataInfo data, MetadataProvider metadata, QueryProvider query)
358 {
359 _data = data;
360 _metadata = metadata;
361 _query = query;
362 }
363
364 readonly MetadataInfo _data;
365 readonly MetadataProvider _metadata;
366 readonly QueryProvider _query;
367 readonly List<ResourceAction> _actions = new List<ResourceAction>();
368
369 #endregion
370
371 #region IDataServiceUpdateProvider
372
373 public void SetConcurrencyValues(object resourceCookie, bool? checkForEquality, IEnumerable<KeyValuePair<string,object>> concurrencyValues)
374 {
375 throw new InvalidOperationException();
376 }
377
378 public void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
379 {
380 throw new InvalidOperationException();
381 }
382
383 public void ClearChanges()
384 {
385 _actions.Clear();
386 }
387
388 public object CreateResource(string containerName, string fullTypeName)
389 {
390 ResourceType type;
391
392 if (_metadata.TryResolveResourceType(fullTypeName, out type))
393 {
394 var resource = _data.CreateInstance(type.InstanceType);
395 _actions.Add(new ResourceAction.Create { Resource = resource });
396 return resource;
397 }
398
399 throw new Exception(string.Format("Type '{0}' not found", fullTypeName));
400 }
401
402 public void DeleteResource(object targetResource)
403 {
404 _actions.Add(new ResourceAction.Delete { Resource = targetResource });
405 }
406
407 public object GetResource(IQueryable query, string fullTypeName)
408 {
409 object resource = null;
410
411 foreach (var item in query)
412 {
413 if (resource != null)
414 throw new LinqException("Resource not uniquely identified");
415 resource = item;
416 }
417
418 return resource;
419 }
420
421 public object GetValue(object targetResource, string propertyName)
422 {
423 var m = _data.TypeDic[targetResource.GetType()].Mapper;
424 return m[propertyName, true].GetValue(targetResource);
425 }
426
427 public void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
428 {
429 throw new InvalidOperationException();
430 }
431
432 public object ResetResource(object resource)
433 {
434 _actions.Add(new ResourceAction.Reset { Resource = resource });
435 return resource;
436 }
437
438 public object ResolveResource(object resource)
439 {
440 return resource;
441 }
442
443 public void SaveChanges()
444 {
445 throw new InvalidOperationException();
446 }
447
448 public void SetReference(object targetResource, string propertyName, object propertyValue)
449 {
450 throw new InvalidOperationException();
451 }
452
453 public void SetValue(object targetResource, string propertyName, object propertyValue)
454 {
455 var m = _data.TypeDic[targetResource.GetType()].Mapper;
456
457 m[propertyName, true].SetValue(targetResource, propertyValue);
458
459 _actions.Add(new ResourceAction.Update { Resource = targetResource, Property = propertyName, Value = propertyValue });
460 }
461
462 #endregion
463 }
464
465 #endregion
466 }
467 }