Dreamine.MVVM.Locators 1.0.10
Dreamine.MVVM.Locators 프로젝트의 API와 구성 요소를 제공합니다.
로딩중...
검색중...
일치하는것 없음
ViewModelLocator.cs
이 파일의 문서화 페이지로 가기
1using Dreamine.MVVM.Interfaces.Locators;
2using System.Reflection;
3
5{
14 public static class ViewModelLocator
15 {
24 private static readonly object _syncRoot = new();
33 private static readonly Dictionary<Type, Type> _map = new(32);
42 private static readonly Dictionary<Type, Type?> _viewModelTypeCache = new(32);
51 private static readonly Dictionary<Type, Type?> _viewTypeCache = new(32);
60 private static IViewModelResolver? _resolver;
69 private static Type[]? _allLoadableTypesCache;
70
80 {
81 AppDomain.CurrentDomain.AssemblyLoad += (_, _) => ClearTypeCache();
82 }
83
116 public static void Register(Type viewType, Type viewModelType)
117 {
118 ArgumentNullException.ThrowIfNull(viewType);
119 ArgumentNullException.ThrowIfNull(viewModelType);
120
121 lock (_syncRoot)
122 {
123 _map[viewType] = viewModelType;
124 _viewModelTypeCache[viewType] = viewModelType;
125 }
126 }
127
152 public static void RegisterResolver(IViewModelResolver resolver)
153 {
154 ArgumentNullException.ThrowIfNull(resolver);
155
156 lock (_syncRoot)
157 {
158 _resolver = resolver;
159 }
160 }
161
170 public static void ClearResolver()
171 {
172 lock (_syncRoot)
173 {
174 _resolver = null;
175 }
176 }
177
186 public static void Clear()
187 {
188 lock (_syncRoot)
189 {
190 _map.Clear();
191 _viewModelTypeCache.Clear();
192 _viewTypeCache.Clear();
194 }
195 }
196
205 public static void Reset()
206 {
207 lock (_syncRoot)
208 {
209 _map.Clear();
210 _viewModelTypeCache.Clear();
211 _viewTypeCache.Clear();
213 _resolver = null;
214 }
215 }
216
257 public static object? Resolve(Type viewType)
258 {
259 ArgumentNullException.ThrowIfNull(viewType);
260
261 Type? mappedType;
262 lock (_syncRoot)
263 {
264 _map.TryGetValue(viewType, out mappedType);
265 }
266
267 if (mappedType is not null)
268 {
269 return CreateInstance(mappedType);
270 }
271
272 Type? resolvedType = FindViewModelType(viewType);
273 if (resolvedType is null)
274 {
275 return null;
276 }
277
278 Register(viewType, resolvedType);
279 return CreateInstance(resolvedType);
280 }
281
314 public static object? ResolveView(Type viewModelType)
315 {
316 ArgumentNullException.ThrowIfNull(viewModelType);
317
318 Type? viewType = FindViewType(viewModelType);
319 return viewType != null
320 ? Activator.CreateInstance(viewType)
321 : null;
322 }
323
348 public static void RegisterAll(Assembly assembly)
349 {
350 ArgumentNullException.ThrowIfNull(assembly);
351
352 Type[] allTypes = GetLoadableTypes(assembly)
353 .Where(t => t is { IsClass: true, IsAbstract: false })
354 .ToArray();
355
356 IEnumerable<Type> viewCandidates = allTypes.Where(ViewNamingConvention.IsLikelyViewType);
357
358 foreach (Type viewType in viewCandidates)
359 {
360 bool alreadyRegistered;
361 lock (_syncRoot)
362 {
363 alreadyRegistered = _map.ContainsKey(viewType);
364 }
365
366 if (alreadyRegistered)
367 {
368 continue;
369 }
370
371 Type? viewModelType = FindViewModelType(viewType, allTypes);
372 if (viewModelType != null)
373 {
374 Register(viewType, viewModelType);
375 }
376 }
377 }
378
411 private static Type? FindViewModelType(Type viewType, Type[]? cachedTypes = null)
412 {
413 if (cachedTypes is null)
414 {
415 lock (_syncRoot)
416 {
417 if (_viewModelTypeCache.TryGetValue(viewType, out Type? cachedViewModelType))
418 {
419 return cachedViewModelType;
420 }
421 }
422 }
423
424 Type[] allTypes = cachedTypes ?? GetAllLoadableTypes();
425 string[] candidateNames = ViewNamingConvention.GetViewModelTypeNameCandidates(viewType);
426
427 foreach (string candidateName in candidateNames)
428 {
429 Type? match = allTypes.FirstOrDefault(t =>
430 t.FullName == candidateName ||
431 t.Name == candidateName);
432
433 if (match != null)
434 {
435 if (cachedTypes is null)
436 {
437 lock (_syncRoot)
438 {
439 _viewModelTypeCache[viewType] = match;
440 }
441 }
442
443 return match;
444 }
445 }
446
447 if (cachedTypes is null)
448 {
449 lock (_syncRoot)
450 {
451 _viewModelTypeCache[viewType] = null;
452 }
453 }
454
455 return null;
456 }
457
474 private static Type[] GetAllLoadableTypes()
475 {
476 lock (_syncRoot)
477 {
478 if (_allLoadableTypesCache is not null)
479 {
481 }
482 }
483
484 Type[] types = AppDomain.CurrentDomain
485 .GetAssemblies()
486 .Where(assembly => !assembly.IsDynamic)
487 .SelectMany(GetLoadableTypes)
488 .Where(type => type is { IsClass: true, IsAbstract: false })
489 .ToArray();
490
491 lock (_syncRoot)
492 {
493 _allLoadableTypesCache ??= types;
495 }
496 }
497
522 private static IEnumerable<Type> GetLoadableTypes(Assembly assembly)
523 {
524 try
525 {
526 return assembly.GetTypes();
527 }
528 catch (ReflectionTypeLoadException ex)
529 {
530 return ex.Types.Where(type => type != null)!;
531 }
532 catch
533 {
534 return Array.Empty<Type>();
535 }
536 }
537
562 private static Type? FindViewType(Type viewModelType)
563 {
564 lock (_syncRoot)
565 {
566 if (_viewTypeCache.TryGetValue(viewModelType, out Type? cachedViewType))
567 {
568 return cachedViewType;
569 }
570 }
571
572 Type[] allTypes = GetAllLoadableTypes();
573 string[] candidateNames = ViewNamingConvention.GetViewTypeNameCandidates(viewModelType);
574
575 foreach (string candidateName in candidateNames)
576 {
577 Type? match = allTypes.FirstOrDefault(t =>
578 t.FullName == candidateName ||
579 t.Name == candidateName);
580
581 if (match != null)
582 {
583 lock (_syncRoot)
584 {
585 _viewTypeCache[viewModelType] = match;
586 }
587
588 return match;
589 }
590 }
591
592 lock (_syncRoot)
593 {
594 _viewTypeCache[viewModelType] = null;
595 }
596
597 return null;
598 }
599
632 private static object? CreateInstance(Type type)
633 {
634 System.Diagnostics.Debug.WriteLine($"[ViewModelLocator] CreateInstance: {type.FullName}");
635
636 IViewModelResolver? resolver;
637 lock (_syncRoot)
638 {
639 resolver = _resolver;
640 }
641
642 if (resolver is null)
643 {
644 throw new InvalidOperationException(
645 $"Cannot resolve ViewModel [{type.FullName}] because no ViewModel resolver is configured. " +
646 "Call ViewModelLocator.RegisterResolver(...) or configure DreamineAppBuilder to use the default DMContainer resolver.");
647 }
648
649 var resolved = resolver.Resolve(type);
650 if (resolved is not null)
651 {
652 return resolved;
653 }
654
655 throw new InvalidOperationException(
656 $"Cannot resolve ViewModel [{type.FullName}]. " +
657 "The type was not resolved by the configured resolver. " +
658 "Register the ViewModel in the configured container or replace the ViewModelLocator resolver.");
659 }
660
669 private static void ClearTypeCache()
670 {
671 lock (_syncRoot)
672 {
673 _viewModelTypeCache.Clear();
674 _viewTypeCache.Clear();
676 }
677 }
678 }
679}
static readonly Dictionary< Type, Type > _map
static readonly Dictionary< Type, Type?> _viewTypeCache
static void Register(Type viewType, Type viewModelType)
static ? object Resolve(Type viewType)
static ? Type FindViewModelType(Type viewType, Type[]? cachedTypes=null)
static IEnumerable< Type > GetLoadableTypes(Assembly assembly)
static ? object ResolveView(Type viewModelType)
static readonly Dictionary< Type, Type?> _viewModelTypeCache
static ? object CreateInstance(Type type)
static ? Type FindViewType(Type viewModelType)
static void RegisterResolver(IViewModelResolver resolver)
static ? IViewModelResolver _resolver
static void RegisterAll(Assembly assembly)
static string[] GetViewTypeNameCandidates(Type viewModelType)
static string[] GetViewModelTypeNameCandidates(Type viewType)