MassTransit Sub-Class when published is not consumed - masstransit

I am trying to create Generic Implementation to publish messages with MassTransit.
BasePublisher
public abstract class BasePublisher
{
private readonly IPublishEndpoint publishEndpoint;
public BasePublisher(IPublishEndpoint publishEndpoint)
{
this.publishEndpoint = publishEndpoint;
}
public Task Publish(IntegrationBaseEvent message)
{
return publishEndpoint.Publish(message);
}
}
IntegrationBaseEvent
public class IntegrationBaseEvent
{
public IntegrationBaseEvent(Guid id, string name, DateTime createdDate)
{
Id = id;
Name = name;
CreationDate = createdDate;
}
public IntegrationBaseEvent()
{
Id = Guid.NewGuid();
CreationDate = DateTime.UtcNow;
}
public Guid Id { get; private set; }
public string Name { get; set; }
public DateTime CreationDate { get; private set; }
}
And I've created events using the abstraction IntegrationBaseEvent.
public class BusinessCreatedEvent : IntegrationBaseEvent
{
public Guid BusinessId { get; set; }
public string BusinessName { get; set; }
}
With the interface below, I am trying to publish the message but it not at all consumed
public interface IPublisher
{
Task Publish(IntegrationBaseEvent message);
}
It is consumed only when I create a separate publisher for the inherited event BusinessCreatedEvent something like below
public interface ISubscriptionPublisher
{
Task Publish(BusinessCreatedEvent message);
}
I don't want to create a different publisher and just want to use the abstract publisher like below.
public class BusinessCreatedEventHandler
{
private readonly IPublisher _publisher;
public BusinessCreatedEventHandler(IPublisher publisher)
{
_publisher = publisher;
}
public Task Handle(string id, string name)
{
var message = new BusinessCreatedEvent
{
BusinessId = id,
BusinessName = name
};
_publisher.Publish(message);
return Task.CompletedTask;
}
}
Any ideas to make it generic, please?

