Giter VIP home page Giter VIP logo

nes's People

Contributors

arwdab avatar elliotritchie avatar marinkobabic avatar mattjohnsonpint avatar sarmaad avatar valem avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nes's Issues

Null Exception in EventConverterFactory ctor

I have come across this problem when I tried to Bus.Reply(Enum) to a command from within a handler.

The static constructor assumes that Global.TypesToScan is always not null.

I have added the following code to the beginning:

if (Global.TypesToScan == null) return;

After applying this fix, the receiver is able to process the message without any issues.

Regards,
Sarmaad

Check aggregate exists - NServiceBus MessageMutator

There are scenarios where I need to check

  • if an aggreagate with a specific Id exists
  • load a aggregate and perform some checks

This happens during validation of the command in the NServiceBus MessageMutator. At this point, there is no UnitOfWork started by NServiceBus.

How to handle this using NES?

Thanks
Marinko

Repository.Get

When I execute Repository.Get then is the idea to get an aggregate from repository. If it does not exists, I expect to get null. But your EventSourceMapper has the following code:

        public T Get<T>(Guid id) where T : class, IEventSource
        {
            Logger.Debug("Get event source Id '{0}', Type '{1}'", id, typeof(T).Name);

            var eventSource = _eventSourceFactory.Create<T>();

            RestoreSnapshot(id, eventSource);
            Hydrate(id, eventSource);

            return eventSource.Id != Guid.Empty ? eventSource : null;
        }

why you don't just check on the first line

if (id == Guid.Empty)
{
  return null;
}

What is the idea of doing this on the last line?

This can have side effects to get an instance back using the repository even if the aggregate does not exist on the database. This would happen if somebody sets automatically the id of the aggregate in the constructor (which may not be a good design).

Thanks
Marinko

Command doesn't send to CommandHandler

Dear @marinkobabic:/ @elliotritchie

My Command Class is as follows:

public class RegisterToConference : ICommand, IValidatableObject
    {
        public RegisterToConference()
        {
            this.Id = Guid.NewGuid();
            this.Seats = new Collection<SeatQuantity>();
        }

        public Guid Id { get; set; }

        public Guid OrderId { get; set; }

        public Guid ConferenceId { get; set; }

        public ICollection<SeatQuantity> Seats { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (this.Seats == null || !this.Seats.Any(x => x.Quantity > 0))
            {
                 return new[] { new ValidationResult("One or more items are required.", new[] { "Seats" }) };
            }
            else if (this.Seats.Any(x => x.Quantity < 0))
            {
                return new[] { new ValidationResult("Invalid registration.", new[] { "Seats" }) };
            }

            return Enumerable.Empty<ValidationResult>();
        }
    }

I send above command from my controllers action method as follows:

 [HttpPost]
        public ActionResult StartRegistration(RegisterToConference command, int orderVersion)
        {
            var existingOrder = orderVersion != 0 ? this.orderDao.FindDraftOrder(command.OrderId) : null;
            var viewModel = this.CreateViewModel();
            if (existingOrder != null)
            {
                UpdateViewModel(viewModel, existingOrder);
            }

            viewModel.OrderId = command.OrderId;

            if (!ModelState.IsValid)
            {
                return View(viewModel);
            }

            // checks that there are still enough available seats, and the seat type IDs submitted are valid.
            ModelState.Clear();
            bool needsExtraValidation = false;
            foreach (var seat in command.Seats)
            {
                var modelItem = viewModel.Items.FirstOrDefault(x => x.SeatType.Id == seat.SeatType);
                if (modelItem != null)
                {
                    if (seat.Quantity > modelItem.MaxSelectionQuantity)
                    {
                        modelItem.PartiallyFulfilled = needsExtraValidation = true;
                        modelItem.OrderItem.ReservedSeats = modelItem.MaxSelectionQuantity;
                    }
                }
                else
                {
                    // seat type no longer exists for conference.
                    needsExtraValidation = true;
                }
            }

            if (needsExtraValidation)
            {
                return View(viewModel);
            }

            command.ConferenceId = this.ConferenceAlias.Id;
            this.commandBus.Send(command);
            //var address = new Address(@"RegistrationEndPoint", "localhost");
            //this.commandBus.Send(address, command);

            return RedirectToAction(
                "SpecifyRegistrantAndPaymentDetails",
                new { conferenceCode = this.ConferenceCode, orderId = command.OrderId, orderVersion = orderVersion });
        }

Logically when I click on register button it had to send the command to registered command handler and executes further actions. But it doesn't. For kind consideration I am giving my all related codes with this action.

Note: Messages are saves in Queue properly.

Following is my command handler class:

public class OrderCommandHandler :
        IHandleMessages<RegisterToConference>
    {
        private readonly IRepository repository;
        private readonly IPricingService pricingService;

        public OrderCommandHandler(IRepository repository, IPricingService pricingService)
        {
            this.repository = repository;
            this.pricingService = pricingService;
        }

        public void Handle(RegisterToConference command)
        {
            var items = command.Seats.Select(t => new OrderItem(t.SeatType, t.Quantity)).ToList();
            var order = repository.Get<Registration.Order>(command.OrderId);
            if (order == null)
            {
                order = new Registration.Order(command.OrderId, command.ConferenceId, items, pricingService);
            }
            else
            {
                order.UpdateSeats(items, pricingService);
            }

            repository.Add<Registration.Order>(order);
        }

    }

The aggregate is as follows:

