IFindSagas with Raven saga persistence and multiple properties in NServiceBus - nservicebus

I am using Raven to persist sagas and I want to implement IFindSagas, I need to find the saga based on 2 properties, SiteId & EmailAddress so ConfigureMapping won't work. The ISagaPersister interface only lets you look up a single saga by a single property.
I have implemented a saga finder like this
public class MySagaFinder : IFindSagas<MySagaData>.Using<ISomeMessage>
{
public ISagaPersister Persister { get; set; }
public MySagaData FindBy(ISomeMessage message)
{
var lookup = string.Format("{0}__{1}", message.SiteId, message.EmailAddress);
return Persister.Get<MySagaData>("SagaLookup", lookup);
}
}
So basically I've added a property on MySagaData called SagaLookup which is a concatenation of SiteId and EmailAddress. I can then look it up by this. This feels like a hack. Is there any way using the saga persister that I can either get a saga back by multiple properties or get a list of sagas back based on one property that I can then filter by the other property?

IMO it is best to look up by a single "key" property because then you don't need to implement a custom persister. Concatenating the site ID and email address may seem like a hack, but if you think of that as defining the ID of that specific saga then it makes sense. The saga data isn't part of your domain model, it is part of the infrastructure which has specific requirements. However, you should consider whether this definition of the saga ID is unique enough. For example, would it ever be possible for two saga's for the same user in the same site ID to execute at the same time?

Related

How to query Sagas stored in SQL Persistence table

