Rebus: Multiple sagas handling same message -OR- best practice for request/reply in saga - rebus

I have a scenario where I'm processing a message in a Saga and might need more information and thus send a request message for which I expect a reply.
The issue is that the reply does not have information unique to the current saga - it has an ID that I would like to use but multiple sagas might be requesting this ID at the same time and thus Rebus disallows it (exception: Correlation property 'Bla' has value '86' in existing saga data with ID 2d12a863-12ed-4632-82d8-290e041c4eed).
If there is no way to have a single message be handled by multiple sagas the alternative for me would be to be able to match the reply to the requesting saga. As far as I can tell there used to be support for this, but it was removed in a later version.
I've tried implementing this using the rbs2-corr-id header, and it works in my tests, however it feels like a hack.
Is there a better way to do it? Without modifying the messages?
I've considered using another saga to act as a sort of proxy by correlating on the ID that might be shared and having a list of correlation IDs for the original saga. I worry however that there might be concurrency issues causing the original saga to wait for the proxy saga.
The following code should show the problem:
public class Message
{
public Guid Id { get; set; }
public int OtherId { get; set; }
}
public class Request
{
public int OtherId { get; set; }
}
public class Response
{
public int OtherId { get; set; }
public string MissingInfo { get; set; }
}
public class SagaData : ISagaData
{
public Guid Id { get; set; }
public int Revision { get; set; }
public Guid MessageId { get; set; }
public int OtherId { get; set; }
}
public class MySaga : Saga<SagaData>, IAmInitiatedBy<Message>, IHandleMessages<Response>
{
IBus _bus;
public MySaga(IBus bus)
{
_bus = bus;
}
public async Task Handle(Message message)
{
Data.OtherId = message.OtherId;
// Send Request expecting someone to .Reply(new Response { OtherId = ,... })
await _bus.Send(new Request { OtherId = message.OtherId });
}
public async Task Handle(Response message)
{
// Do something with message.MissingInfo
}
protected override void CorrelateMessages(ICorrelationConfig<SagaData> config)
{
config.Correlate((Message m) => m.Id, s => s.MessageId);
// This works as long as only one saga has this ID
config.Correlate((Response m) => m.OtherId, s => s.OtherId);
}
}

I've tried implementing this using the rbs2-corr-id header, and it works in my tests, however it feels like a hack.
Well... it's a clever hack. 😅 I think this is actually the best you can do: Take advantage of the fact that the correlation ID of the request is under your control, and the reply will carry back the same correlation ID.
How about setting the correlation ID to the ID you'd prefer to see again, when you receive the reply?
And then you correlate your reply with something like
protected override void CorrelateMessages(ICorrelationConfig<InviteToTeamByEmail> config)
{
config.CorrelateHeader<YourReply>(Headers.CorrelationId, d => d.Bla);
}
(assuming that the name of the correlation property was actually Bla... 😆)

Related

Edit HTTP POST method to POST different data depending on existing data in .NET Core

I'm creating an API for a game that can (for now) only be played over Postman issuing POST and GET requests. It needs to be able to receive POST requests and update the InMemory database based on previous requests. Player state is not an issue - it just needs to allow for a player to issue a request and update a value based on the value of the previous request.
I have a Model that contains the class variables and automatically sets one of the variables to a PlayerScore value (0, initially).
public class Game
{
public long Id { get; set; }
public long PlayerScore { get; set; }
public bool Roll { get; set; }
public bool IsComplete { get; set; }
public Game()
{
this.PlayerScore = 0;
}
}
This sets the score to 0 every time the game is played/HTTP POST requests are submitted, but I want to be able to edit this value based on previous turns in the game.
Where should I place this method?
The POST is performed via an 'ActionResult' style method.
Thanks for any help anyone can possibly provide!
You seem to be using your Game class for both your internal game state and also the model from the POST request.
I'd recommend creating a separate class for your POST request so that they don't get confused. e.g.
public class GameUpdateRequest
{
public long Id { get; set; }
public long PlayerScore { get; set; }
public bool Roll { get; set; }
public bool IsComplete { get; set; }
}
// .. controller action
[HttpPost]
public IActionResult UpdateGame([FromBody] GameUpdateRequest request)
{
if (request.PlayerScore > 0)
// update game state logic
}
Then your request model can change and is not tied to the game state model. You could make PlayerScore nullable in the request or add other properties.

