private void ValidateUniqueProperties(ISagaEntity saga) { var uniqueProperties = UniqueAttribute.GetUniqueProperties(saga.GetType()); if (!uniqueProperties.Any()) { return; } var sagasFromSameType = from s in data where ((s.Value as ISagaEntity).GetType() == saga.GetType() && (s.Key != saga.Id)) select s.Value; foreach (var storedSaga in sagasFromSameType) { foreach (var uniqueProperty in uniqueProperties) { if (uniqueProperty.CanRead) { var inComingSagaPropertyValue = uniqueProperty.GetValue(saga, null); var storedSagaPropertyValue = uniqueProperty.GetValue(storedSaga, null); if (inComingSagaPropertyValue.Equals(storedSagaPropertyValue)) { throw new InvalidOperationException( string.Format("Cannot store a saga. The saga with id '{0}' already has property '{1}' with value '{2}'.", storedSaga.Id, uniqueProperty.ToString(), storedSagaPropertyValue)); } } } } }
public void Save(ISagaEntity saga) { Action saveSagaAction = () => { var stringId = saga.Id.ToString("N"); var sagaKey = GetSagaKey(saga); var sagaPropertyMapKey = GetSagaPropertyMapKey(saga); var sagaVersionKey = GetSagaVersionKey(saga); var uniqueProperties = UniqueAttribute.GetUniqueProperties(saga); using (var client = GetClient()) { using (var rlock = client.AcquireLock(stringId, TimeSpan.FromSeconds(30))) { long version = client.Increment(sagaVersionKey, 1); SetSagaVersion(saga, version); var sagaString = Serialize(saga); //Check that unique properties don't already exist for a different saga foreach (var prop in uniqueProperties) { var propertyKey = GetPropertyKey(saga.GetType(), prop.Key, prop.Value); var sagaId = client.Get <string>(propertyKey); if (sagaId != null) { if (saga.Id != Guid.Parse(sagaId)) { throw new UniquePropertyException("Unique property " + prop.Key + " already exists with value " + prop.Value); } } } using (var tran = client.CreateTransaction()) { tran.QueueCommand(c => c.Set(sagaKey, sagaString)); foreach (var prop in uniqueProperties) { var propertyKey = GetPropertyKey(saga.GetType(), prop.Key, prop.Value); tran.QueueCommand(c => c.Lists[sagaPropertyMapKey].Add(propertyKey)); //Link from saga ID to property key tran.QueueCommand(c => client.Set(propertyKey, stringId)); //Link from property key to saga } tran.Commit(); } } } }; if (Transaction.Current != null) { Transaction.Current.EnlistVolatile(new ActionResourceManager(saveSagaAction, null), EnlistmentOptions.None); } else { saveSagaAction(); } }
public UniqueProperty(ISagaEntity saga, string name, object value) { _type = saga.GetType(); Name = name; Value = value; SagaId = saga.Id; }
private string GetXmlForSaga(ISagaEntity saga) { var serializer = new XmlSerializer(saga.GetType()); var stringWriter = new StringWriter(); serializer.Serialize(stringWriter, saga); return(stringWriter.ToString()); }
/// <summary> /// Gets a single property that is marked with the UniqueAttribute for a saga entity /// </summary> /// <param name="entity">A saga entity</param> /// <returns>A PropertyInfo of the property marked with a UniqAttribute or null if not used</returns> public static KeyValuePair<string, object>? GetUniqueProperty(ISagaEntity entity) { var prop = GetUniqueProperty(entity.GetType()); return prop != null ? new KeyValuePair<string, object>(prop.Name, prop.GetValue(entity, null)) : (KeyValuePair<string, object>?) null; }
protected string GetSagaKey(ISagaEntity saga) { if (saga == null) { throw new ArgumentNullException("saga"); } return(KeyPrefix + "saga:" + saga.GetType().FullName + ":" + saga.Id.ToString("N")); }
/// <summary> /// Gets a single property that is marked with the UniqueAttribute for a saga entity /// </summary> /// <param name="entity">A saga entity</param> /// <returns>A PropertyInfo of the property marked with a UniqAttribute or null if not used</returns> public static KeyValuePair <string, object>?GetUniqueProperty(ISagaEntity entity) { var prop = GetUniqueProperty(entity.GetType()); return(prop != null ? new KeyValuePair <string, object>(prop.Name, prop.GetValue(entity, null)) : (KeyValuePair <string, object>?)null); }
private static UniqueProperty GetUniqueProperty(ISagaEntity saga) { return((from propertyInfo in saga.GetType().GetProperties() let customAttributes = propertyInfo.GetCustomAttributes(typeof(UniqueAttribute), false) where customAttributes.Length > 0 where propertyInfo.CanRead select new UniqueProperty(saga, propertyInfo.Name, propertyInfo.GetValue(saga, null)) ).FirstOrDefault()); }
protected void SetSagaVersion(ISagaEntity saga, long version) { var versionProp = saga.GetType().GetProperties().FirstOrDefault(o => o.Name == "Version"); if (versionProp == null) { throw new MissingMemberException("'Version' property must be defined"); } versionProp.SetValue(saga, version, null); }
protected long GetSagaVersion(ISagaEntity saga) { //TODO: Use an attribute instead of fixed property name? var versionProp = saga.GetType().GetProperties().FirstOrDefault(o => o.Name == "Version"); if (versionProp == null) { throw new MissingMemberException("'Version' property must be defined on saga data"); } return(Convert.ToInt64(versionProp.GetValue(saga, null))); }
void StoreUniqueProperty(ISagaEntity saga) { var uniqueProperty = UniqueAttribute.GetUniqueProperty(saga); if (!uniqueProperty.HasValue) { return; } var id = SagaUniqueIdentity.FormatId(saga.GetType(), uniqueProperty.Value); Session.Store(new SagaUniqueIdentity { Id = id, SagaId = saga.Id, UniqueValue = uniqueProperty.Value.Value }); SetUniqueValueMetadata(saga, uniqueProperty.Value); }
void StoreUniqueProperty(ISagaEntity saga) { var uniqueProperty = UniqueAttribute.GetUniqueProperty(saga); if (!uniqueProperty.HasValue) { return; } var id = SagaUniqueIdentity.FormatId(saga.GetType(), uniqueProperty.Value); var sagaDocId = sessionFactory.Store.Conventions.FindFullDocumentKeyFromNonStringIdentifier(saga.Id, saga.GetType(), false); Session.Store(new SagaUniqueIdentity { Id = id, SagaId = saga.Id, UniqueValue = uniqueProperty.Value.Value, SagaDocId = sagaDocId }); SetUniqueValueMetadata(saga, uniqueProperty.Value); }
/// <summary> /// Handles a message. /// </summary> /// <param name="message">The message to handle.</param> /// <remarks> /// If the message received needs to start a new saga, then a new /// saga instance will be created and will be saved using the <see cref="ISagaPersister"/> /// implementation provided in the configuration. Any other message implementing /// <see cref="ISagaMessage"/> will cause the existing saga instance with which it is /// associated to continue.</remarks> public void Handle(object message) { if (!NeedToHandle(message)) { return; } var entitiesHandled = new List <ISagaEntity>(); var sagaTypesHandled = new List <Type>(); foreach (IFinder finder in Configure.GetFindersFor(message)) { ISaga saga; bool sagaEntityIsPersistent = true; ISagaEntity sagaEntity = UseFinderToFindSaga(finder, message); if (sagaEntity == null) { Type sagaToCreate = Configure.GetSagaTypeToStartIfMessageNotFoundByFinder(message, finder); if (sagaToCreate == null) { continue; } if (sagaTypesHandled.Contains(sagaToCreate)) { continue; // don't create the same saga type twice for the same message } sagaTypesHandled.Add(sagaToCreate); Type sagaEntityType = Configure.GetSagaEntityTypeForSagaType(sagaToCreate); sagaEntity = Activator.CreateInstance(sagaEntityType) as ISagaEntity; if (sagaEntity != null) { if (message is ISagaMessage) { sagaEntity.Id = (message as ISagaMessage).SagaId; } else { sagaEntity.Id = GenerateSagaId(); } sagaEntity.Originator = Bus.CurrentMessageContext.ReplyToAddress.ToString(); sagaEntity.OriginalMessageId = Bus.CurrentMessageContext.Id; sagaEntityIsPersistent = false; } saga = Builder.Build(sagaToCreate) as ISaga; } else { if (entitiesHandled.Contains(sagaEntity)) { continue; // don't call the same saga twice } saga = Builder.Build(Configure.GetSagaTypeForSagaEntityType(sagaEntity.GetType())) as ISaga; } if (saga != null) { saga.Entity = sagaEntity; HaveSagaHandleMessage(saga, message, sagaEntityIsPersistent); sagaTypesHandled.Add(saga.GetType()); } entitiesHandled.Add(sagaEntity); } if (entitiesHandled.Count == 0) { logger.InfoFormat("Could not find a saga for the message type {0} with id {1}. Going to invoke SagaNotFoundHandlers.", message.GetType().FullName, Bus.CurrentMessageContext.Id); foreach (var handler in NServiceBus.Configure.Instance.Builder.BuildAll <IHandleSagaNotFound>()) { logger.DebugFormat("Invoking SagaNotFoundHandler: {0}", handler.GetType().FullName); handler.Handle(message); } } }
public void Complete(ISagaEntity saga) { using (var session = OpenSession()) { session.Advanced.DatabaseCommands.Delete(Store.Conventions.FindTypeTagName(saga.GetType()) + "/" + saga.Id, null); session.SaveChanges(); } }
/// <summary> /// Get Dispatcher /// </summary> /// <param name="messageHandlerType">Type of the message Handler</param> /// <param name="builder">Builder</param> /// <param name="message">Message</param> /// <returns>Saga Dispatcher</returns> public IEnumerable <Action> GetDispatcher(Type messageHandlerType, IBuilder builder, object message) { if (message.IsTimeoutMessage() && !message.TimeoutHasExpired()) { yield return(() => Bus.HandleCurrentMessageLater()); yield break; } var entitiesHandled = new List <ISagaEntity>(); var sagaTypesHandled = new List <Type>(); foreach (var finder in GetFindersFor(message, builder)) { bool sagaEntityIsPersistent = true; ISagaEntity sagaEntity = UseFinderToFindSaga(finder, message); Type sagaType; if (sagaEntity == null) { sagaType = Configure.GetSagaTypeToStartIfMessageNotFoundByFinder(message, finder); if (sagaType == null) { continue; } if (sagaTypesHandled.Contains(sagaType)) { continue; // don't create the same saga type twice for the same message } sagaEntity = CreateNewSagaEntity(sagaType); sagaEntityIsPersistent = false; } else { if (entitiesHandled.Contains(sagaEntity)) { continue; // don't call the same saga twice } sagaType = Configure.GetSagaTypeForSagaEntityType(sagaEntity.GetType()); } if (messageHandlerType.IsAssignableFrom(sagaType)) { yield return () => { var saga = (ISaga)builder.Build(sagaType); saga.Entity = sagaEntity; try { SagaContext.Current = saga; HandlerInvocationCache.Invoke(saga, message); if (!saga.Completed) { if (!sagaEntityIsPersistent) { Persister.Save(saga.Entity); } else { Persister.Update(saga.Entity); } } else { if (sagaEntityIsPersistent) { Persister.Complete(saga.Entity); } NotifyTimeoutManagerThatSagaHasCompleted(saga); } LogIfSagaIsFinished(saga); } finally { SagaContext.Current = null; } } } ; sagaTypesHandled.Add(sagaType); entitiesHandled.Add(sagaEntity); } if (entitiesHandled.Count == 0) { yield return () => { logger.InfoFormat("Could not find a saga for the message type {0} with id {1}. Going to invoke SagaNotFoundHandlers.", message.GetType().FullName, Bus.CurrentMessageContext.Id); foreach (var handler in builder.BuildAll <IHandleSagaNotFound>()) { logger.DebugFormat("Invoking SagaNotFoundHandler: {0}", handler.GetType().FullName); handler.Handle(message); } } } ; } ISagaEntity CreateNewSagaEntity(Type sagaType) { var sagaEntityType = Configure.GetSagaEntityTypeForSagaType(sagaType); if (sagaEntityType == null) { throw new InvalidOperationException("No saga entity type could be found for saga: " + sagaType); } var sagaEntity = (ISagaEntity)Activator.CreateInstance(sagaEntityType); sagaEntity.Id = GuidCombGenerator.Generate(); sagaEntity.Originator = Bus.CurrentMessageContext.ReplyToAddress.ToString(); sagaEntity.OriginalMessageId = Bus.CurrentMessageContext.Id; return(sagaEntity); }
void DeleteUniqueProperty(ISagaEntity saga, KeyValuePair <string, object> uniqueProperty) { var id = SagaUniqueIdentity.FormatId(saga.GetType(), uniqueProperty); Session.Advanced.DatabaseCommands.Delete(id, null); }
private void UpdateUniquePropertyForSaga(ISagaEntity saga, SagaData sagaData) { var uniqueProperty = UniqueAttribute.GetUniqueProperty(saga); sagaData.UniqueProperty = uniqueProperty != null?GetUniqueProperty(saga.GetType(), uniqueProperty.Value) : Guid.NewGuid().ToString(); }
/// <summary> /// Gets all the properties that are marked with the UniqueAttribute for a saga entity /// </summary> /// <param name="entity">A saga entity</param> /// <returns>A dictionary of property names and their values</returns> public static IDictionary<string, object> GetUniqueProperties(ISagaEntity entity) { return GetUniqueProperties(entity.GetType()).ToDictionary(p => p.Name, p => p.GetValue(entity, null)); }
/// <summary> /// Gets all the properties that are marked with the UniqueAttribute for a saga entity /// </summary> /// <param name="entity">A saga entity</param> /// <returns>A dictionary of property names and their values</returns> public static IDictionary <string, object> GetUniqueProperties(ISagaEntity entity) { return(GetUniqueProperties(entity.GetType()).ToDictionary(p => p.Name, p => p.GetValue(entity, null))); }