Пример #1
0
        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));
                        }
                    }
                }
            }
        }
Пример #2
0
        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();
            }
        }
Пример #3
0
 public UniqueProperty(ISagaEntity saga, string name, object value)
 {
     _type  = saga.GetType();
     Name   = name;
     Value  = value;
     SagaId = saga.Id;
 }
Пример #4
0
 public UniqueProperty(ISagaEntity saga, string name, object value)
 {
     _type = saga.GetType();
     Name = name;
     Value = value;
     SagaId = saga.Id;
 }
Пример #5
0
        private string GetXmlForSaga(ISagaEntity saga)
        {
            var serializer   = new XmlSerializer(saga.GetType());
            var stringWriter = new StringWriter();

            serializer.Serialize(stringWriter, saga);
            return(stringWriter.ToString());
        }
Пример #6
0
        /// <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;
        }
Пример #7
0
 protected string GetSagaKey(ISagaEntity saga)
 {
     if (saga == null)
     {
         throw new ArgumentNullException("saga");
     }
     return(KeyPrefix + "saga:" + saga.GetType().FullName + ":" + saga.Id.ToString("N"));
 }
Пример #8
0
        /// <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());
 }
Пример #10
0
        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);
        }
Пример #11
0
        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)));
        }
Пример #12
0
        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);
        }
Пример #13
0
        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);
        }
Пример #14
0
        /// <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);
                }
            }
        }
Пример #15
0
 public void Complete(ISagaEntity saga)
 {
     using (var session = OpenSession())
     {
         session.Advanced.DatabaseCommands.Delete(Store.Conventions.FindTypeTagName(saga.GetType()) + "/" + saga.Id, null);
         session.SaveChanges();
     }
 }
Пример #16
0
        /// <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);
        }
Пример #17
0
        void DeleteUniqueProperty(ISagaEntity saga, KeyValuePair <string, object> uniqueProperty)
        {
            var id = SagaUniqueIdentity.FormatId(saga.GetType(), uniqueProperty);

            Session.Advanced.DatabaseCommands.Delete(id, null);
        }
Пример #18
0
        private void UpdateUniquePropertyForSaga(ISagaEntity saga, SagaData sagaData)
        {
            var uniqueProperty = UniqueAttribute.GetUniqueProperty(saga);

            sagaData.UniqueProperty = uniqueProperty != null?GetUniqueProperty(saga.GetType(), uniqueProperty.Value) : Guid.NewGuid().ToString();
        }
Пример #19
0
 /// <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));
 }
Пример #20
0
 /// <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)));
 }