I need to query a property of Saga Data class to get a list. It is stored on SqlPersistance table [Data] column as a serialized object.
Think about a scenario that my SagaData has a property called UserName, so I want to query every saga related to that user.
In a sloppy way, I can query the column content, get the list and can create Saga objects out of the content, by querying like:
SELECT [Id]
,[Correlation_TaskId]
,[Metadata]
,[Data]
,[PersistenceVersion]
,[SagaTypeVersion]
,[Concurrency]
FROM [myWonderfulDb].[dbo].[MyWonderfulPeristanceTable]
where JSON_VALUE(Data,'$.Username') = 'arthur'
but I am looking for an elegant way to do it by possibly using NserviceBus API's.
There is a SagaFinder implementation described in ParticularSoftware documentation (link: https://docs.particular.net/persistence/sql/saga-finder) but this returns only one object which does not perfectly fit into my scenario.
Here how it is implemented in the documentation:
class SqlServerSagaFinder :
IFindSagas<MySagaData>.Using<MyMessage>
{
public Task<MySagaData> FindBy(MyMessage message, SynchronizedStorageSession session, ReadOnlyContextBag context)
{
return session.GetSagaData<MySagaData>(
context: context,
whereClause: "JSON_VALUE(Data,'$.PropertyPathInJson') = #propertyValue",
appendParameters: (builder, append) =>
{
var parameter = builder();
parameter.ParameterName = "propertyValue";
parameter.Value = message.PropertyValue;
append(parameter);
});
}
}
Any ideas appreciated. Thank you!
We have guidance about querying saga state available at
https://docs.particular.net/nservicebus/sagas/#querying-saga-data
In short, you can query the saga data, there is no out of the box way provided by NServiceBus because we recommend using a different approach instead:
the saga to publish events containing the required data and have handlers that process these events and store the data in one or more read model(s) for querying purposes.
The major points why we don't recommend it is in my viewpoint these two
By exposing the data outside of the safeguards of the business logic in the saga the risk is that the data is not treated as
read-only. Eventually, a component tries to bypass the saga and
directly modify the data.
Querying the data might require additional indexes, resources etc. which need to be managed by the component issuing the query.
Those additional resources can influence saga performance.
The only purpose of the SagaFinder is to find a single instance of a certain saga that correlates with the incoming message. I'm not aware of any framework functionalities to query saga (data) instances.

OOP web application class design

I'm trying to build a web application using OOP.
In my application i have Courses and Subscribers.
Each Course can have multiple Subscribers (1-N relation).
Now i need to perform some operations on Courses (check some expire dates and perform actions on it's subcribers, send some emails to admins) and, after performing them, perform other operations on each Subscriber (send emails).
I created a Course class and a Subscriber class.
Course class contain course data like title, dates, current status and a group of Subscriber objects (those who partecipate to it).
Subscriber class contains name, last name, subscription status etc.
I have a problem.
My Course class need to be aware of it's Subscribers.
My Subscriber class need to be aware of the Course it belongs to (to exctract data like title, dates...) and aware of how much subscribers are and their status.
How can i redesign my class structure to solve this?
I was thinking about using some kind of observer pattern...
PS. i'm using PHP
No need for a special design pattern, this is a normal bidirectional association. I get from your description that any subscriber only subscribes to one course, otherwise there should be two classes Student and CourseSubscription instead.
How to simply construct the association in PHP:
class Course
{
/**
* #var Subscriber[]
*/
protected $subscribers = array();
public function addSubscriber(Subscriber $subscriber)
{
$this->subscribers[] = $subscriber;
}
}
class Subscriber
{
/**
* #var Course
*/
protected $course;
public function __construct(Course $course, $name, ...)
{
$this->course = $course;
$course->addSubscriber($this);
$this->name = $name;
...
}
}
A subscriber object can only exist with a course, so you pass the course as parameter to the constructor. There the newly created subscriber registers itself for the course.
it sounds like an observer pattern till the point you said
and aware of how much subscribers are and their status.
You need a version of Observer Pattern which expose limited information of its observers.
This point is something like for each subscriber of a course , you need to get all of the subscribers from the course object so you can create a method in Course object which will give you the limited information for each subscriber of the course.

REST API Status Change

I am designing a REST API.
I have a single resource that I want to be able to change the status of for different conditions e.g. the URI is:
Applications/{application_id}/
The possible status changes are to set the application to:
Cancelled
SignedOff
Hold
Each status change will require different information e.g. a reason for cancelled, a date for signedoff.
What would be a good looking URI to handle this? I had thought of
POST: Applications/{application_id}/Cancel
POST: Applications/{application_id}/SignOff
POST: Applications/{application_id}/Hold
but it doesnt seem right to me.
EDIT:
I should have mentioned that I was already planning
POST: Applications/{application_id}
to update an existing application with a full set of application data.
I would stick with one url for all statuses and have your Status object encapsulate all the different properties. These keeps your url from having words that look like actions and to be more restful.
POST: Applications/{application_id}/status
public class Status
{
public string StatusType {get;set;}
public string CancelReason {get;set;}
public string SignOffDate {get;set;}
...
}
POST: Applications/{application_id}?cancel=true
POST is used only for CREATE. I think put will be better option.

wcf data services - expose properties of the same type in data model

I'm new to WCF data services. I have a quite simple data model. Some of its properties have the same type, like this:
public IQueryable<IntegerSum> HouseholdGoodsSums
{
get
{
return GetData<IntegerSum>(DefaultProgramID, "rHouseholdGoodsPrice", IntegerSumConverter);
}
}
public IQueryable<IntegerSum> StructureSums
{
get
{
return GetData<IntegerSum>(DefaultProgramID, "rStructurePrice", IntegerSumConverter);
}
}
The IntegerSum is a very very simple class:
[DataServiceKey("Amount")]
public class IntegerSum
{
public int Amount { get; set; }
}
When I navigate to my service in a web browser, I see the following error message:
The server encountered an error processing the request. The exception message is 'Property 'HouseholdGoodsSums' and 'StructureSums' are IQueryable of types 'IntegrationServices.PropertyIntegrationServices.IntegerSum' and 'IntegrationServices.PropertyIntegrationServices.IntegerSum' and type 'IntegrationServices.PropertyIntegrationServices.IntegerSum' is an ancestor for type 'IntegrationServices.PropertyIntegrationServices.IntegerSum'. Please make sure that there is only one IQueryable property for each type hierarchy.'.
When I get rid of one of these two properties, the service starts working.
I searched for this error message in google, but haven't found a solution.
Is it really not allowed to have two properties with the same type in a data model? If so, why?
Comrade,
To address the error first, you're running into a limitation in the Reflection provider. Specifically, the Reflection provider doesn't support MEST.
That said, there are better approaches to achieve what you're trying to achieve. You should probably not make IntegerSum an entity type (an entity type is a uniquely identifiable entity, which doesn't really fit your scenario). While you can't expose that directly, you can expose it as a service operation. That seems much closer to what you're trying to achieve.
A couple of ways to distinguish between whether or not something should be an entity:
If it has a key already, such as a PK in a database, it should probably be an entity type
If you need to create/update/delete the object independently, it must be an entity type
HTH,
Mark

Filter contents of lazy-loaded collection with NHibernate

I have a domain model that includes something like this:
public class Customer : EntityBase<Customer>, IAggregateRoot
{
public IList<Comment> Comments { get; set; }
}
public class Comment : EntityBase<Comment>
{
public User CreatedBy { get; set; }
public bool Private { get; set; }
}
I have a service layer through which I retrieve these entities, and among the arguments passed to that service layer is who the requesting user is.
What I'd like to do is be able to construct a DetachedCriteria in the service layer that would limit the Comment items returned for a given customer so the user isn't shown any comments that don't belong to them and are marked private.
I tried doing something like this:
criteria.CreateCriteria("Comments")
.Add(Restrictions.Or(Restrictions.Eq("Private", false),
Restrictions.And(Restrictions.Eq("Private", true),
Restrictions.Eq("CreatedBy.Id", requestingUser.Id))));
But this doesn't flow through to the lazy-loaded comments.
I'd prefer not to use a filter because that would require either interacting with the session (which isn't currently exposed to the service layer) or forcing my repository to know about user context (which seems like too much logic in what should be a dumb layer). The filter is a dirty solution for other reasons, too -- the logic that determines what is visible and what isn't is more detailed than just a private flag.
I don't want to use LINQ in the service layer to filter the collection because doing so would blow the whole lazy loading benefit in a really bad way. Lists of customers where the comments aren't relevant would cause a storm of database calls that would be very slow. I'd rather not use LINQ in my presentation layer (an MVC app) because it seems like the wrong place for it.
Any ideas whether this is possible using the DetachedCriteria? Any other ways to accomplish this?
Having the entity itself expose a different set of values for a collection property based on some external value does not seem correct to me.
This would be better handled, either as a call to your repository service directly, or via the entity itself, by creating a method to do this specifically.
To fit in best with your current model though, I would have the call that you currently make to get the the entities return a viewmodel rather than just the entities;
public class PostForUser
{
public Post Post {get; set;}
public User User {get; set;}
public IList<Comment> Comments}
}
And then in your service method (I am making some guesses here)
public PostForUser GetPost(int postId, User requestingUser){
...
}
You would then create and populate the PostForUser view model in the most efficient way, perhaps by the detached criteria, or by a single query and a DistinctRootEntity Transformer (you can leave the actual comments property to lazy load, as you probably won't use it)