MassTransit. Consume equal objects defined in different namespaces

First of all, excuse my English, it's very bad. I am using MassTransit with Azure Service Bus for asynchronous communication between microservices. By their own definition, and to avoid generating dependencies between them, messages sent between different microservices are defined in each of them, that is, they are part of different namespaces. The automatic management of MassTransit causes queues and topics to be managed by the object type, which prevents the microservices that consume a message from receiving the messages sent by the microservice publisher. The same thing happens with two classes with the same properties in the same namespace but with a different class name.
Is there any way to solve this?
The options that have occurred to me are:
Remove the namespace from the endpoint of the destination address, naming it only with the name of the class.
That MassTransit can manage the creation of queues and topics based on the serialization of the object, instead of managing it based on the object type (perhaps through some type of wrapping object?)
I leave an example that I hope can help you in understanding the problem.
//FIRST PROGRAM - MESSAGE CONSUMER
namespace Consumer
{
public class Example
{
public string PropOne { get; set; }
public string PropTwo { get; set; }
}
public class ExampleConsumer :
IConsumer<Example>
{
public List<Example> ConsumedTestObjectList { get; } = new List<Example>();
//THIS METHOD NEVER CALL !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
public Task Consume(ConsumeContext<ExampleConsumer> context)
{
ConsumedTestObjectList.Add(context.Message);
return Task.CompletedTask;
}
}
public class ConsumerProgram
{
public static void Main()
{
var bus = Bus.Factory.CreateUsingAzureServiceBus(sbc =>
{
var host = sbc.Host("connectionString", h => {});
});
sbc.ReceiveEndpoint(host, e =>
{
e.Consumer<ConsumerProgram.Example>(context =>
{
return Console.Out.WriteLineAsync($"Message Received: {JsonConvert.SerializeObject(context.Message)}");
});
});
bus.Start(); // This is important!
Console.WriteLine("Press any key to exit");
Console.ReadKey();
bus.Stop();
}
}
}
//SECOND PROGRAM - MESSAGE PUBLISHER
namespace Publisher
{
public class Example
{
public string PropOne { get; set; }
public string PropTwo { get; set; }
}
public class PublisherProgram
{
public static void Main()
{
var bus = Bus.Factory.CreateUsingAzureServiceBus(sbc =>
{
var host = sbc.Host("connectionString", h => {});
});
bus.Start(); // This is important!
//send new instance of Publisher.Example
var example = new Example() { PropOne = "1", PropTwo = "2" };
bus.Publish(example);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
bus.Stop();
}
}
}
Thank you very much.
regards
Borja
The message type, and the resulting name, are a key concept within MassTransit. If you want to avoid sharing assemblies between projects, that is fine, but you will need to match the entire interface (or class, in your case) name, including namespace, or it will not route properly.
Yes, you can override the entity name formatter to change how topics are named but it won't change the message type requirement for deserialization of the message (which happens, by type).
So the recommendation here is to use the same namespace for the contracts, even if they're in separate projects.

How to specify default property values for owned entity types in Entity Framework Core 2.0?