public class Order : AggregateBase<Guid>
    {

        private static readonly TimeSpan ReservationAutoExpiration = TimeSpan.FromMinutes(15);

        private List<SeatQuantity> seats;
        private bool isConfirmed;
        private Guid conferenceId;


        static Order()
        {
            Mapper.CreateMap<OrderPaymentConfirmed, OrderConfirmed>();
        }

        protected Order(Guid id)
        {
            this.Id = id;

        }
        private void Handle(OrderTotalsCalculated @event)
        {
            this.Id = @event.SourceId;
            this.OnOrderTotalsCalculated(@event);
        }
        private void Handle(OrderRegistrantAssigned @event)
        {
            this.Id = @event.SourceId;
            this.OnOrderRegistrantAssigned(@event);
        }

        private void Handle(OrderConfirmed @event)
        {
            this.Id = @event.SourceId;
            this.OnOrderConfirmed(@event);
        }

        private void Handle(OrderPaymentConfirmed @event)
        {
            this.Id = @event.SourceId;
            this.OnOrderConfirmed(Mapper.Map<OrderConfirmed>(@event));
        }

        private void Handle(OrderExpired @event)
        {
            this.Id = @event.SourceId;
            this.OnOrderExpired(@event);
        }

        private void Handle(OrderReservationCompleted @event)
        {
            this.Id = @event.SourceId;
            this.OnOrderReservationCompleted(@event);
        }

        private void Handle(OrderPlaced @event)
        {
            this.Id = @event.SourceId;
            this.OnOrderPlaced(@event);
        }

        private void Handle(OrderUpdated @event)
        {
            this.Id = @event.SourceId;
            this.OnOrderUpdated(@event);
        }

        private void Handle(OrderPartiallyReserved @event)
        {
            this.Id = @event.SourceId;
            this.OnOrderPartiallyReserved(@event);
        }


        public Order(Guid id, Guid conferenceId, IEnumerable<OrderItem> items, IPricingService pricingService)
            : this(id)
        {
            var all = ConvertItems(items);
            var totals = pricingService.CalculateTotal(conferenceId, all.AsReadOnly());

            this.Apply<OrderPlaced>(e =>
            {
                e.ConferenceId = conferenceId;
                e.Seats = all;
                e.ReservationAutoExpiration = DateTime.UtcNow.Add(ReservationAutoExpiration);
                e.AccessCode = HandleGenerator.Generate(6);
            });

            this.Apply<OrderTotalsCalculated>(e =>
            {
                e.Total = totals.Total;
                e.Lines = totals.Lines != null ? totals.Lines.ToArray() : null;
                e.IsFreeOfCharge = totals.Total == 0m;
            });

        }


        public void UpdateSeats(IEnumerable<OrderItem> items, IPricingService pricingService)
        {
            var all = ConvertItems(items);
            var totals = pricingService.CalculateTotal(this.conferenceId, all.AsReadOnly());

            this.Apply<OrderUpdated>(e =>
            {
                e.Seats = all;
            });

            this.Apply<OrderTotalsCalculated>(e =>
            {
                e.Total = totals.Total;
                e.Lines = totals.Lines != null ? totals.Lines.ToArray() : null;
                e.IsFreeOfCharge = totals.Total == 0m;
            });
        }

        public void MarkAsReserved(IPricingService pricingService, DateTime expirationDate, IEnumerable<SeatQuantity> reservedSeats)
        {
            if (this.isConfirmed)
                throw new InvalidOperationException("Cannot modify a confirmed order.");

            var reserved = reservedSeats.ToList();

            // Is there an order item which didn't get an exact reservation?
            if (this.seats.Any(item => item.Quantity != 0 && !reserved.Any(seat => seat.SeatType == item.SeatType && seat.Quantity == item.Quantity)))
            {
                var totals = pricingService.CalculateTotal(this.conferenceId, reserved.AsReadOnly());

                this.Apply<OrderPartiallyReserved>(e =>
                {
                    e.ReservationExpiration = expirationDate;
                    e.Seats = reserved.ToArray();
                });

                this.Apply<OrderTotalsCalculated>(e =>
                {
                    e.Total = totals.Total;
                    e.Lines = totals.Lines != null ? totals.Lines.ToArray() : null;
                    e.IsFreeOfCharge = totals.Total == 0m;
                });
            }
            else
            {
                this.Apply<OrderReservationCompleted>(e =>
                {
                    e.ReservationExpiration = expirationDate;
                    e.Seats = reserved.ToArray();
                });
            }
        }

        public void Expire()
        {
            if (this.isConfirmed)
                throw new InvalidOperationException("Cannot expire a confirmed order.");

            this.Apply<OrderExpired>(e =>
            {
                e.SourceId = this.Id;
            });

        }

        public void Confirm()
        {

            this.Apply<OrderConfirmed>(e =>
            {
                e.SourceId = this.Id;
            });
        }

        public void AssignRegistrant(string firstName, string lastName, string email)
        {

            this.Apply<OrderRegistrantAssigned>(e =>
            {
                e.FirstName = firstName;
                e.LastName = lastName;
                e.Email = email;
            });

        }

        public SeatAssignments CreateSeatAssignments()
        {
            if (!this.isConfirmed)
                throw new InvalidOperationException("Cannot create seat assignments for an order that isn't confirmed yet.");

            return new SeatAssignments(this.Id, this.seats.AsReadOnly());
        }

        private static List<SeatQuantity> ConvertItems(IEnumerable<OrderItem> items)
        {
            return items.Select(x => new SeatQuantity(x.SeatType, x.Quantity)).ToList();
        }

        private void OnOrderPlaced(OrderPlaced e)
        {
            this.conferenceId = e.ConferenceId;
            this.seats = e.Seats.ToList();
        }

        private void OnOrderUpdated(OrderUpdated e)
        {
            this.seats = e.Seats.ToList();
        }

        private void OnOrderPartiallyReserved(OrderPartiallyReserved e)
        {
            this.seats = e.Seats.ToList();
        }

        private void OnOrderReservationCompleted(OrderReservationCompleted e)
        {
            this.seats = e.Seats.ToList();
        }

        private void OnOrderExpired(OrderExpired e)
        {
        }

        private void OnOrderConfirmed(OrderConfirmed e)
        {
            this.isConfirmed = true;
        }

        private void OnOrderRegistrantAssigned(OrderRegistrantAssigned e)
        {
        }

        private void OnOrderTotalsCalculated(OrderTotalsCalculated e)
        {
        }
    }

Events are as follows:

public interface IVersionedEvent : IEvent
    {
        /// <summary>
        /// Gets the version or order of the event in the stream.
        /// </summary>
        Guid SourceId { get; set; }
        int Version { get; set; }
    }

public interface OrderPlaced : IVersionedEvent
    {
        Guid ConferenceId { get; set; }

        IEnumerable<SeatQuantity> Seats { get; set; }

        /// <summary>
        /// The expected expiration time if the reservation is not explicitly confirmed later.
        /// </summary>
        DateTime ReservationAutoExpiration { get; set; }

        string AccessCode { get; set; }
    }


public interface OrderTotalsCalculated : IVersionedEvent
    {
        decimal Total { get; set; }

        OrderLine[] Lines { get; set; }

        bool IsFreeOfCharge { get; set; }
    }

Events are handled as follows:

public class PricedOrderViewModelGenerator :
        IHandleMessages<OrderPlaced>,
        IHandleMessages<OrderTotalsCalculated>
    {
        private readonly Func<ConferenceRegistrationDbContext> contextFactory;
        private readonly ObjectCache seatDescriptionsCache;

        public PricedOrderViewModelGenerator(Func<ConferenceRegistrationDbContext> contextFactory)
        {
            this.contextFactory = contextFactory;
            this.seatDescriptionsCache = MemoryCache.Default;
        }

        public void Handle(OrderPlaced @event)
        {
            using (var context = this.contextFactory.Invoke())
            {
                var dto = new PricedOrder
                {
                    OrderId = @event.SourceId,
                    ReservationExpirationDate = @event.ReservationAutoExpiration,
                    OrderVersion = @event.Version
                };
                context.Set<PricedOrder>().Add(dto);
                try
                {
                    context.SaveChanges();
                }
                catch (DbUpdateException)
                {
                    Trace.TraceWarning(
                        "Ignoring OrderPlaced message with version {1} for order id {0}. This could be caused because the message was already handled and the PricedOrder entity was already created.",
                        dto.OrderId,
                        @event.Version);
                }
            }
        }

        public void Handle(OrderTotalsCalculated @event)
        {
            var seatTypeIds = @event.Lines.OfType<SeatOrderLine>().Select(x => x.SeatType).Distinct().ToArray();
            using (var context = this.contextFactory.Invoke())
            {
                var dto = context.Query<PricedOrder>().Include(x => x.Lines).First(x => x.OrderId == @event.SourceId);
                if (!WasNotAlreadyHandled(dto, @event.Version))
                {
                    // message already handled, skip.
                    return;
                }

                var linesSet = context.Set<PricedOrderLine>();
                foreach (var line in dto.Lines.ToList())
                {
                    linesSet.Remove(line);
                }

                var seatTypeDescriptions = GetSeatTypeDescriptions(seatTypeIds, context);

                for (int i = 0; i < @event.Lines.Length; i++)
                {
                    var orderLine = @event.Lines[i];
                    var line = new PricedOrderLine
                    {
                        LineTotal = orderLine.LineTotal,
                        Position = i,
                    };

                    var seatOrderLine = orderLine as SeatOrderLine;
                    if (seatOrderLine != null)
                    {
                        // should we update the view model to avoid losing the SeatTypeId?
                        line.Description = seatTypeDescriptions.Where(x => x.SeatTypeId == seatOrderLine.SeatType).Select(x => x.Name).FirstOrDefault();
                        line.UnitPrice = seatOrderLine.UnitPrice;
                        line.Quantity = seatOrderLine.Quantity;
                    }

                    dto.Lines.Add(line);
                }

                dto.Total = @event.Total;
                dto.IsFreeOfCharge = @event.IsFreeOfCharge;
                dto.OrderVersion = @event.Version;

                context.SaveChanges();
            }
        }

  }


