Mercurial > pub > bltoolkit
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 } |