I have a simple POCO type, say something like
public class OwnedEntity {
public string stringProperty { get; set; }
public decimal decimalProperty { get; set; }
public bool boolProperty { get; set; }
public int intProperty { get; set; }
}
and an actual entity with an OwnedEntity reference
public class SomeEntity {
public string Id { get; set; }
public OwnedEntity OwnedEntity { get; set; }
}
I set up the relationship like described in the documentation using EF Core's Fluent API:
protected override void OnModelCreating (ModelBuilder builder) {
base.OnModelCreating (builder);
builder.Entity<SomeEntity> ().OwnsOne (e => e.OwnedEntity);
}
I can't find anything on how to define default-values for all the properties of OwnedEntity. I tried to initialize the properties like this:
public class OwnedEntity {
public string stringProperty { get; set; } = "initial"
public decimal decimalProperty { get; set; } = -1M;
public bool boolProperty { get; set; } = false;
public int intProperty { get; set; } = -1;
}
but with no effect. Same goes with the [DefaultValueAttribute] (but that was to expect since it's explicitly mentioned).
There's a bit of information on how to handle initial values for regular entities:
modelBuilder.Entity<SomeOtherEntity>()
.Property(e => e.SomeIntProperty)
.HasDefaultValue(3);
But since I'm facing an Owned Entity Type, I can't access the type via Entity<T>.
Is there a way of doing what I'm looking for?
Some things worth mentioning:
I have a solid amount of specific entities where most of them are using the OwnsOne relation
Declaring all OwnedEntity-properties in a base class is not an option since not all the entities have those properties
I`m using EF Core 2.0.3 and ASP.NET Core MVC 2.0.4
Edit:
Originally, I wanted to have newly created SomeEntity instances to come with preset properties for all of the 'embedded' SomeEntity.OwnedEntity properties.
But looking at how my associated controller works, it all makes sense... I have the following methods for the 'Create' operation:
[HttpGet]
public IActionResult Create () {
return View (nameof (Create));
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create (SomeEntity model) {
context.Add (model);
await context.SaveChangesAsync ();
// redirect etc.
}
Which means that no object is created for the [HttGet] overload of Create and all the HTML inputs linked to properties (via asp-for) are initially empty. Okay. So I guess the proper way of doing this is to manually create a new instance of SomeEntity and pass it to the Create view like this:
[HttpGet]
public IActionResult Create () {
return View (nameof (Create), new SomeEntity());
}
Is this the right approach then or are there some more things to keep in mind?
Assuming you understand what EF Core Default Values are for, and just looking for equivalent of Entity<T>().Property(...) equivalent.
The owned entities are always configured for each owner type by using the ReferenceOwnershipBuilder<TEntity,TRelatedEntity> class methods. To access this class you either use the result of OwnsOne method, or use the OwnsOne overload taking second argument of type Action<ReferenceOwnershipBuilder<TEntity,TRelatedEntity>>.
For instance, using the second approach:
builder.Entity<SomeEntity>().OwnsOne(e => e.OwnedEntity, ob =>
{
ob.Property(e => e.stringProperty)
.HasDefaultValue("initial");
ob.Property(e => e.decimalProperty)
.HasDefaultValue(-1M);
// etc.
});

Practical usage of AutoMapper with POST request

In a GET request, I can create a mapping from my back-end model to a customized DTO with AutoMapper with ease. However, I have some concerns when using AutoMapper with POST requests.
Suppose a user orders a product online, he sends the server a POST request with some required data. The fact is, not every piece of data in the back-end model is sent by the user. Let's say the ID of the Order is a GUID which is generated automatically when the entry is inserted into the database; or maybe there are other properties which are auto-incremented. All of these cannot-be-mapped properties lead to a lot of .ForMember(dest => dest.myProperty, opt => opt.Ignore()) chains, and extra handling on the mapped instance after var mappedInstance = Mapper.Map<PostDTO, BackEndModel>(postDTO).
Is AutoMapper not designed for the aforementioned scenario? What is the practice for handling the model-mapping process if the back-end model is much more complex than the DTO?
Update
public class MultipleChoiceQuestion
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid MultipleChoiceQuestionId { get; set; }
[Required]
public string Question { get; set; }
[Required]
public ICollection<PossibleChoice> PossibleChoices { get; set; }
}
public class PossibleChoice
{
[Key, Column(Order = 1), ForeignKey("MultipleChoiceQuestion")]
public Guid MultipleChoiceQuestionId { get; set; }
[Key, Column(Order = 2)]
public int ChoiceIndex { get; set; }
[Required]
public string AnswerText { get; set; }
public MultipleChoiceQuestion MultipleChoiceQuestion { get; set; }
}
The user sends a request to create a new question. Only 2 fields are sent.
{
"Question": "How are you?",
"Answers": [
{ "Text": "I am fine." },
{ "Text": "Feeling bad." }
]
}
Properties that are missing at this stage:
MultipleChoiceQuestionId
Generated after the insertion
ChoiceIndex
Auto-incremented from 1 up to the number of answers available
Without manual mapping, how to handle this situation with AutoMapper?
1- Define your DTOs to be something like this:
public class MultipleChoiceQuestionDto
{
// This property could stay here, because you may need to use the same DTO for update (PUT),
// which means you need the Id to distinguish and validate the DTO data against the URL id
//public Guid MultipleChoiceQuestionId { get; set; }
public string Question { get; set; }
public ICollection<PossibleChoiceDto> PossibleChoices { get; set; }
}
public class PossibleChoiceDto
{
// This can go from this dto, because this DTO is a child dto for its parent.
//public Guid MultipleChoiceQuestionId { get; set; }
// This property could stay here, because you may need to use the same DTO for update (PUT),
// which means you need the Id to know which Choice was updated.
//public int ChoiceIndex { get; set; }
public string AnswerText { get; set; }
}
2- You create a mapping between the entity and the corresponding Dto like this, make sure you call this code from the global.asax file.
Mapper.CreateMap<MultipleChoiceQuestion, MultipleChoiceQuestionDto>();
Mapper.CreateMap<MultipleChoiceQuestionDto, MultipleChoiceQuestion>()
.ForMember(m => m.MultipleChoiceQuestionId, e => e.Ignore()); // you force automapper to ignore this property
Mapper.CreateMap<PossibleChoice, PossibleChoiceDto>();
Mapper.CreateMap<PossibleChoiceDto, PossibleChoice>()
.ForMember(m => m.MultipleChoiceQuestion, e => e.Ignore()) //
.ForMember(m => m.MultipleChoiceQuestionId, e => e.Ignore())
.ForMember(m => m.ChoiceIndex, e => e.Ignore());
3- In your controller.Post you need to map from the DTO to the entity and save the mapped entity to the database.
Now, the above solution will work for you for POST, however, you need to think about the PUT scenario and soon you will realize that you need the Ids to be included in the DTOs, and if you decided to do that then you need to revisit the mapping in point 2 and remove the Ignore code for the properties that you decided to include in the DTO.
Hope that helps.
I'm not sure where in your architecture you're using AutoMapper, but you could conceptually whitelist the properties before doing the automapping. For example, if you're in MVC and you're doing modelbinding, there are techniques (e.g. in the UpdateModel method) to include or exclude a list of properties.

Is there much authentication overhead when WebAPI makes a request to the server?

From what I understand. Every time a webapi request goes to the server then it's authenticated. My application uses WebAPI 2, Identity 2 and has get methods such as this:
/api/cityStatusList
/api/cityTypeList
/api/cityOptionList
These calls exist to get data for a page.
If the webapi is authenticating every request then should I look into how I can combine all these requests into one?
If the webapi is authenticating every request then should I look into how I can combine all these requests into one?
Why, is it causing any trouble?
You can of course define and return a class like this:
public class CityData
{
public List<CityStatus> StatusList { get; set; }
public List<CityType> TypeList { get; set; }
public List<CityOption> OptionList { get; set; }
}
Create CityView model class for your city like following :
public class CityView
{
public List<cityStatusView> cityStatusList { get; set; }
public List<cityTypeView> cityTypeList { get; set; }
public List<cityOptionView> cityOptionList { get; set; }
}
public class cityStatusView
{
public int ID { get; set; }
}
public class cityTypeView
{
public int ID { get; set; }
}
public class cityOptionView
{
public int ID { get; set; }
}
use it like following code in your web api :
// View model
var cityStatusList=..;
var cityTypeList=..;
var cityOptionList=..;
CityView objVM = new CityView();
objVM.cityStatusList = cityStatusList;
objVM.cityTypeList = cityTypeList;
objVM.cityOptionList = cityOptionList;
return ActionContext.Request.CreateResponse(HttpStatusCode.OK, objVM);
To address the question directly - yes, it is authenticating your application every time. However, on the scale of standard web-application this time is don't-you-even-worry-about-it miniscule.
Combining those routes into one might well be a good idea not because authentication has to happen multiple times, but because a web request can simply take a while. Typically this is due to the time it takes to physically send signals from the server to the client over TCP/IP (and re-send to compensate for any dropped packets). Even when parallelizing requests, fewer web requests is faster.
That being said, by default I would do the simplest thing possible and not worry about it. What I just mentioned is an optimization, should be treated as such, and not done prematurely.
As for authentication? It's just a few steps of the super-marathon that is your web request, it really doesn't matter. Someone correct me if I'm wrong, but I don't think it usually even hits the database - all it has to do is decode the claims that are stored in a cryptographically-secure fashion in the authentication cookie.

Resources