public class DraftOrderViewModelGenerator :
        IEventHandler<OrderPlaced>
    {
        private readonly Func<ConferenceRegistrationDbContext> contextFactory;

        static DraftOrderViewModelGenerator()
        {
            // Mapping old version of the OrderPaymentConfirmed event to the new version.
            // Currently it is being done explicitly by the consumer, but this one in particular could be done
            // at the deserialization level, as it is just a rename, not a functionality change.
            Mapper.CreateMap<OrderPaymentConfirmed, OrderConfirmed>();
        }

        public DraftOrderViewModelGenerator(Func<ConferenceRegistrationDbContext> contextFactory)
        {
            this.contextFactory = contextFactory;
        }

        public void Handle(OrderPlaced @event)
        {
            using (var context = this.contextFactory.Invoke())
            {
                var dto = new DraftOrder(@event.SourceId, @event.ConferenceId, DraftOrder.States.PendingReservation, @event.Version)
                {
                    AccessCode = @event.AccessCode,
                };
                dto.Lines.AddRange(@event.Seats.Select(seat => new DraftOrderItem(seat.SeatType, seat.Quantity)));

                context.Save(dto);
            }
        }


    }


public class OrderEventHandler :
        IHandleMessages<OrderPlaced>,
        IHandleMessages<OrderTotalsCalculated>
    {
        private Func<ConferenceContext> contextFactory;

        public OrderEventHandler(Func<ConferenceContext> contextFactory)
        {
            this.contextFactory = contextFactory;
        }

        public void Handle(OrderPlaced @event)
        {
            using (var context = this.contextFactory.Invoke())
            {
                context.Orders.Add(new Order(@event.ConferenceId, @event.SourceId, @event.AccessCode));
                context.SaveChanges();
            }
        }


        public void Handle(OrderTotalsCalculated @event)
        {
            if (!ProcessOrder(order => order.Id == @event.SourceId, order => order.TotalAmount = @event.Total))
            {
                Trace.TraceError("Failed to locate the order with id {0} to apply calculated totals", @event.SourceId);
            }
        }

    }

My EndPoint Configuration is as follows:

public class EndpointConfig : IConfigureThisEndpoint, AsA_Server, IWantToRunWhenBusStartsAndStops, IWantToRunWhenConfigurationIsComplete
    {
        public void Init()
        {
            LogManager.Use<Log4NetFactory>();
        }

        public void Start()
        {
            Wireup.Init()
                .UsingInMemoryPersistence()
                .EnlistInAmbientTransaction()
                .NES()
                .Build();
        }

        public void Stop()
        {
        }

        public void Customize(BusConfiguration configuration)
        {
            configuration.UseSerialization<Json>();
            configuration.EnableInstallers();
            configuration.UsePersistence<InMemoryPersistence>();
            configuration.UseTransport<MsmqTransport>();
            configuration.PurgeOnStartup(false);
            configuration.RegisterComponents(c =>
            {
                c.ConfigureComponent<Repository>(DependencyLifecycle.InstancePerUnitOfWork);
                c.ConfigureComponent<SqlBlobStorage>(DependencyLifecycle.InstancePerUnitOfWork);
                c.ConfigureComponent<RegistrationProcessManagerRouter>(DependencyLifecycle.InstancePerUnitOfWork);
                c.ConfigureComponent<RegistrationProcessManager>(DependencyLifecycle.InstancePerUnitOfWork);
                c.ConfigureComponent<ConferenceViewModelGenerator>(DependencyLifecycle.InstancePerUnitOfWork);
                c.ConfigureComponent<DraftOrderViewModelGenerator>(DependencyLifecycle.InstancePerUnitOfWork);
                c.ConfigureComponent<OrderCommandHandler>(DependencyLifecycle.InstancePerUnitOfWork);
                c.ConfigureComponent<PricedOrderViewModelGenerator>(DependencyLifecycle.InstancePerUnitOfWork);
                c.ConfigureComponent<SeatAssignmentsHandler>(DependencyLifecycle.InstancePerUnitOfWork);
                c.ConfigureComponent<SeatAssignmentsViewModelGenerator>(DependencyLifecycle.InstancePerUnitOfWork);
                c.ConfigureComponent<SeatsAvailabilityHandler>(DependencyLifecycle.InstancePerUnitOfWork);
                c.ConfigureComponent<ValidationService>(DependencyLifecycle.SingleInstance);
                c.ConfigureComponent<ValidateIncomingMessages>(DependencyLifecycle.InstancePerCall);
            });
        }

        public void Run(Configure config)
        {
            config.NES();
        }
    }

Inside my web.config I configured as follows:

<configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="TransportConfig" type="NServiceBus.Config.TransportConfig, NServiceBus.Core" />
    <section name="UnicastBusConfig" type="NServiceBus.Config.UnicastBusConfig, NServiceBus.Core" />
    <section name="MessageForwardingInCaseOfFaultConfig" type="NServiceBus.Config.MessageForwardingInCaseOfFaultConfig, NServiceBus.Core" />
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>

 <TransportConfig MaxRetries="5" MaximumConcurrencyLevel="1" MaximumMessageThroughputPerSecond="0" />
  <UnicastBusConfig>
    <MessageEndpointMappings>
      <add Messages="Registration" Endpoint="Registration" />
    </MessageEndpointMappings>
  </UnicastBusConfig>
  <MessageForwardingInCaseOfFaultConfig ErrorQueue="RegistrationEndPoint.Errors" />

Inside Global.asax I registered Bus as follows:

 public static ISendOnlyBus Bus { get; private set; }
        private void RegisterBus()
        {
            var busConfiguration = new BusConfiguration();
            busConfiguration.UseSerialization<JsonSerializer>();
            busConfiguration.UseTransport<MsmqTransport>();
            busConfiguration.EndpointName("Conference.Web.Public");
            busConfiguration.Transactions().Disable();
            busConfiguration.PurgeOnStartup(false);

            LogManager.Use<NServiceBus.Log4Net.Log4NetFactory>();

            Bus = NServiceBus.Bus.CreateSendOnly(busConfiguration);
        }

Note: I am using Unity Container for Resolve Dependencies as follows:

private static UnityContainer CreateContainer()
        {
            var container = new UnityContainer();
            try
            {
                // repositories used by the application

                container.RegisterType<ConferenceRegistrationDbContext>(new TransientLifetimeManager(), new InjectionConstructor("ConferenceRegistration"));
                container.RegisterType<PaymentsReadDbContext>(new TransientLifetimeManager(), new InjectionConstructor("Payments"));

                var cache = new MemoryCache("ReadModel");
                container.RegisterType<IOrderDao, OrderDao>();
                container.RegisterType<IConferenceDao, CachingConferenceDao>(
                    new ContainerControlledLifetimeManager(),
                    new InjectionConstructor(new ResolvedParameter<ConferenceDao>(), cache));
                container.RegisterType<IPaymentDao, PaymentDao>();

                // configuration specific settings

                OnCreateContainer(container);

                return container;
            }
            catch
            {
                container.Dispose();
                throw;
            }
        }

Now I am really confused where I am making mistake.

Please help me to fix the issue.

NES and WebApi SelfHosted in NServiceBus Endpoint

Hi there,

I have one requirement to use a WebApi self hosted within NServiceBus. This is working fine. But I cannot manage to work NES inside my "POST" endpoint.

Im sure its something related to DependencyInjection of NServiceBus components that IRepository needs to use.

How can I apply the SAME DI from NSB to my self hosted WebApi ? Im using Autofac and setting mycontainer to the Bus. But the .NES() extension also add more instances to NSB container.

Should I get the NSB container and apply it to webapi ?

Bruno

Quick question

As I've been playing with the sample project that you posted to understand more about your framework, I am just curious of why the handlers never get called? Am I missing something?

Handling Event without Aggregate

