PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/FluentNHibernate/Automapping/AutoPersistenceModel.cs

http://github.com/jagregory/fluent-nhibernate
C# | 511 lines | 341 code | 80 blank | 90 comment | 45 complexity | e2cfde00d7103a197c412311cb93d020 MD5 | raw file
Possible License(s): BSD-3-Clause, CC-BY-SA-3.0, Apache-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using FluentNHibernate.Automapping.Alterations;
  6. using FluentNHibernate.Cfg;
  7. using FluentNHibernate.Mapping;
  8. using FluentNHibernate.Mapping.Providers;
  9. using FluentNHibernate.MappingModel;
  10. using FluentNHibernate.MappingModel.ClassBased;
  11. using FluentNHibernate.Utils;
  12. using FluentNHibernate.Utils.Reflection;
  13. using FluentNHibernate.Visitors;
  14. namespace FluentNHibernate.Automapping
  15. {
  16. public class AutoPersistenceModel : PersistenceModel
  17. {
  18. readonly IAutomappingConfiguration cfg;
  19. readonly AutoMappingExpressions expressions;
  20. readonly AutoMapper autoMapper;
  21. readonly List<ITypeSource> sources = new List<ITypeSource>();
  22. Func<Type, bool> whereClause;
  23. readonly List<AutoMapType> mappingTypes = new List<AutoMapType>();
  24. bool autoMappingsCreated;
  25. readonly AutoMappingAlterationCollection alterations = new AutoMappingAlterationCollection();
  26. readonly List<InlineOverride> inlineOverrides = new List<InlineOverride>();
  27. readonly List<Type> ignoredTypes = new List<Type>();
  28. readonly List<Type> includedTypes = new List<Type>();
  29. public AutoPersistenceModel()
  30. {
  31. expressions = new AutoMappingExpressions();
  32. cfg = new ExpressionBasedAutomappingConfiguration(expressions);
  33. autoMapper = new AutoMapper(cfg, Conventions, inlineOverrides);
  34. componentResolvers.Add(new AutomappedComponentResolver(autoMapper, cfg));
  35. }
  36. public AutoPersistenceModel(IAutomappingConfiguration cfg)
  37. {
  38. this.cfg = cfg;
  39. autoMapper = new AutoMapper(cfg, Conventions, inlineOverrides);
  40. componentResolvers.Add(new AutomappedComponentResolver(autoMapper, cfg));
  41. }
  42. public AutoPersistenceModel AddMappingsFromAssemblyOf<T>()
  43. {
  44. return AddMappingsFromAssembly(typeof(T).Assembly);
  45. }
  46. public new AutoPersistenceModel AddMappingsFromAssembly(Assembly assembly)
  47. {
  48. AddMappingsFromSource(new AssemblyTypeSource(assembly));
  49. return this;
  50. }
  51. public new AutoPersistenceModel AddMappingsFromSource(ITypeSource source)
  52. {
  53. base.AddMappingsFromSource(source);
  54. return this;
  55. }
  56. /// <summary>
  57. /// Specify alterations to be used with this AutoPersisteceModel
  58. /// </summary>
  59. /// <param name="alterationDelegate">Lambda to declare alterations</param>
  60. /// <returns>AutoPersistenceModel</returns>
  61. public AutoPersistenceModel Alterations(Action<AutoMappingAlterationCollection> alterationDelegate)
  62. {
  63. alterationDelegate(alterations);
  64. return this;
  65. }
  66. /// <summary>
  67. /// Use auto mapping overrides defined in the assembly of T.
  68. /// </summary>
  69. /// <typeparam name="T">Type to get assembly from</typeparam>
  70. /// <returns>AutoPersistenceModel</returns>
  71. public AutoPersistenceModel UseOverridesFromAssemblyOf<T>()
  72. {
  73. return UseOverridesFromAssembly(typeof(T).Assembly);
  74. }
  75. /// <summary>
  76. /// Use auto mapping overrides defined in the assembly of T.
  77. /// </summary>
  78. /// <param name="assembly">Assembly to scan</param>
  79. /// <returns>AutoPersistenceModel</returns>
  80. public AutoPersistenceModel UseOverridesFromAssembly(Assembly assembly)
  81. {
  82. alterations.Add(new AutoMappingOverrideAlteration(assembly));
  83. return this;
  84. }
  85. /// <summary>
  86. /// Alter convention discovery
  87. /// </summary>
  88. public new SetupConventionFinder<AutoPersistenceModel> Conventions
  89. {
  90. get { return new SetupConventionFinder<AutoPersistenceModel>(this, base.Conventions); }
  91. }
  92. /// <summary>
  93. /// Alter some of the configuration options that control how the automapper works.
  94. /// Depreciated in favour of supplying your own IAutomappingConfiguration instance to AutoMap: <see cref="AutoMap.AssemblyOf{T}(FluentNHibernate.Automapping.IAutomappingConfiguration)"/>.
  95. /// Cannot be used in combination with a user-defined configuration.
  96. /// </summary>
  97. [Obsolete("Depreciated in favour of supplying your own IAutomappingConfiguration instance to AutoMap: AutoMap.AssemblyOf<T>(your_configuration_instance)")]
  98. public AutoPersistenceModel Setup(Action<AutoMappingExpressions> expressionsAction)
  99. {
  100. if (HasUserDefinedConfiguration)
  101. throw new InvalidOperationException("Cannot use Setup method when using a user-defined IAutomappingConfiguration instance.");
  102. expressionsAction(expressions);
  103. return this;
  104. }
  105. /// <summary>
  106. /// Supply a criteria for which types will be mapped.
  107. /// Cannot be used in combination with a user-defined configuration.
  108. /// </summary>
  109. /// <param name="where">Where clause</param>
  110. public AutoPersistenceModel Where(Func<Type, bool> where)
  111. {
  112. if (HasUserDefinedConfiguration)
  113. throw new InvalidOperationException("Cannot use Where method when using a user-defined IAutomappingConfiguration instance.");
  114. whereClause = where;
  115. return this;
  116. }
  117. public override IEnumerable<HibernateMapping> BuildMappings()
  118. {
  119. CompileMappings();
  120. return base.BuildMappings();
  121. }
  122. private void CompileMappings()
  123. {
  124. if (autoMappingsCreated)
  125. return;
  126. alterations.Apply(this);
  127. var types = sources
  128. .SelectMany(x => x.GetTypes())
  129. .OrderBy(x => InheritanceHierarchyDepth(x));
  130. foreach (var type in types)
  131. {
  132. // skipped by user-defined configuration criteria
  133. if (!cfg.ShouldMap(type))
  134. {
  135. log.AutomappingSkippedType(type, "Skipped by result of IAutomappingConfiguration.ShouldMap(Type)");
  136. continue;
  137. }
  138. // skipped by inline where clause
  139. if (whereClause != null && !whereClause(type))
  140. {
  141. log.AutomappingSkippedType(type, "Skipped by Where clause");
  142. continue;
  143. }
  144. // skipped because either already mapped elsewhere, or not valid for mapping
  145. if (!ShouldMap(type))
  146. continue;
  147. mappingTypes.Add(new AutoMapType(type));
  148. }
  149. log.AutomappingCandidateTypes(mappingTypes.Select(x => x.Type));
  150. foreach (var type in mappingTypes)
  151. {
  152. if (type.IsMapped) continue;
  153. AddMapping(type.Type);
  154. }
  155. autoMappingsCreated = true;
  156. }
  157. private int InheritanceHierarchyDepth(Type type)
  158. {
  159. var depth = 0;
  160. var parent = type;
  161. while (parent != null && parent != typeof(object))
  162. {
  163. parent = parent.BaseType;
  164. depth++;
  165. }
  166. return depth;
  167. }
  168. public override void Configure(NHibernate.Cfg.Configuration configuration)
  169. {
  170. CompileMappings();
  171. base.Configure(configuration);
  172. }
  173. private void AddMapping(Type type)
  174. {
  175. Type typeToMap = GetTypeToMap(type);
  176. if (typeToMap != type)
  177. {
  178. log.BeginAutomappingType(type);
  179. var derivedMapping = autoMapper.Map(type, mappingTypes);
  180. Add(new PassThroughMappingProvider(derivedMapping));
  181. }
  182. log.BeginAutomappingType(typeToMap);
  183. var mapping = autoMapper.Map(typeToMap, mappingTypes);
  184. Add(new PassThroughMappingProvider(mapping));
  185. }
  186. private Type GetTypeToMap(Type type)
  187. {
  188. while (ShouldMapParent(type))
  189. {
  190. type = type.BaseType;
  191. }
  192. return type;
  193. }
  194. private bool ShouldMapParent(Type type)
  195. {
  196. return ShouldMap(type.BaseType) && !cfg.IsConcreteBaseType(type.BaseType);
  197. }
  198. private bool ShouldMap(Type type)
  199. {
  200. if (includedTypes.Contains(type))
  201. return true; // inclusions take precedence over everything
  202. if (ignoredTypes.Contains(type))
  203. {
  204. log.AutomappingSkippedType(type, "Skipped by IgnoreBase");
  205. return false; // excluded
  206. }
  207. if (type.IsGenericType && ignoredTypes.Contains(type.GetGenericTypeDefinition()))
  208. {
  209. log.AutomappingSkippedType(type, "Skipped by IgnoreBase");
  210. return false; // generic definition is excluded
  211. }
  212. if (type.IsAbstract && cfg.AbstractClassIsLayerSupertype(type))
  213. {
  214. log.AutomappingSkippedType(type, "Skipped by IAutomappingConfiguration.AbstractClassIsLayerSupertype(Type)");
  215. return false; // is abstract and a layer supertype
  216. }
  217. if (cfg.IsComponent(type))
  218. {
  219. log.AutomappingSkippedType(type, "Skipped by IAutomappingConfiguration.IsComponent(Type)");
  220. return false; // skipped because we don't want to map components as entities
  221. }
  222. if (type == typeof(object))
  223. return false;
  224. return true;
  225. }
  226. public IMappingProvider FindMapping<T>()
  227. {
  228. return FindMapping(typeof(T));
  229. }
  230. public IMappingProvider FindMapping(Type type)
  231. {
  232. Func<IMappingProvider, Type, bool> finder = (provider, expectedType) =>
  233. {
  234. var mappingType = provider.GetType();
  235. if (mappingType.IsGenericType)
  236. {
  237. // instance of a generic type (probably AutoMapping<T>)
  238. return mappingType.GetGenericArguments()[0] == expectedType;
  239. }
  240. if (mappingType.BaseType.IsGenericType &&
  241. mappingType.BaseType.GetGenericTypeDefinition() == typeof(ClassMap<>))
  242. {
  243. // base type is a generic type of ClassMap<T>, so we've got a XXXMap instance
  244. return mappingType.BaseType.GetGenericArguments()[0] == expectedType;
  245. }
  246. if (provider is PassThroughMappingProvider)
  247. return provider.GetClassMapping().Type == expectedType;
  248. return false;
  249. };
  250. var mapping = classProviders.FirstOrDefault(t => finder(t, type));
  251. if (mapping != null) return mapping;
  252. // if we haven't found a map yet then try to find a map of the
  253. // base type to merge if not a concrete base type
  254. if (type.BaseType != typeof(object) && !cfg.IsConcreteBaseType(type.BaseType))
  255. {
  256. return FindMapping(type.BaseType);
  257. }
  258. return null;
  259. }
  260. /// <summary>
  261. /// Adds all entities from a specific assembly.
  262. /// </summary>
  263. /// <param name="assembly">Assembly to load from</param>
  264. public AutoPersistenceModel AddEntityAssembly(Assembly assembly)
  265. {
  266. return AddTypeSource(new AssemblyTypeSource(assembly));
  267. }
  268. /// <summary>
  269. /// Adds all entities from the <see cref="ITypeSource"/>.
  270. /// </summary>
  271. /// <param name="source"><see cref="ITypeSource"/> to load from</param>
  272. public AutoPersistenceModel AddTypeSource(ITypeSource source)
  273. {
  274. sources.Add(source);
  275. return this;
  276. }
  277. public AutoPersistenceModel AddFilter<TFilter>() where TFilter : IFilterDefinition
  278. {
  279. Add(typeof(TFilter));
  280. return this;
  281. }
  282. internal void AddOverride(Type type, Action<object> action)
  283. {
  284. inlineOverrides.Add(new InlineOverride(type, action));
  285. }
  286. /// <summary>
  287. /// Override the mapping of a specific entity.
  288. /// </summary>
  289. /// <remarks>This may affect subclasses, depending on the alterations you do.</remarks>
  290. /// <typeparam name="T">Entity who's mapping to override</typeparam>
  291. /// <param name="populateMap">Lambda performing alterations</param>
  292. public AutoPersistenceModel Override<T>(Action<AutoMapping<T>> populateMap)
  293. {
  294. inlineOverrides.Add(new InlineOverride(typeof(T), x =>
  295. {
  296. if (x is AutoMapping<T>)
  297. populateMap((AutoMapping<T>)x);
  298. }));
  299. return this;
  300. }
  301. static bool IsAutomappingForType(object o, Type entityType)
  302. {
  303. var autoMappingType = ReflectionHelper.AutomappingTypeForEntityType(entityType);
  304. return o.GetType().IsAssignableFrom(autoMappingType);
  305. }
  306. /// <summary>
  307. /// Adds an IAutoMappingOverride reflectively
  308. /// </summary>
  309. /// <param name="overrideType">Override type, expected to be an IAutoMappingOverride</param>
  310. public void Override(Type overrideType)
  311. {
  312. var overrideInterfaces = overrideType.GetInterfaces().Where(x => x.IsAutoMappingOverrideType()).ToList();
  313. foreach (var overrideInterface in overrideInterfaces)
  314. {
  315. var entityType = overrideInterface.GetGenericArguments().First();
  316. AddOverride(entityType, instance =>
  317. {
  318. if (!IsAutomappingForType(instance, entityType)) return;
  319. var overrideInstance = Activator.CreateInstance(overrideType);
  320. MethodInfo overrideHelperMethod = typeof(AutoPersistenceModel)
  321. .GetMethod("OverrideHelper", BindingFlags.NonPublic | BindingFlags.Instance);
  322. if (overrideHelperMethod == null) return;
  323. overrideHelperMethod
  324. .MakeGenericMethod(entityType)
  325. .Invoke(this, new[] { instance, overrideInstance });
  326. });
  327. }
  328. }
  329. //called reflectively from method above
  330. private void OverrideHelper<T>(AutoMapping<T> x, IAutoMappingOverride<T> mappingOverride)
  331. {
  332. mappingOverride.Override(x);
  333. }
  334. /// <summary>
  335. /// Override all mappings.
  336. /// </summary>
  337. /// <remarks>Currently only supports ignoring properties on all entities.</remarks>
  338. /// <param name="alteration">Lambda performing alterations</param>
  339. public AutoPersistenceModel OverrideAll(Action<IPropertyIgnorer> alteration)
  340. {
  341. inlineOverrides.Add(new InlineOverride(typeof(object), x =>
  342. {
  343. if (x is IPropertyIgnorer)
  344. alteration((IPropertyIgnorer)x);
  345. }));
  346. return this;
  347. }
  348. /// <summary>
  349. /// Ignore a base type. This removes it from any mapped inheritance hierarchies, good for non-abstract layer
  350. /// supertypes.
  351. /// </summary>
  352. /// <typeparam name="T">Type to ignore</typeparam>
  353. public AutoPersistenceModel IgnoreBase<T>()
  354. {
  355. return IgnoreBase(typeof(T));
  356. }
  357. /// <summary>
  358. /// Ignore a base type. This removes it from any mapped inheritance hierarchies, good for non-abstract layer
  359. /// supertypes.
  360. /// </summary>
  361. /// <param name="baseType">Type to ignore</param>
  362. public AutoPersistenceModel IgnoreBase(Type baseType)
  363. {
  364. ignoredTypes.Add(baseType);
  365. return this;
  366. }
  367. /// <summary>
  368. /// Explicitly includes a type to be used as part of a mapped inheritance hierarchy.
  369. /// </summary>
  370. /// <remarks>
  371. /// Abstract classes are probably what you'll be using this method with. Fluent NHibernate considers abstract
  372. /// classes to be layer supertypes, so doesn't automatically map them as part of an inheritance hierarchy. You
  373. /// can use this method to override that behavior for a specific type; otherwise you should consider using the
  374. /// <see cref="IAutomappingConfiguration.AbstractClassIsLayerSupertype"/> setting.
  375. /// </remarks>
  376. /// <typeparam name="T">Type to include</typeparam>
  377. public AutoPersistenceModel IncludeBase<T>()
  378. {
  379. return IncludeBase(typeof(T));
  380. }
  381. /// <summary>
  382. /// Explicitly includes a type to be used as part of a mapped inheritance hierarchy.
  383. /// </summary>
  384. /// <remarks>
  385. /// Abstract classes are probably what you'll be using this method with. Fluent NHibernate considers abstract
  386. /// classes to be layer supertypes, so doesn't automatically map them as part of an inheritance hierarchy. You
  387. /// can use this method to override that behavior for a specific type; otherwise you should consider using the
  388. /// <see cref="AutoMappingExpressions.AbstractClassIsLayerSupertype"/> setting.
  389. /// </remarks>
  390. /// <param name="baseType">Type to include</param>
  391. public AutoPersistenceModel IncludeBase(Type baseType)
  392. {
  393. includedTypes.Add(baseType);
  394. return this;
  395. }
  396. protected override string GetMappingFileName()
  397. {
  398. return "AutoMappings.hbm.xml";
  399. }
  400. bool HasUserDefinedConfiguration
  401. {
  402. get { return !(cfg is ExpressionBasedAutomappingConfiguration); }
  403. }
  404. }
  405. public class AutomappedComponentResolver : IComponentReferenceResolver
  406. {
  407. readonly AutoMapper mapper;
  408. IAutomappingConfiguration cfg;
  409. public AutomappedComponentResolver(AutoMapper mapper, IAutomappingConfiguration cfg)
  410. {
  411. this.mapper = mapper;
  412. this.cfg = cfg;
  413. }
  414. public ExternalComponentMapping Resolve(ComponentResolutionContext context, IEnumerable<IExternalComponentMappingProvider> componentProviders)
  415. {
  416. // this will only be called if there was no ComponentMap found
  417. var mapping = new ExternalComponentMapping(ComponentType.Component)
  418. {
  419. Member = context.ComponentMember,
  420. ContainingEntityType = context.EntityType,
  421. };
  422. mapping.Set(x => x.Name, Layer.Defaults, context.ComponentMember.Name);
  423. mapping.Set(x => x.Type, Layer.Defaults, context.ComponentType);
  424. if (context.ComponentMember.IsProperty && !context.ComponentMember.CanWrite)
  425. mapping.Set(x => x.Access, Layer.Defaults, cfg.GetAccessStrategyForReadOnlyProperty(context.ComponentMember).ToString());
  426. mapper.FlagAsMapped(context.ComponentType);
  427. mapper.MergeMap(context.ComponentType, mapping, new List<Member>());
  428. return mapping;
  429. }
  430. }
  431. }