MassTransit leverages generics extensively, and limits types to the generic type specified. Since you're specifying the base interface as the generic type in the call to Publish, that base interface type is all that is published.
Your question is similar to the this one, and my answer – and the solution is the same. Convert it to object and use the Publish(object message) overload instead. That way, MassTransit will use the object type (MassTransit will call message.GetType() and properly dispatch the message type.
Or, you could make your interface include a generic method as well:
public interface IPublisher
{
Task Publish<T>(T message)
where T : class, IntegrationBaseEvent;
}

Related

registering serivices in startup class in asp.net core with generic interface dynamically

public interface IBLLRepo<T>
{
T Create(T app);
long Find(long id);
T Update(T app);
}
public class CustomTDRUser : IBLLRepo<CustomTDRUser>
{
public long UserId { get; set; }
public long? ApplicationId { get; set; }
[Required]
public string UserName { get; set; }
public long? DeptId { get; set; }
[Required]
public long? DesgnId { get; set; }
public long Find(long id)
{
throw new NotImplementedException();
}
public CustomTDRUser Create(CustomTDRUser app)
{
throw new NotImplementedException();
}
public CustomTDRUser Update(CustomTDRUser app)
{
throw new NotImplementedException();
}
}
services.AddTransient(typeof(IBLLRepo<CustomTDRUser>), typeof(CustomTDRUser));
i dont want to add each class in startup like this i want register these class dynamically which inherits genric interface
i tried many solution its throwing error
: 'Open generic service type 'TestingCore.Repositories.IBLLRepo`1[T]' requires registering an open generic implementation type. (Parameter 'descriptors)
if i use this code given below linkgithubgenericinterfacelink
You could use Scrutor for this. It’s a package that allows you to register services with the DI container based on conventions that you can define yourself.
In your case, you have a single interface IBLLRepo<T> and want to register the implementation for any T.
services.Scan(scan => scan
// look in the assembly which contains the Startup class
.FromAssemblyOf<Startup>()
// Find all classes that are an IBLLRepo
.AddClasses(classes => classes.AssignableTo(typeof(IBLLRepo<>)))
// register them as the interface they implement
.AsImplementedInterface()
// with a transient lifetime
.WithTransientLifetime()
);
So if you had the following classes:
public class CustomTDRUser : IBLLRepo<CustomTDRUser> { … }
public class CustomTDRCustomer : IBLLRepo<CustomTDRCustomer> { … }
public class CustomTDRProduct : IBLLRepo<CustomTDRProduct> { … }
then this would result in the following registrations:
services.AddTransient<IBLLRepo<CustomTDRUser>, CustomTDRUser>();
services.AddTransient<IBLLRepo<CustomTDRCustomer>, CustomTDRCustomer>();
services.AddTransient<IBLLRepo<CustomTDRProduct>, CustomTDRProduct>();

Can I put a method inside class that is used as entity in Entity Framework?

I have POCO class that is used by Entity Framework. I'm using one field that is not mapped with database because I wanted it to be calculated when I access some data row.
Can I move data processing to some method in that class and expect that Entity Framework will work fine?
public class SomeClass
{
public int Id { get; set; }
public string Variable { get; set; }
[NotMapped]
public string VariableProcessed
{
get
{
return Variable.DoSomethingBlaBla();
}
set {}
}
}
I want to rewrite it like this:
public class SomeClass
{
public int Id { get; set; }
public string Variable { get; set; }
[NotMapped]
public string VariableInverted
{
get
{
return ProcessVariable(Variable);
}
set {}
}
private int ProcessVariable(string variable)
{
return variable.DoSomethingBlaBla();
}
}
Yes you can. But you don't need to pass a parameter to that function as long as it works on a variable in your POCO class

Mapping a foreign key ID in DTO to an Object using AutoMapper and AutoFac IOC

Im my Web API project, am trying to map the id in DTO call to the Object. But I am facing issues in setting up dependecy using AutoFac.
Below is the classes I am using.
public abstract class DomainObject<IdT> : IDomainObject<IdT>
{
public virtual IdT Id { get; set; }
}
public class Course : DomainObject<long>
{
public string CourseName { get; set; }
public Teacher Teacher { get; set; }
}
public class Teacher : DomainObject<long>
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
I want to map CourseDTO to Course , the TeacherID has to be mapped to Teacher object
public class CourseDTO
{
public long Id{ get; set; }
public string CourseName { get; set; }
public long TeacherID { get; set; }
}
Below is the entity converter.
public class EntityConverter<T,IdT> : ITypeConverter<IdT, T>
{
private readonly IGenericRepository<T, IdT> _repository;
public EntityConverter(IGenericRepository<T, IdT> repository)
{
_repository = repository;
}
public T Convert(ResolutionContext context)
{
return _repository.GetById((IdT)context.SourceValue);
}
}
How can I define the mapping and inject the dependency using AutoFac.
It's not clear from your question what you are having trouble with. If you're just trying to map the TeacherId in your DTO, then there is no work to do at all:
Mapper.CreateMap<Course, CourseDTO>();
TeacherId will be mapped automatically from Teacher.Id.
If you're wanting to map a TeacherDTO by looking it up from the repository, then you can use the approach in the linked question. Registration will be very similar to the Windsor approach, and setting up the container for resolution is almost identical too:
ContainerBuilder builder = new ContainerBuilder();
var container = builder.Build();
Mapper.Initialize(cfg => cfg.ConstructServicesUsing(container.Resolve));
If you're trying to do more complicated things than this, then you'll need to post the actual DTOs you're trying to map to.

Event Sourcing Event typed against an Aggregate

enter code here I'm looking to enforce the specific type of aggregate, an event can be used with.
I have a basic IEventHandler interface :-
public interface IEventHandler<TEvent> where TEvent : IEvent
{
void Handle(TEvent #event);
}
The Event Base class inherits from this :-
public class Event : IEvent
{
#region Constructors
public Event(Guid aggregateRootId)
{
Timestamp = DateTime.Now;
Id = Guid.NewGuid();
AggregateRootId = aggregateRootId;
}
#endregion
#region Implementation of IEvent<Guid,Guid>
public Guid Id { get; private set; }
public Guid AggregateRootId { get; private set; }
public int Version { get; set; }
public DateTime Timestamp { get; private set; }
#endregion
}
Now I need to make my Aggregate or Class specific Event :-
public class ClientNameChangedEvent : Event
{
#region Constructors
public ClientNameChangedEvent(Guid aggregateRootId, string name, int version) : base(aggregateRootId)
{
Name = name;
Version = version;
}
#endregion
#region Properties
public string Name { get; private set; }
#endregion
}
As the name suggests, I only want to use this against the Client aggregate. I want a way to prevent the programmer registering the event, from associating it against another Aggregate, In the example below, Ive registered an Interest in the client Events against a Case Aggregate.
public class Case : AggregateRoot, IEventHandler<ClientNameChangedEvent>
{
#region Properties
public string Name { get; private set; }
#endregion
public Case()
{
}
public Case(Guid id, string name) : base(id)
{
}
public void ChangeName(string name)
{
if (!Name.ToUpper().Equals(name.ToUpper()))
{
ApplyChange(new ClientNameChangedEvent(Id, name, Version));
}
}
public void Handle(ClientNameChangedEvent #event)
{
Name = #event.Name;
Version = #event.Version + 1;
}
public void Handle(ClientCreatedEvent #event)
{
Id = #event.AggregateRootId;
Name = #event.Name;
Version = #event.Version;
}
}
}
How can I prevent this?
Events are tied to a particular aggregate by the aggregateId. When you prepare an aggregate you load it's previous events via the id. You then apply all the past events associated to that id (assuming no snapshot) to the aggregate.
The subscription to events is not on the domain side but on read side. Multiple denormalisers can 'handle' any one event.
For a little more in depth overview of the flow take a look at my post: CQRS Step-By-Step Guide to the Flow of a Typical Application.
Good luck - I hope it helps

FindBy method Entity framework

I have this account Model:
public class Account :IAggregateRoot
{
public Account()
{
}
public Account(Guid accountId)
{
Id = accountId;
}
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
}
}
and this repository class :
public class Repository<T> : IRepository<T> where T : class, IAggregateRoot
{
private readonly DbSet<T> _entitySet;
public T FindBy(T entity)
{
return _entitySet.Find(entity);
}
}
and now when I want to get an entity by Id , for example :
public AccountViewModel GetAccountBy(Guid accountId)
{
var account = new Account(accountId);
_unitOfWork.AccountRepository.FindBy(account);
var accountView = account.ConvertToAccountView();
return accountView;
}
I got this Error :
The specified parameter type is not valid. Only scalar types, such as System.Int32, System.Decimal, System.DateTime, and System.Guid, are supported.
my action to call GetAccountBy is like this:
public ActionResult Edit(Guid accountId)
{
var account = _accountService.GetAccountBy(accountId);
return View(account);
}
what is problem with this ? Any help is much appreciated.
You're not calling the DBSet.Find() method correctly.
As the documentation states you need to pass
The values of the primary key for the entity to be found
you don't pass in an instance of the entity, you pass in the key values that identify an entity. From your example you don't need to create a new instance of account:
var account = new Account(accountId);
_unitOfWork.AccountRepository.FindBy(account);
you just need to pass the accountId to FindBy()
_unitOfWork.AccountRepository.FindBy(accountId);
Here's your code amended:
public class Repository<T> : IRepository<T> where T : class, IAggregateRoot
{
private readonly DbSet<T> _entitySet;
public T FindBy(params Object[] keyValues)
{
return _entitySet.Find(keyValues)
}
}
public AccountViewModel GetAccountBy(Guid accountId)
{
_unitOfWork.AccountRepository.FindBy(accountId);
var accountView = account.ConvertToAccountView();
return accountView;
}
You can call the DbSet.Find(params object[] keyValues) Method only with System.Int32 and System.Guid as the error message indicates. (well and System.Decimal, System.DateTime probably for composite keys)
The method will not look for a Id or PK in your Model and use it automatically (when you pass Account, the method will not use Account.Id) - as its using "the primary key value" http://msdn.microsoft.com/en-us/library/gg696418(v=vs.103).aspx
Consider passing a predicate as suggested in FindBy Id method in EntityFramework
If you Models always have an Id of Type Guid, then maybe you can pass the Id directly:
public T FindBy(T entity)
{
return _entitySet.Find(entity.Id);
}
Hope this helps.

Resources