If I am not wrong generally in NES.CQRS pattern
We send command from controller to Command Handler using Bus -> Command Handler handle the Command and send the data to Event Store as well as create an instance of Aggregate -> Aggregate Raise the Event and invoke the event handler -> Event Handler handles the event and save the data to database.

In some cases we have to use only domain event pattern i.e. no command will send only a event will raise and send to event handler. For instance my save method is as follows (note this complete crud method):

public void CreateConference(ConferenceInfo conference)
        {
            using (var context = new ConferenceContext(this.nameOrConnectionString))
            {
                var existingSlug = this.retryPolicy.ExecuteAction(() => 
                    context.Conferences
                        .Where(c => c.Slug == conference.Slug)
                        .Select(c => c.Slug)
                        .Any());

                if (existingSlug)
                    throw new DuplicateNameException("The chosen conference slug is already taken.");

                // Conference publishing is explicit. 
                if (conference.IsPublished)
                    conference.IsPublished = false;

                context.Conferences.Add(conference);
                this.retryPolicy.ExecuteAction(() => context.SaveChanges());

            }
        }

When data being save to database I also want to raise following event as well:

public interface ConferenceCreated : IEvent
    {
        Guid SourceId { get; set; }
        string Name { get; set; }
        string Description { get; set; }
        string Location { get; set; }
        string Slug { get; set; }
        string Tagline { get; set; }
        string TwitterSearch { get; set; }
        DateTime StartDate { get; set; }
        DateTime EndDate { get; set; }
        Owner Owner { get; set; }
    }

Above event will invoke following event Handler:

 public void Handle(ConferenceCreated @event)
        {
            using (var repository = this.contextFactory.Invoke())
            {
                var dto = repository.Find<Conference>(@event.SourceId);
                if (dto != null)
                {
                    Trace.TraceWarning(
                        "Ignoring ConferenceCreated event for conference with ID {0} as it was already created.",
                        @event.SourceId);
                }
                else
                {
                    repository.Set<Conference>().Add(
                        new Conference(
                            @event.SourceId,
                            @event.Slug,
                            @event.Name,
                            @event.Description,
                            @event.Location,
                            @event.Tagline,
                            @event.TwitterSearch,
                            @event.StartDate,
                            Enumerable.Empty<Registration.ReadModel.SeatType>()));

                    repository.SaveChanges();
                }
            }
        }

Note: This event will handle inside curd context so there will be no Aggregate.

To fix this issue I did as follows:

Created following methods inside my service class:

protected void Apply<TEvent>(Action<TEvent> action)
        {
            var @event = _eventFactory.Create(action);
            Raise(@event);
            _events.Add(@event);
        }

        private void Raise(object @event)
        {
            _eventHandlerFactory.Get(this, @event.GetType())(@event);
        }

private void PublishConferenceEvent(ConferenceInfo conference)
        {
            this.Apply<ConferenceCreated>(e =>
            {
                e.SourceId = conference.Id;
                e.Owner = new Owner
                {
                    Name = conference.OwnerName,
                    Email = conference.OwnerEmail,
                };
                e.Name = conference.Name;
                e.Description = conference.Description;
                e.Location = conference.Location;
                e.Slug = conference.Slug;
                e.Tagline = conference.Tagline;
                e.TwitterSearch = conference.TwitterSearch;
                e.StartDate = conference.StartDate;
                e.EndDate = conference.EndDate;
            });

 }

Then called the Publish method from CreateConference method as follows:

public void CreateConference(ConferenceInfo conference)
        {
            using (var context = new ConferenceContext(this.nameOrConnectionString))
            {
                var existingSlug = this.retryPolicy.ExecuteAction(() => 
                    context.Conferences
                        .Where(c => c.Slug == conference.Slug)
                        .Select(c => c.Slug)
                        .Any());

                if (existingSlug)
                    throw new DuplicateNameException("The chosen conference slug is already taken.");

                // Conference publishing is explicit. 
                if (conference.IsPublished)
                    conference.IsPublished = false;

                context.Conferences.Add(conference);
                this.retryPolicy.ExecuteAction(() => context.SaveChanges());

               this.PublishConferenceEvent(conference);
            }
        }

But it doesn't work. Any suggestions?

Expose NServiceBus Headers

When calling .Apply() to persist an event and dispatch it, I need to be able to set headers on the outbound NServiceBus event message.

If there is already a way to do this, please let me know how. Thanks.

InvalidOperationException occurred in EventConversionRunner.GetInterfaceType

I'm dealing with CQRS by utilizing NServiceBus, NES and RavenDB as event repository.

I have User aggregate with 2 methods: Create and ChangePassword.

When I send CreateUser command all works perfectly and I can see newly created event in Raven storage.
Then I send ChangePassword command. Behind the scenes command handler restores the User aggregate by calling Repository.Get(message.UserId);

At that point the InvalidOperationException exception is occurred at EventConversionRunner.GetInterfaceType line 32.

There was the type parameter with the following value:

{Name = "DynamicJsonObject" FullName = "Raven.Abstractions.Linq.DynamicJsonObject"}

By calling the type.FindInterfaces at line 32 the list of 3 items was returned:

[0] = {Name = "IDynamicMetaObjectProvider" FullName = "System.Dynamic.IDynamicMetaObjectProvider"}
[1] = {Name = "IEnumerable\`1" FullName = "System.Collections.Generic.IEnumerable\`1[[System.Object, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"}
[2] = {Name = "IDynamicJsonObject" FullName = "Raven.Abstractions.Linq.IDynamicJsonObject"}

That is why the exception is occurred.

Please give me a hint how I can fix this issue?

StackTrace:

