/src/NServiceBus.Core/Conventions/Conventions.cs

https://github.com/MikeEast/NServiceBus · C# · 272 lines · 216 code · 31 blank · 25 comment · 19 complexity · 3596fea32a2f4729609db73fb9222f39 MD5 · raw file

  1. namespace NServiceBus
  2. {
  3. using Logging;
  4. using System;
  5. using System.Collections.Concurrent;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using System.Reflection;
  9. /// <summary>
  10. /// Message convention definitions.
  11. /// </summary>
  12. public class Conventions
  13. {
  14. /// <summary>
  15. /// Initializes message conventions with the default NServiceBus conventions.
  16. /// </summary>
  17. public Conventions()
  18. {
  19. defaultMessageConvention = new OverridableMessageConvention(new NServiceBusMarkerInterfaceConvention());
  20. conventions.Add(defaultMessageConvention);
  21. }
  22. /// <summary>
  23. /// Returns true if the given type is a message type.
  24. /// </summary>
  25. public bool IsMessageType(Type t)
  26. {
  27. Guard.AgainstNull(nameof(t), t);
  28. try
  29. {
  30. return MessagesConventionCache.ApplyConvention(t,
  31. typeHandle =>
  32. {
  33. var type = Type.GetTypeFromHandle(typeHandle);
  34. if (IsInSystemConventionList(type))
  35. {
  36. return true;
  37. }
  38. if (type.IsFromParticularAssembly())
  39. {
  40. return false;
  41. }
  42. foreach (var convention in conventions)
  43. {
  44. if (convention.IsMessageType(type))
  45. {
  46. if (logger.IsDebugEnabled)
  47. {
  48. logger.Debug($"{type.FullName} identified as message type by {convention.Name} convention.");
  49. }
  50. return true;
  51. }
  52. if (convention.IsCommandType(type))
  53. {
  54. if (logger.IsDebugEnabled)
  55. {
  56. logger.Debug($"{type.FullName} identified as command type by {convention.Name} but does not match message type convention. Treating as a message.");
  57. }
  58. return true;
  59. }
  60. if (convention.IsEventType(type))
  61. {
  62. if (logger.IsDebugEnabled)
  63. {
  64. logger.Debug($"{type.FullName} identified as event type by {convention.Name} but does not match message type convention. Treating as a message.");
  65. }
  66. return true;
  67. }
  68. }
  69. return false;
  70. });
  71. }
  72. catch (Exception ex)
  73. {
  74. throw new Exception("Failed to evaluate Message convention. See inner exception for details.", ex);
  75. }
  76. }
  77. /// <summary>
  78. /// Returns true is message is a system message type.
  79. /// </summary>
  80. public bool IsInSystemConventionList(Type t)
  81. {
  82. Guard.AgainstNull(nameof(t), t);
  83. return IsSystemMessageActions.Any(isSystemMessageAction => isSystemMessageAction(t));
  84. }
  85. /// <summary>
  86. /// Add system message convention.
  87. /// </summary>
  88. /// <param name="definesMessageType">Function to define system message convention.</param>
  89. public void AddSystemMessagesConventions(Func<Type, bool> definesMessageType)
  90. {
  91. Guard.AgainstNull(nameof(definesMessageType), definesMessageType);
  92. if (!IsSystemMessageActions.Contains(definesMessageType))
  93. {
  94. IsSystemMessageActions.Add(definesMessageType);
  95. MessagesConventionCache.Reset();
  96. }
  97. }
  98. /// <summary>
  99. /// Returns true if the given type is a command type.
  100. /// </summary>
  101. public bool IsCommandType(Type t)
  102. {
  103. Guard.AgainstNull(nameof(t), t);
  104. try
  105. {
  106. return CommandsConventionCache.ApplyConvention(t, typeHandle =>
  107. {
  108. var type = Type.GetTypeFromHandle(typeHandle);
  109. if (type.IsFromParticularAssembly())
  110. {
  111. return false;
  112. }
  113. foreach (var convention in conventions)
  114. {
  115. if (convention.IsCommandType(type))
  116. {
  117. if (logger.IsDebugEnabled)
  118. {
  119. logger.Debug($"{type.FullName} identified as command type by {convention.Name} convention.");
  120. }
  121. return true;
  122. }
  123. }
  124. return false;
  125. });
  126. }
  127. catch (Exception ex)
  128. {
  129. throw new Exception("Failed to evaluate Command convention. See inner exception for details.", ex);
  130. }
  131. }
  132. /// <summary>
  133. /// Returns true if the given property should be send via the DataBus.
  134. /// </summary>
  135. public bool IsDataBusProperty(PropertyInfo property)
  136. {
  137. Guard.AgainstNull(nameof(property), property);
  138. try
  139. {
  140. return IsDataBusPropertyAction(property);
  141. }
  142. catch (Exception ex)
  143. {
  144. throw new Exception("Failed to evaluate DataBus Property convention. See inner exception for details.", ex);
  145. }
  146. }
  147. /// <summary>
  148. /// Returns true if the given type is a event type.
  149. /// </summary>
  150. public bool IsEventType(Type t)
  151. {
  152. Guard.AgainstNull(nameof(t), t);
  153. try
  154. {
  155. return EventsConventionCache.ApplyConvention(t, typeHandle =>
  156. {
  157. var type = Type.GetTypeFromHandle(typeHandle);
  158. if (type.IsFromParticularAssembly())
  159. {
  160. return false;
  161. }
  162. foreach (var convention in conventions)
  163. {
  164. if (convention.IsEventType(type))
  165. {
  166. if (logger.IsDebugEnabled)
  167. {
  168. logger.Debug($"{type.FullName} identified as event type by {convention.Name} convention.");
  169. }
  170. return true;
  171. }
  172. }
  173. return false;
  174. });
  175. }
  176. catch (Exception ex)
  177. {
  178. throw new Exception("Failed to evaluate Event convention. See inner exception for details.", ex);
  179. }
  180. }
  181. internal bool CustomMessageTypeConventionUsed => defaultMessageConvention.ConventionModified || conventions.Count > 1;
  182. internal string[] RegisteredConventions => conventions.Select(x => x.Name).ToArray();
  183. internal List<DataBusPropertyInfo> GetDataBusProperties(object message)
  184. {
  185. return cache.GetOrAdd(message.GetType(), messageType =>
  186. {
  187. var properties = new List<DataBusPropertyInfo>();
  188. foreach (var propertyInfo in messageType.GetProperties())
  189. {
  190. if (IsDataBusProperty(propertyInfo))
  191. {
  192. properties.Add(new DataBusPropertyInfo
  193. {
  194. Name = propertyInfo.Name,
  195. Getter = DelegateFactory.CreateGet(propertyInfo),
  196. Setter = DelegateFactory.CreateSet(propertyInfo)
  197. });
  198. }
  199. }
  200. return properties;
  201. });
  202. }
  203. internal void DefineMessageTypeConvention(Func<Type, bool> definesMessageType)
  204. {
  205. defaultMessageConvention.DefiningMessagesAs(definesMessageType);
  206. }
  207. internal void DefineCommandTypeConventions(Func<Type, bool> definesCommandType)
  208. {
  209. defaultMessageConvention.DefiningCommandsAs(definesCommandType);
  210. }
  211. internal void DefineEventTypeConventions(Func<Type, bool> definesEventType)
  212. {
  213. defaultMessageConvention.DefiningEventsAs(definesEventType);
  214. }
  215. internal void Add(IMessageConvention messageConvention)
  216. {
  217. conventions.Add(messageConvention);
  218. }
  219. internal Func<PropertyInfo, bool> IsDataBusPropertyAction = p => typeof(IDataBusProperty).IsAssignableFrom(p.PropertyType) && typeof(IDataBusProperty) != p.PropertyType;
  220. ConcurrentDictionary<Type, List<DataBusPropertyInfo>> cache = new ConcurrentDictionary<Type, List<DataBusPropertyInfo>>();
  221. ConventionCache CommandsConventionCache = new ConventionCache();
  222. ConventionCache EventsConventionCache = new ConventionCache();
  223. List<Func<Type, bool>> IsSystemMessageActions = new List<Func<Type, bool>>();
  224. ConventionCache MessagesConventionCache = new ConventionCache();
  225. IList<IMessageConvention> conventions = new List<IMessageConvention>();
  226. OverridableMessageConvention defaultMessageConvention;
  227. static ILog logger = LogManager.GetLogger<Conventions>();
  228. class ConventionCache
  229. {
  230. public bool ApplyConvention(Type type, Func<RuntimeTypeHandle, bool> action)
  231. {
  232. return cache.GetOrAdd(type.TypeHandle, action);
  233. }
  234. public void Reset()
  235. {
  236. cache.Clear();
  237. }
  238. ConcurrentDictionary<RuntimeTypeHandle, bool> cache = new ConcurrentDictionary<RuntimeTypeHandle, bool>();
  239. }
  240. }
  241. }