at System.Linq.Enumerable.Single[TSource](IEnumerable1 source) at NES.EventConversionRunner.GetInterfaceType(Type type) in d:...\NES\EventConversionRunner.cs:line 32 at NES.EventConversionRunner.Run(Object event) in d:...\NES\EventConversionRunner.cs:line 20 at NES.EventStore.EventConverterPipelineHook.Select(Commit committed) in d:...\NES\EventStore\EventConverterPipelineHook.cs:line 21 at EventStore.OptimisticEventStore.<>c__DisplayClass1.<GetFrom>b__0(IPipelineHook x) in d:\Projects\Joliver.EventStore\src\proj\EventStore.Core\OptimisticEventStore.cs:line 67 at System.Linq.Enumerable.WhereArrayIterator1.MoveNext()
at EventStore.OptimisticEventStore.d__3.MoveNext() in d:\Projects\Joliver.EventStore\src\proj\EventStore.Core\OptimisticEventStore.cs:line 67
at EventStore.OptimisticEventStream.PopulateStream(Int32 minRevision, Int32 maxRevision, IEnumerable`1 commits) in d:\Projects\Joliver.EventStore\src\proj\EventStore.Core\OptimisticEventStream.cs:line 46
at EventStore.OptimisticEventStream..ctor(Guid streamId, ICommitEvents persistence, Int32 minRevision, Int32 maxRevision) in d:\Projects\Joliver.EventStore\src\proj\EventStore.Core\OptimisticEventStream.cs:line 33
at EventStore.OptimisticEventStore.OpenStream(Guid streamId, Int32 minRevision, Int32 maxRevision) in d:\Projects\Joliver.EventStore\src\proj\EventStore.Core\OptimisticEventStore.cs:line 50
at NES.EventStore.EventStoreAdapter.Read(Guid id, Int32 version) in d:...\NES\EventStore\EventStoreAdapter.cs:line 25
at NES.EventSourceMapper.Hydrate[T](Guid id, T eventSource) in d:...\NES\EventSourceMapper.cs:line 73
at NES.EventSourceMapper.Get[T](Guid id) in d:...\NES\EventSourceMapper.cs:line 23
at NES.UnitOfWork.Get[T](Guid id) in d:...\NES\UnitOfWork.cs:line 22
at NES.Repository.Get[T](Guid id) in d:...\NES\Repository.cs:line 9
at CommandHandler.UserCommandHandler.Handle(ChangeUserPassword message) in d:...\CommandHandler\UserCommandHandler.cs:line 38

Optimistic concurrency control by NES and NeventStore+SQL

Hi there,
I've noticed that when the stream is commited, NES performs a read first to detect concurrency:

using (IEventStream eventStream = this._eventStore.OpenStream(bucketId, id, version, int.MaxValue))
    {
        EventStoreAdapter.Logger.Debug("Opened stream has StreamRevision {0}", (object) eventStream.StreamRevision);
        if (version != eventStream.StreamRevision && Transaction.Current != (Transaction) null)
        { 
            throw new ConflictingCommandException

Still there's a possibility for concurrency issue this way (result of OpenStream becomes stale immediately). I am using NEventStore with SQL persistence and the consistency of event stream is guaranteed by the database (PK on bucket, streamId and version). So the OpenStream looks like a redundant action in that case for me.
Please comment on that, thank you!

NullReferenceException: Object reference not set to an instance of an object.

private static readonly ILogger Logger = LoggerFactory.Create(typeof(EventHandlerFactory)); return null reference exception.

I have written my code as follows:

` private static readonly IEventFactory _eventFactory = DI.Current.Resolve();
private static readonly IEventHandlerFactory _eventHandlerFactory = DI.Current.Resolve();
private readonly string nameOrConnectionString;
private readonly RetryPolicy retryPolicy;

    public ConferenceService(string nameOrConnectionString = "ConferenceManagement")
    {

        this.nameOrConnectionString = nameOrConnectionString;

        this.retryPolicy = new RetryPolicy<SqlAzureTransientErrorDetectionStrategy>(new Incremental(5, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1.5)) { FastFirstRetry = true });
        this.retryPolicy.Retrying += (s, e) =>
            Trace.TraceWarning("An error occurred in attempt number {1} to access the database in ConferenceService: {0}", e.LastException.Message, e.CurrentRetryCount);

    }

    public void CreateConference(ConferenceInfo conference)
    {
        using (var context = new ConferenceContext(this.nameOrConnectionString))
        {
            var existingSlug = this.retryPolicy.ExecuteAction(() => 
                context.Conferences
                    .Where(c => c.Slug == conference.Slug)
                    .Select(c => c.Slug)
                    .Any());

            if (existingSlug)
                throw new DuplicateNameException("The chosen conference slug is already taken.");

            // Conference publishing is explicit. 
            if (conference.IsPublished)
                conference.IsPublished = false;

            context.Conferences.Add(conference);
            this.retryPolicy.ExecuteAction(() => context.SaveChanges());

           this.PublishConferenceEvent<ConferenceCreated>(conference);
        }
    }

private void PublishConferenceEvent(ConferenceInfo conference)
where T : ConferenceEvent, new()
{

        Action<T> action = x => new T
        {
            SourceId = conference.Id,
            Owner = new Owner
            {
                Name = conference.OwnerName,
                Email = conference.OwnerEmail,
            },
            Name = conference.Name,
            Description = conference.Description,
            Location = conference.Location,
            Slug = conference.Slug,
            Tagline = conference.Tagline,
            TwitterSearch = conference.TwitterSearch,
            StartDate = conference.StartDate,
            EndDate = conference.EndDate,
        };

        Apply(action);
    }

   protected void Apply<TEvent>(Action<TEvent> action)
    {
       var @event = _eventFactory.Create(action);
       Raise(@event);
    }

    private void Raise(object @event)
    {
        _eventHandlerFactory.Get(this, @event.GetType())(@event);
    }

`

When I run the application and trying to create a conference it returns following error message

Object reference not set to an instance of an object.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error:

Line 10: public class EventHandlerFactory : IEventHandlerFactory
Line 11: {
Line 12: private static readonly ILogger Logger = LoggerFactory.Create(typeof(EventHandlerFactory));
Line 13: private static readonly Dictionary<Type, Dictionary<Type, Action<object, object>>> _cache = new Dictionary<Type, Dictionary<Type, Action<object, object>>>();
Line 14: private static readonly object _cacheLock = new object();

Source File: F:\MyProjects\NES\NES-master\src\NES\EventHandlerFactory.cs Line: 12

Stack Trace:

[NullReferenceException: Object reference not set to an instance of an object.]
NES.EventHandlerFactory..cctor() in F:\MyProjects\NES\NES-master\src\NES\EventHandlerFactory.cs:12

[TypeInitializationException: The type initializer for 'NES.EventHandlerFactory' threw an exception.]
NES.EventHandlerFactory..ctor() +0
NES.<>c.<.cctor>b__4_4() in F:\MyProjects\NES\NES-master\src\NES\DI.cs:24
NES.DependencyInjectionContainer.Resolve() in F:\MyProjects\NES\NES-master\src\NES\DependencyInjectionContainer.cs:21
Conference.ConferenceService..cctor() in F:\MyProjects\NES\NES-master\samples\Conference\ConferenceService.cs:23

[TypeInitializationException: The type initializer for 'Conference.ConferenceService' threw an exception.]
Conference.ConferenceService..ctor(String nameOrConnectionString) in F:\MyProjects\NES\NES-master\samples\Conference\ConferenceService.cs:36
Conference.Web.Admin.Controllers.ConferenceController.get_Service() in F:\MyProjects\NES\NES-master\samples\Conference.Web.Admin\Controllers\ConferenceController.cs:32
Conference.Web.Admin.Controllers.ConferenceController.Create(ConferenceInfo conference) in F:\MyProjects\NES\NES-master\samples\Conference.Web.Admin\Controllers\ConferenceController.cs:119
lambda_method(Closure , ControllerBase , Object[] ) +180
System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters) +68
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) +486 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 parameters) +71
System.Web.Mvc.Async.ActionInvocation.InvokeSynchronousActionMethod() +85
System.Web.Mvc.Async.AsyncControllerActionInvoker.b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState) +44
System.Web.Mvc.Async.WrappedAsyncResult2.CallEndDelegate(IAsyncResult asyncResult) +82 System.Web.Mvc.Async.WrappedAsyncResultBase1.End() +143
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +109
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +58
System.Web.Mvc.Async.AsyncInvocationWithFilters.b__3d() +121
System.Web.Mvc.Async.<>c__DisplayClass46.b__3f() +321
System.Web.Mvc.Async.<>c__DisplayClass33.b__32(IAsyncResult asyncResult) +53
System.Web.Mvc.Async.WrappedAsyncResult1.CallEndDelegate(IAsyncResult asyncResult) +56 System.Web.Mvc.Async.WrappedAsyncResultBase1.End() +143
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +109
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +58
System.Web.Mvc.Async.<>c__DisplayClass2b.b__1c() +76
System.Web.Mvc.Async.<>c__DisplayClass21.b__1e(IAsyncResult asyncResult) +184
System.Web.Mvc.Async.WrappedAsyncResult1.CallEndDelegate(IAsyncResult asyncResult) +53 System.Web.Mvc.Async.WrappedAsyncResultBase1.End() +140
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +63
System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +50
System.Web.Mvc.Controller.b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +45
System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +81 System.Web.Mvc.Async.WrappedAsyncResultBase1.End() +140
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +63
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +47
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +50
System.Web.Mvc.Controller.b__15(IAsyncResult asyncResult, Controller controller) +50
System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +73 System.Web.Mvc.Async.WrappedAsyncResultBase1.End() +140
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +63
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +47
System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +50
System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +50
System.Web.Mvc.MvcHandler.b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +45
System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +81 System.Web.Mvc.Async.WrappedAsyncResultBase1.End() +140
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +63
System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +47
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +50
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +49
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9644037
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155

Custom JSON Serialization

How do I provide NES (for EventStore and NServiceBus) information for customizing JSON serialization, such as the use of JsonConverter's?

Running NService bus host when having un-dispatched commits raise error (Object reference not set to an instance of an object)

Hello,
I'm getting (Object reference not set to an instance of an object). exception when there are un-dispatched commits in database,
The error is because _eventPublisherFactory() failed to resolve the (IEventPublisher) interface, which I believe is because the start-up process is not yet completed and the interface is not registered yet,

Here is the stack trace:

NES.NEventStore.dll!NES.NEventStore.MessageDispatcher.Dispatch(NEventStore.Commit commit) Line 19   
    NEventStore.dll!NEventStore.Dispatcher.SynchronousDispatchScheduler.DispatchImmediately(NEventStore.Commit commit) Line 65  
    NEventStore.dll!NEventStore.Dispatcher.SynchronousDispatchScheduler.ScheduleDispatch(NEventStore.Commit commit) Line 32 
    NEventStore.dll!NEventStore.Dispatcher.SynchronousDispatchScheduler.Start() Line 54 
    NEventStore.dll!NEventStore.Dispatcher.SynchronousDispatchScheduler.SynchronousDispatchScheduler(NEventStore.Dispatcher.IDispatchCommits dispatcher, NEventStore.Persistence.IPersistStreams persistence) Line 21   
    NES.NEventStore.dll!NES.NEventStore.NESWireup..ctor.AnonymousMethod__1(NEventStore.NanoContainer c) Line 30 
    NEventStore.dll!NEventStore.NanoContainer.Register<NEventStore.Dispatcher.IScheduleDispatches>.AnonymousMethod__0(NEventStore.NanoContainer c) Line 18  
    NEventStore.dll!NEventStore.ContainerRegistration.Resolve(NEventStore.NanoContainer container) Line 99  
    NEventStore.dll!NEventStore.NanoContainer.Resolve<NEventStore.Dispatcher.IScheduleDispatches>() Line 48 
    NEventStore.dll!NEventStore.Wireup.BuildEventStore(NEventStore.NanoContainer context) Line 74   
    NEventStore.dll!NEventStore.NanoContainer.Register<NEventStore.IStoreEvents>.AnonymousMethod__0(NEventStore.NanoContainer c) Line 18    
    NEventStore.dll!NEventStore.ContainerRegistration.Resolve(NEventStore.NanoContainer container) Line 99  
    NEventStore.dll!NEventStore.NanoContainer.Resolve<NEventStore.IStoreEvents>() Line 48   
    NEventStore.dll!NEventStore.Wireup.Build() Line 67  
    NEventStore.dll!NEventStore.Wireup.Build() Line 64  
    NEventStore.dll!NEventStore.PersistenceWireup.Build() Line 78   
    NEventStore.dll!NEventStore.Wireup.Build() Line 64  
    NES.NEventStore.dll!NES.NEventStore.NESWireup.Build() Line 63   
    NEventStore.dll!NEventStore.Wireup.Build() Line 64  
    eClaim.NES.Host.dll!eClaim.NES.Host.EndpointConfig.Init() Line 39   
    NServiceBus.Core.dll!NServiceBus.Hosting.GenericHost.PerformConfiguration() Line 154    
    NServiceBus.Core.dll!NServiceBus.Hosting.GenericHost.Start() Line 69    
    NServiceBus.Host.exe!NServiceBus.Hosting.Windows.WindowsHost.Start() Line 53    
    NServiceBus.Host.exe!NServiceBus.Hosting.Windows.Program.Main.AnonymousMethod__5(NServiceBus.Hosting.Windows.WindowsHost service) Line 81   
    NServiceBus.Host.exe!Topshelf.Internal.ControllerDelegates<NServiceBus.Hosting.Windows.WindowsHost>.StartActionObject(object obj) Line 18   
    NServiceBus.Host.exe!Topshelf.Internal.IsolatedServiceControllerWrapper<NServiceBus.Hosting.Windows.WindowsHost>.set_StartAction.AnonymousMethod__1(NServiceBus.Hosting.Windows.WindowsHost service) Line 65    
    NServiceBus.Host.exe!Topshelf.Internal.ServiceController<NServiceBus.Hosting.Windows.WindowsHost>..cctor.AnonymousMethod__1(Topshelf.Internal.ServiceController<NServiceBus.Hosting.Windows.WindowsHost> sc) Line 35    
    NServiceBus.Host.exe!Magnum.StateMachine.LambdaAction<Topshelf.Internal.ServiceController<NServiceBus.Hosting.Windows.WindowsHost>>.Execute(Topshelf.Internal.ServiceController<NServiceBus.Hosting.Windows.WindowsHost> instance, Magnum.StateMachine.Event event, object parameter)   Unknown
    NServiceBus.Host.exe!Magnum.StateMachine.EventActionList<Topshelf.Internal.ServiceController<NServiceBus.Hosting.Windows.WindowsHost>>.Execute(Topshelf.Internal.ServiceController<NServiceBus.Hosting.Windows.WindowsHost> stateMachine, Magnum.StateMachine.Event event, object parameter)    Unknown
    NServiceBus.Host.exe!Magnum.StateMachine.EventActionBase<Topshelf.Internal.ServiceController<NServiceBus.Hosting.Windows.WindowsHost>>.Execute(Topshelf.Internal.ServiceController<NServiceBus.Hosting.Windows.WindowsHost> instance, Magnum.StateMachine.Event event, object parameter)    Unknown
    NServiceBus.Host.exe!Magnum.StateMachine.State<Topshelf.Internal.ServiceController<NServiceBus.Hosting.Windows.WindowsHost>>.RaiseEvent(Topshelf.Internal.ServiceController<NServiceBus.Hosting.Windows.WindowsHost> instance, Magnum.StateMachine.BasicEvent<Topshelf.Internal.ServiceController<NServiceBus.Hosting.Windows.WindowsHost>> eevent, object value)   Unknown
    NServiceBus.Host.exe!Magnum.StateMachine.StateMachine<Topshelf.Internal.ServiceController<NServiceBus.Hosting.Windows.WindowsHost>>.RaiseEvent(Magnum.StateMachine.Event raised)    Unknown
    NServiceBus.Host.exe!Topshelf.Internal.ServiceController<NServiceBus.Hosting.Windows.WindowsHost>.Start() Line 77   
    NServiceBus.Host.exe!Topshelf.Internal.IsolatedServiceControllerWrapper<NServiceBus.Hosting.Windows.WindowsHost>.Start() Line 40    
    NServiceBus.Host.exe!Topshelf.Internal.ServiceControllerProxy.Start() Line 47   
    [AppDomain (NServiceBus.Host.exe, #1) -> AppDomain (NServiceBus.Hosting.Windows.WindowsHost, NServiceBus.Host, Version=4.4.0.0, Culture=neutral, PublicKeyToken=9fc386479f8a226c, #2)]  
    NServiceBus.Host.exe!Topshelf.Internal.FacadeToIsolatedServiceController<NServiceBus.Hosting.Windows.WindowsHost>.Start() Line 62   
    NServiceBus.Host.exe!Topshelf.Internal.ServiceCoordinator.Start() Line 48   
    NServiceBus.Host.exe!Topshelf.Internal.Hosts.ConsoleHost.Run() Line 53  Unknown
    NServiceBus.Host.exe!Topshelf.Internal.Actions.RunAsConsoleAction.Do(Topshelf.Configuration.IRunConfiguration configuration) Line 33    
    NServiceBus.Host.exe!Topshelf.Runner.Host(Topshelf.Configuration.IRunConfiguration configuration, string[] args) Line 70    
    NServiceBus.Host.exe!NServiceBus.Hosting.Windows.Program.Main(string[] args) Line 144   

Handle Events from entity

I hvae to handle some events from domain model. Then how I will register them with EventHandlers? My domain model is not inherited by IHandleMessages<IDomainEvent>

Consider the following scenerio:

I have following two dictionaries inside my domain model:

private readonly Dictionary<Guid, int> remainingSeats = new Dictionary<Guid, int>(); private readonly Dictionary<Guid, List<SeatQuantity>> pendingReservations = new Dictionary<Guid, List<SeatQuantity>>();

Now I want to manipulate those dictionary from domain model as follows:

private void OnAvailableSeatsChanged(AvailableSeatsChanged e)
        {
            foreach (var seat in e.Seats)
            {
                int newValue = seat.Quantity;
                int remaining;
                if (this.remainingSeats.TryGetValue(seat.SeatType, out remaining))
                {
                    newValue += remaining;
                }

                this.remainingSeats[seat.SeatType] = newValue;
            }
        }

        private void OnSeatsReserved(SeatsReserved e)
        {
            var details = e.ReservationDetails.ToList();
            if (details.Count > 0)
            {
                this.pendingReservations[e.ReservationId] = details;
            }
            else
            {
                this.pendingReservations.Remove(e.ReservationId);
            }

            foreach (var seat in e.AvailableSeatsChanged)
            {
                this.remainingSeats[seat.SeatType] = this.remainingSeats[seat.SeatType] + seat.Quantity;
            }
        }

        private void OnSeatsReservationCommitted(SeatsReservationCommitted e)
        {
            this.pendingReservations.Remove(e.ReservationId);
        }

        private void OnSeatsReservationCancelled(SeatsReservationCancelled e)
        {
            this.pendingReservations.Remove(e.ReservationId);

            foreach (var seat in e.AvailableSeatsChanged)
            {
                this.remainingSeats[seat.SeatType] = this.remainingSeats[seat.SeatType] + seat.Quantity;
            }
        }

Even there is no method found for Update existing event in AggregateBase class. How will I update my existing event?

How can I move forward by solving above issues?

Thanks

Use JsonConverter if they are available

Hello,
I have a suggestion: use JsonConverter collection if it is available in JsonConvert.DefaultSettings().Converters or (optionally) let user pass converters or whole settings in wire-up method UseJsonSerialization().

It would require just small non-breaking change here https://github.com/elliotritchie/NES/blob/master/src/NES.NEventStore/JsonSerializer.cs#L21

something along the lines:

public JsonSerializer(Func<IEventMapper> eventMapperFunc, Func<IEventFactory> eventFactoryFunc)
{
    _settings = GetJsonSerializerSettings();
    _eventMapperFunc = eventMapperFunc;
    _eventFactoryFunc = eventFactoryFunc;
}

private static JsonSerializerSettings GetJsonSerializerSettings()
{
    var jss = JsonConvert.DefaultSettings != null 
        ? JsonConvert.DefaultSettings.Invoke() 
        : new JsonSerializerSettings();

    jss.TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple;
    jss.TypeNameHandling = TypeNameHandling.Auto;
    jss.DefaultValueHandling = DefaultValueHandling.Ignore;
    jss.NullValueHandling = NullValueHandling.Ignore;

    return jss;
}

...

Multi Tenancy and IEventStore

Hi Elliot

What I'm actually missing is the IEventStoreFactory which gets the actual commandContext to be able to return a IEventStore based on the TanantId. This means , when the property TenantId (GUID) exists on the commandType it will be set on the new CommandContext property TenantId.

To be able to handle this the actual WireUp extension will not be used. NESWireup.InitializeMultitenancy() will be executed.

What do you think?

Cannot deserialize events containing properties with interface types

Events of this form are not deserialized correctly (throws a NullReferenceException)

public interface MyEvent : IEvent
{
         IMyInterface MyProp {get; set;}

}

The problem is that this piece of code

protected override JsonObjectContract CreateObjectContract(Type objectType)
        {
            if (objectType.IsInterface)
            {
                var mappedType = _eventMapper.GetMappedTypeFor(objectType);
                var objectContract = base.CreateObjectContract(mappedType);

                objectContract.DefaultCreator = () => _eventFactory.Create(mappedType);

                return objectContract;
            }

            return base.CreateObjectContract(objectType);
        }

asks nServiceBus to create a proxy also for the interface IMyInterface, of which it knows nothing about.

I fixed it for my code in this way but I do not know if it is the best/correct way.

protected override JsonObjectContract CreateObjectContract(Type objectType)
        {
            if (objectType.IsInterface  && _eventMapper.GetMappedTypeFor(objectType) != null)
            {
                var mappedType = _eventMapper.GetMappedTypeFor(objectType);
                var objectContract = base.CreateObjectContract(mappedType);

                objectContract.DefaultCreator = () => _eventFactory.Create(mappedType);

                return objectContract;
            }

            return base.CreateObjectContract(objectType);
        }

This is the exception thrown:

System.ArgumentNullException was unhandled by user code
  HResult=-2147467261
  Message=Il valore non può essere null.
Nome parametro: underlyingType
  Source=Newtonsoft.Json
  ParamName=underlyingType
  StackTrace:
       in Newtonsoft.Json.Serialization.JsonContract..ctor(Type underlyingType)
       in Newtonsoft.Json.Serialization.JsonContainerContract..ctor(Type underlyingType)
       in Newtonsoft.Json.Serialization.JsonObjectContract..ctor(Type underlyingType)
       in Newtonsoft.Json.Serialization.DefaultContractResolver.CreateObjectContract(Type objectType)
       in NES.EventStore.EventContractResolver.CreateObjectContract(Type objectType)ù

Documentation / Tutorial

I believe the documentation of this project is extremely sparse (sorry, I know its not the fun part of a project ;) ). I'm not able to fully understand the code, even though the underlying concepts are slightly familiar (took some CQRS/event sourcing) work shops. Especially the sample is a bit of a mystery since it doesn't seem to use NEventStore but a XmlDocument for storage, (isn't the whole idea of NES to use NEventStore?). I'm also having trouble understanding from the sample why so much code needs to be duplicated (the create commands and events seem to do almost the same).

Now this all is probably due to a gap in knowledge, but without at least something resembling a tutorial I'm afraid I can't get much further (in a time efficient manner that is ;) ).

Is there any more documentation, or better: a tutorial, for the NES project? Or are there other resources that I should read first (maybe some NEventStore and NServiceBus manuals that I should read first, though the NEventStore documentation doesn't seem to be much more comprehensive :( ). So that its easier to get into NES? Or maybe NES isn't the right CQRS/Event Sourcing framework to start off with? I'm open to suggestions?

NES.StringWay

Is your intention with this new namespace to make a NES that allows both Guid and string ids?
I am still playing with the v5 branch version and happen to need aggregates with string ids for a specific root.

The implementation looks like that is what you are aiming for, but IRepositoryAdd still only accepts an AggregateBase with a Guid id so its kind of useless for adding.

I am attempting to fix it up but need to know what your intentions for the namespace are

Thanks

Events from Entities within the AggregateRoot

Hi there,

I was wondering if how could one handle events from Entities within the aggragate root. I mean entities that are part of the AR but not the AR itself.

I can see only a class for BaseAggregate, but I do not see a class for Entities.

I can see IEventSource (which I infer is the currently way to achieve what I need)

Is this the correct approach using NES?

Implement the IEventSource from an Entity so it can produce Events as well ?

Example: the AR has a method to createNewEntity(entity data) that will in fact use new Entity(). And within this entity I would have Apply()

And all these Events are persisted with the AR

Any thoughts?

Thanks a lot

Bruno Bertechini

Handle Events from entity

I hvae to handle some events from domain model. Then how I will register them with EventHandlers? My domain model is not inherited by IHandleMessages<IDomainEvent>

Consider the following scenerio:

I have following two dictionaries inside my domain model:

private readonly Dictionary<Guid, int> remainingSeats = new Dictionary<Guid, int>(); private readonly Dictionary<Guid, List<SeatQuantity>> pendingReservations = new Dictionary<Guid, List<SeatQuantity>>();

Now I want to manipulate those dictionary from domain model as follows:

private void OnAvailableSeatsChanged(AvailableSeatsChanged e)
        {
            foreach (var seat in e.Seats)
            {
                int newValue = seat.Quantity;
                int remaining;
                if (this.remainingSeats.TryGetValue(seat.SeatType, out remaining))
                {
                    newValue += remaining;
                }

                this.remainingSeats[seat.SeatType] = newValue;
            }
        }

        private void OnSeatsReserved(SeatsReserved e)
        {
            var details = e.ReservationDetails.ToList();
            if (details.Count > 0)
            {
                this.pendingReservations[e.ReservationId] = details;
            }
            else
            {
                this.pendingReservations.Remove(e.ReservationId);
            }

            foreach (var seat in e.AvailableSeatsChanged)
            {
                this.remainingSeats[seat.SeatType] = this.remainingSeats[seat.SeatType] + seat.Quantity;
            }
        }

        private void OnSeatsReservationCommitted(SeatsReservationCommitted e)
        {
            this.pendingReservations.Remove(e.ReservationId);
        }

        private void OnSeatsReservationCancelled(SeatsReservationCancelled e)
        {
            this.pendingReservations.Remove(e.ReservationId);

            foreach (var seat in e.AvailableSeatsChanged)
            {
                this.remainingSeats[seat.SeatType] = this.remainingSeats[seat.SeatType] + seat.Quantity;
            }
        }

Can you please advise me regarding above issue?

Thanks

Note for new Doc

I've been playing with NES 5.0 and ran into an issue you'll want to include when you update the docs.
Since you are now using IWantToRunWhenConfigurationIsComplete to run NES's ConfigurationRunner its important that users include NES in assembly scanning.

I am expliciting listing assemblies to scan for NServiceBus and had to add NES otherwise I was getting a null reference exception in EventHandlerFactory ctor

Repository should work even when not wired to NSB.

I want to write some unit tests around entities that derive from AggregateBase and are stored and retrieved using a Repository.

EventStore has an in-memory persistance store that would be ideal for this. I should be able to put something like the following in with my unit tests:

[AssemblyInitialize]
public static void AssemblyInit(TestContext context)
{
Wireup.Init()
.UsingInMemoryPersistence()
.NES()
.Build();
}

This should be enough to the use the repository in my unit tests - but its not. I get a null reference exception when trying to call repository.Add(myentity) because there's no current UnitOfWorkFactory. I think this is because it's not wired to NServiceBus.

I think the EventStore side of things should still work even when there's nothing to dispatch events to. It should still allow for storage and retrieval of the events. right?

The alternative to this approach would be to not use the NES repository at all, but to use something like Rhino Mocks and mock out the IRepository interface to a MockRepository on each call. This would be acceptable, but it would be a lot easier just to rely on the memory persistance option on EventStore.

Thanks,
Matt

IReplayCommits - Implementation with access to IStoreEvents, IEventPublisher

Hi Elliot! First off, thank you very much for this contribution - I am grateful for having a lot of the cruft of CQRS/ES already implemented, especially with the winning combination of NServiceBus and Jonathan's EventStore. With a few lines of code, we can expose a replay utility, enabling one to publish already committed events. This utility could then be leveraged in some administration tool / UI (in fact, that's exactly what we're doing with a product in production). Gist is linked below, I'll make a pull request this evening or tomorrow morning.

https://gist.github.com/2409876

Separate nuget package without dependencies

I'm using NES in a very large solution with many projects. Several of the projects only require NES because they contain entities derived from AggregateBase, or they use the IRepository interface. They don't use EventStore or NServiceBus themselves. Those are used in other projects within the solution.

The current nuget package for NES has package dependencies on NServiceBus and EventStore. When I use NES on these satellite projects, I have to explicitly tell Nuget to ignore package dependencies. This creates friction when applying updates.

Other projects handle this problem by splitting into two packages, one main package with the dependencies and one without. For example, see the "NServiceBus.Interfaces" nuget package.

I'd like to request that NES take a similar approach. The primary NES package could depend on a NES.Core package which carries no external dependencies. NES.dll could then be split up into NES.dll and NES.Core.dll and put into the appropriate packages.

I'm open to other ideas around this subject. Thanks again for the great work on this.

NES with NEventStore and NServiceBus on Visual Studio 2013

Is NES compatible with Visual Studio 2013 ?

I am using NES + NEventStore + NServiceBus 4.2.0 without issues on Visual Studio 2012.

We are trying to move on to new version (2013) but any method from NES Repository always throws an AccessViolationException when using Visual Studio 2013.

Any comments:

Bruno

If an event has no interfaces, retrieving an aggregate from repository will throw ambigous exception

I forgot to implement IEvent in my event class, when I tried to call
eventSourceRepository.Get(Id);

I got this exception:

System.InvalidOperationException: Sequence contains no elements at System.Linq.Enumerable.Single[TSource](IEnumerable1 source)
at NES.EventConversionRunner.GetInterfaceType(Type type) in d:\Libraries\NES\NES4\src\NES\EventConversionRunner.cs:line 32
at NES.EventConversionRunner.Run(Object event) in d:\Libraries\NES\NES4\src\NES\EventConversionRunner.cs:line 20
at NES.NEventStore.EventConverterPipelineHook.Select(Commit committed) in d:\Libraries\NES\NES4\src\NES.NEventStore\EventConverterPipelineHook.cs:line 21
at NEventStore.OptimisticEventStore.<>c__DisplayClass1.b__0(IPipelineHook x) in d:\Libraries\NEventStore\NEventStore-4.1.0.10\src\NEventStore\OptimisticEventStore.cs:line 31
at System.Linq.Enumerable.WhereArrayIterator1.MoveNext() at NEventStore.OptimisticEventStore.<GetFrom>d__3.MoveNext() in d:\Libraries\NEventStore\NEventStore-4.1.0.10\src\NEventStore\OptimisticEventStore.cs:line 31 at NEventStore.OptimisticEventStream.PopulateStream(Int32 minRevision, Int32 maxRevision, IEnumerable1 commits) in d:\Libraries\NEventStore\NEventStore-4.1.0.10\src\NEventStore\OptimisticEventStream.cs:line 126
at NEventStore.OptimisticEventStream..ctor(Guid streamId, ICommitEvents persistence, Int32 minRevision, Int32 maxRevision) in d:\Libraries\NEventStore\NEventStore-4.1.0.10\src\NEventStore\OptimisticEventStream.cs:line 32
at NEventStore.OptimisticEventStore.OpenStream(Guid streamId, Int32 minRevision, Int32 maxRevision) in d:\Libraries\NEventStore\NEventStore-4.1.0.10\src\NEventStore\OptimisticEventStore.cs:line 95
at NES.NEventStore.EventStoreAdapter.Read(Guid id, Int32 version) in d:\Libraries\NES\NES4\src\NES.NEventStore\EventStoreAdapter.cs:line 25
at NES.EventSourceMapper.Hydrate[T](Guid id, T eventSource) in d:\Libraries\NES\NES4\src\NES\EventSourceMapper.cs:line 85
at NES.EventSourceMapper.Get[T](Guid id) in d:\Libraries\NES\NES4\src\NES\EventSourceMapper.cs:line 26
at NES.UnitOfWork.Get[T](Guid id) in d:\Libraries\NES\NES4\src\NES\UnitOfWork.cs:line 23
at NES.Repository.Get[T](Guid id) in d:\Libraries\NES\NES4\src\NES\Repository.cs:line 9`

I guess it is assuming that event should have intefaces, that's why (Single) method is used when looking for interfaces, at least we can provide a more friendly message.

NServiceBus message headers using NES

Hi,

Is there any way I can ser a message header (when using nservice bus)?

Is there any hook in place? I would loke to carry some data using headers and dont add tjese properties on every event.

Any suggestions?

Bruno

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.