I have a class similar to the below (C#):
public class Product {
public int ID {get;set;}
public string Name {get;set;}
public double Price {get;set;}
public void Save() {
string sql = "INSERT INTO Product.....";
Database.Execute(sql);
}
public void Delete() {
string sql = "DELETE Product WHERE.....";
Database.Execute(sql);
}
}
My main concern is that the code above violates SOLID principles, since it takes responsibility for creating and deleting itself.
Perhaps these Save and Delete methods should be placed somewhere outside the Product entity (Factory/Repository maybe?).
I would introduce your model entity, a command and query pattern and a database layer or repository.
Your model is your Product and this object should be a plain object:
public class Product : IEntity {
public int ID { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
Next I would create a command and query interface for working with this entity:
public interface ICommand {} // Marker interface
public interface IQuery<TResult> {} // Marker interface
Next define the handlers for ICommand and IQuery:
public interface IHandleQuery<TQuery, TResult> where TQuery : IQuery<TResult>
{
TResult Handle(TQuery query);
}
public interface IHandleCommand<TCommand> where TCommand : ICommand
{
void Handle(TCommand command);
}
Now you have a clear indication and separation of your write (command) and read (query) sides.
This means we can create a command and its handler for saving your Product like:
public class SaveProduct : ICommand
{
public string Name { get; private set; }
public double Price { get; private set; }
public SaveProduct(string name, double price)
{
Name = name;
Price = price;
}
}
public class HandleSaveProduct : IHandleCommand<SaveProduct>
{
private readonly IRepository<Product> _productRepository;
public HandleSaveProduct(IRepository<Product> productRepository)
{
_productRepository = productRepository;
}
public void Handle(SaveProduct command)
{
var product = new Product {
Name = command.Name,
Price = command.Price
};
_productRepository.Save(product);
}
}
In the above we have defined an repository for handling this entity,
you can however depend directly on your database context here and do
the queries/commands to it or you can implement the repository pattern
using an GenericRepository<TEntity> : IRepository<TEntity> or just
the separate product repository:
public interface IEntity { } // Marker interface
public interface IRepository<TEntity> where TEntity : IEntity
{
TEntity Get(object primaryKey);
void Save(TEntity entity); // should handle both new and updating entities
void Delete(TEntity entity);
}
public class ProductRepository : IRepository<Product>
{
public Product Get(object primaryKey)
{
// Database method for getting Product
}
public void Save(Product entity)
{
// Database method for saving Product
}
public void Delete(Product entity)
{
// Database method for deleting Product
}
}
You should never return your Product entity to your UI, but use a view model, e.g:
public class ProductViewModel {
public int ID { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public DateTime Whatever { get; set; }
}
public class GetProductById : IQuery<ProductViewModel>
{
public int Id { get; private set; }
public GetProductById(int id)
{
Id = id;
}
}
public class HandleGetProductById : IHandleQuery<GetProductById, ProductViewModel>
{
private readonly IRepository<Product> _productRepository;
public HandleGetProductById(IRepository<Product> productRepository)
{
_productRepository = productRepository;
}
public ProductViewModel Handle(GetProductById query)
{
var product = _productRepository.Get(query.Id);
return product.Select(x => new ProductViewModel {
Name = x.Name,
Price = x.Price;
});
}
}
Please note this is written in notepad, might not compile 100%, but you should get the idea on how the separation of the various components should be in order to follow SOLID. :-)
I believe the Facade pattern will do just fine in your case. The Facade pattern is also known as Service Layer.
In your case you will basically have a service(a class) which will have all the methods you need. Your service should look something like this.
class ProductService
{
public void Save(Product product)
{
// SAVE THE PRODUCT
}
public void Delete(Product product)
{
// DELETE PRODUCT
}
}
You want to inject your class where you want to save or delete products. This way all the work you have to do will be in a separate class and your code will become much cleaner.
It's also a good idea to have all those insert and delete statemenets in stored procedures.
You seem to want some sort of Repository like contract. You've already alluded to it in your [kind of] question. The link is for reference - I'm not suggesting you implement one. Why?
Because like #Igor said, if you're using an ORM then you'll get this contract for free. For example, NHibernate has an ISession with Query<T>(), Save(), Delete(), etc methods. This is all you need.
Nearly every project I've worked on where some abstraction over this "infrastructure" ORM contract is used (service/repository/etc), the said abstraction is weak and only serves to create more code to maintain and a higher risk of tech debt and bugs.
Take the pragmatic approach:
Don't reinvent the wheel by creating a Repository/ORM abstraction of your own with ADO.NET calls inside. Use a solid ORM like Fluent NHibernate which makes mapping simple and interacting with the data easy (other perfectly sound alternatives might be Entity Framework, etc). If that's too much party for you, try something really simple like Dapper - it's a very lightweight ORM which maps to your models like freaking magic and you can still write all your own SQL. You'll get your ORM contract interface to use, which I believe is what you're asking for here, and you can get on with building out your app, rather than pondering about over engineering.
Keep stuff simple by using your ORM contract in your controllers ('controllers' doesn't have to be MVC Controllers, it can be whatever the UI entry point for your app is. Remember: avoid unnecessary abstractions). Here are some simple examples.
Folks want to keep things DRY, but the bizarre addiction developers have to #reusingallthethings means they often have Repositories or Services which wrap a perfectly fine ORM contract call and often only have one or two usages. Forget about reuse! Use the Rule of Three and start by having your queries, save and delete logic in your controllers and only extract reusable code when you know you need to.
I know these examples are
trivial,
but just imagine you needed to return some data and the query
required some long linq expression or complex select with joins. Now imagine you needed this same query in a few places (it happens. Not often, but you will have some) - copy and paste it! Yes that's right; you can't believe I said it but I did. Leave that identical 10 lines copied and pasted in 2, 3 or 4 places in your code. It's totally ok. No one will die. You don't need a query object or a Repository method as long as the linq expression itself (GetTop15TransactionsWithoutFeesExcludingCreditsGroupByDayRecentAtTop() anyone?).
HTH.
Related
I have .net core rest api, which contains hybrid structure in which it only contains repositories and not the service layer.
Now, there is one issue that I am facing with base repository and main structure. let me explain the issue first.
So, consider one entity. let's say Product and below is the definition for that entity. this entity has one base class called FullAuditedEntity.
[Table(name: "Products")]
public class Product : FullAuditedEntity
{
public string Name { get; set; }
}
public class FullAuditedEntity: IFullAuditedEntity
{
public FullAuditedEntity() { }
[Key]
public virtual int Id { get; set; }
}
public interface IFullAuditedEntity
{
int Id { get; set; }
}
The Base repository and it's interfaces are as below.
public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IFullAuditedEntity, new()
{
private readonly ApplicationContext context;
public EntityBaseRepository(ApplicationContext context)
{
this.context = context;
}
public virtual IEnumerable<T> items => context.Set<T>().AsEnumerable().OrderByDescending(m => m.Id);
public virtual T GetSingle(int id) => context.Set<T>().FirstOrDefault(x => x.Id == id);
}
public interface IEntityBaseRepository<T> where T : class, new()
{
IEnumerable<T> items { get; }
T GetSingle(int id);
}
So, my Product repository will be like this.
public interface IProductRepository : IEntityBaseRepository<Product> { }
public class ProductRepository : EntityBaseRepository<Product>, IProductRepository
{
private readonly ApplicationContext context;
public ProductRepository(ApplicationContext context) : base(context: context)
{
this.context = context;
}
}
Now, up-to here everything is good, I can access this repository in controllers and can perform the actions that are listed in base class.
Issue I am facing : So with this structure, If I tries to add any new entity without FullAuditedEntity (see Product entity above, I have base class full audited entity over there), my structure of repository fails and it gives error.
let's say if I tries to add new entity Implementation, and this new entity has a random Id, so I do not want to inherit the FullAuditedEnitity base class. now in this case most of the thing will work fine but when I will try to create repository for Implementation entity, it will give generic error. see below snap of that.
What I tried so far...
I was thinking to create a parallel Base repository which does not inherit FullAuditedEntity as a generic class but I am not sure if it's best practice or not. also my concern is that what if I am doing any mistake in my current structure of repository pattern and Dependency injection?
Any help world be best and really appreciated.
Thank you in advance for your time.
Repositories are usually mapped to database tables. Database table should always have some column which can uniquely identify the row in table and it is common practice to call this column as Id. So you correctly implemented your FullAuditedEntity as there is Id property. However, your Id has always type of int. I suggest you to use the following construction and then your Id would be any type of struct such as int, decimal, Guid, and etc:
/// <summary>
/// Abstraction of the Entity
/// </summary>
public interface IEntity
{
object Id { get; set; }
}
/// <summary>
/// Base class for IDs
/// </summary>
public abstract class Entity<T>: IEntity where T: struct
{
public T Id { get; set; }
object IEntity.Id
{
get { return Id; }
set {
Id = (T)value;
}
}
}
public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IEntity, new()
{
// The code is omitted for the brevity
}
In addition, try to avoid entities without Id like Implementation as in future you will have to figure out how to find rows without Id in your database table.
UPDATE:
If you do not want to inherit FullAuditedEntity, then you can create BaseRepository<T> and then derive it in the EntityBaseRepository.
public abstract class BaseRepository<T> : IEntityBaseRepository<T> where T : class, new()
{
public virtual IEnumerable<T> items => throw new NotImplementedException();
public virtual T GetSingle(int id)
{
throw new NotImplementedException();
}
}
public class EntityBaseRepository<T> : BaseRepository<T> where T : class
, IFullAuditedEntity, new()
{
public override IEnumerable<T> items => base.items;
public override T GetSingle(int id)
{
return base.GetSingle(id);
}
}
and then your Implementation repository:
public interface IImplementationRepository : IEntityBaseRepository<Implementation> { }
public class ImplementationRepository: BaseRepository<Implementation>
, IImplementationRepository
{
public override Implementation GetSingle(int id)
{
return base.GetSingle(id);
}
}
UPDATE 1:
In my view, it is better to use services(Service layer) which consume ITRepository<T>.
Because it gives to you new abilities such as:
Add some calculations on data which got by Repository
Remap your entities which are pulled by the Repository
It is one additional layer of decoupling. So when you edit your service layer, then you don't need to edit Repository layer and then recompile your assembly
I am trying to limit the use of types by chaining the aggregate IAggregate, the aggregate event IDomainEvent, and Identity together with generics, I have snipped the below code to give context of the issue of what I have got so far.
I have the following interfaces:
public abstract class Identity<T>
{
protected abstract string GetIdentity();
}
public interface IAggregate<T>
{
Identity<T> Identity { get; }
}
public interface IDomainEvent<TIdentity,TIdentity>
where T : Identity<TIdentity>
{
TIdentity Id { get; }
}
I implement with the below:
public class TestUserId : Identity<TestUser>
{
public TestUserId(string name) { Name = name; }
readonly public string Name;
protected override string GetIdentity() => Name.ToLowerInvariant();
}
public class TestUser : IAggregate<TestUser>
{
public TestUser(TestUserId id)
{
Id = id;
var ev = new TestUserCreated()
}
public TestUserId Id { get; }
public Identity<TestUser> Identity => Id;
}
public class TestUserCreated : IDomainEvent<TestUserId, TestUser>
{
public TestUserCreated() { }
public TestUserId Id => throw new NotImplementedException();
}
Then in the command handler, for this event to be used (and for me to be able to obtain the TestUserId which should be member of the domainEvent object).
public interface IDomainEventHandler<TEvent>
{
void Handle(TEvent domainEvent, bool isReplay);
}
That gives me the code:
public class TesterHandler : IDomainEventHandler<TestUser, TestUserCreated>
{
public void Handle(TestUserCreated domainEvent, bool isReplay)
{
// can access the ID (of type TestUserId)
var testUserId = domainEvent.Id;
}
}
So the above TesterHandler is fine exactly how i would want - however the compiler is failing on class TestUserCreated : IDomainEvent<TestUserId, TestUser> with The type TestUserId' cannot be used as type parameter 'TIdentity' in the generic type or method 'IDomainEvent<TIdentity, Type>'. There is no implicit reference conversion from 'TestUserId' to 'Identity<TestUser>'.
What I want is to couple (without OO inheritance) the event to the aggregate, so that it is tied to a specific aggregate type (i.e. specific events are tied to a specific entity, and the entity ID is part of the event type as a field), I want to try and make it impossible to code event handlers for unrelated aggregates.
I am trying to implement but the compiler complains of boxing and implicit casting errors (depending on what i try/guess), in short I am unsure how to code the above.
Given I was unable to create running code as per comments requested (hence the reason for the post) and general complexity, I decided using generics in this way was a bad idea with rationale below.
I currently have code which calls the handler as follows (and this is working fine) passing in the sourceIdentity external to the domainEvent object:
public interface IDomainEventHandler<TIdentity, TEvent>
where TIdentity : IIdentity
where TEvent : IDomainEvent
{
void Handle(TIdentity sourceIdentity, TEvent domainEvent, bool isReplay);
}
I am passing in the aggregate ID external to the IDomainEvent object (and this is desired to keep the events, from an event sourcing perspective, as simple as possible as simple POCO objects without inheritance or involving any framework).
The reason for the question was I just wanted to explore all options with generics (so the domainEvent object could have an interface that would give an ID field) but it started to get complicated quickly, specifically additional template parameters would be required since we are inferring relationships via templates, rather than OO relationships.
Without OO, the relationship would need to be defined somewhere by adding additional types to templates to tie them together interface IDomainEvent<TIdentity,TAggregate,TEvent> and interface IDomainEventHandler<TIdentity, TAggregate, TEvent>, in this case OO inheritance would be preferred and result in way less code.
All this was done to give an interface to obtain the ID, however as if an ID is really needed it can be incorporated in the event as a normal field (without the need for complex OO relationships or templates).
public interface IDomainEvent
{
DateTime OccurredOn { get; set; }
Guid MessageId { get; set; }
}
public class TestUserCreated : IDomainEvent
{
// id can be accessed by whatever needs it by being
// defined explicity within the domain event POCO
// without needing any base class or framework.
public readonly TestUserId Id;
public readonly string Name;
public TestUserCreated(TestUserId id, string name)
{
Id = id;
Name = name;
}
}
This example shows the registration and resolving with a simple object hierarchy with SimpleInjector. How this can be solved if I add a generic type (Shipper with a contract) in the hierarchy?
Considerations:
The contract is parsed as a csv file and they can be totally different for UPS and FedEx. Only the UPS and FedEx classes know what to look for in the contracts before shipping. The contracts are read from the csv due to the Business can update the contract anytime and drop it in the folder. The app should not start as a result of changes in the contract.
I want to test UPS and FedEx with its dependencies like notifications etc... (not in the code).
Please don't consider the register and resolve that I have used in the example is wrong. Should consider only the class hierarchy.
The following is the partial code:
public Setup()
{
var container = new SimpleInjector.Container();
// TODO: Registration required for two different shippers
container.Register<UPS, IShipper>();
}
public interface IShipper<T>
{
T contact;
void Ship(Product product);
T ParseContract(string fileName);
void SetContract(T);
}
public class FiveYearContract
{
public string IsSigned { get; set; }
}
public class TenYearContract
{
public string IsPrepaid { get; set; }
}
public interface IUPS<T> : IShipper<T> { }
public interface IFedEx<T> : IShipper<T> { }
public class UPS : IUPS<FiveYearContract> { ... }
public class FedEx : IFedEx<TenYearContract> { ... }
Usage:
public void Ship(String shipper, Product product)
{
// TODO: Retrieve the shipper by name
var shipper = container.GetInstance<IShipper>();
shipper.SetContract(shipper.ParseContract("contract.csv");
// Contract will be checked before sending
shipper.Ship(product);
}
Ship("FedEx", new Product());
To be able to resolve a IShipper<T> where the T is unknown at compile time, you will have to use reflection. For instance:
public Setup()
{
container.Register(typeof(IShipper<>), new[] { typeof(FedEx).Assembly });
}
public void Ship(String shipper, Product product)
{
var contract = shipper.ParseContract("contract.csv");
dynamic shipper = container.GetInstance(
typeof(IShipper<>).MakeGenericType(contract.GetType()));
shipper.SetContract((dynamic)contract);
shipper.Ship(product);
}
The SetContract method however, causes the shipper implementations to become mutable and it causes Temporal Coupling. A better design would typically be to pass the contract runtime value on with the Ship method, as follows:
public void Ship(String shipper, Product product)
{
var contract = shipper.ParseContract("contract.csv");
dynamic shipper = container.GetInstance(
typeof(IShipper<>).MakeGenericType(contract.GetType()));
shipper.Ship((dynamic)contract, product);
}
This allows the IShipper<T> implementations to stay immutable (which can considerably simplify your application) and removes the implicit relationship between the SetContract and Ship methods. It most likely makes those implementations easier to test as well.
I have the following classes in c#:
public class Customer{
public long Id { get; set;}
public String Firstname { get; set;}
public String Lastname { get; set;}
public Customer(long id, String firstname, String lastname){...}
}
public class Book{
public long Id { get; set;}
public String Title { get; set;}
public String Author{ get; set;}
public int NumberOfCopies{ get; set;}
public Book(long id, String title, String author, int numberofcopies){...}
}
My repository is generic and the interface is as follows:
public interface IGenericRepository<T> where T : class, new(){
Add(T entity);
Update(T entity);
Delete(T entity);
GetAll();
}
So to create a repository for a Customer I would create a IGenericRepository<Customer> and for a Book IGenericRepository<Book>. To access these methods from the GUI, I would need a method like: AddCustomer(long id, String firstname, String lastname) and the same for a book, because the GUI doesn't know the entities themselves. I was thinking to create a CustomerService and BookService which hold these methods.
But then I would have to create a new Service for every entity I add.
My question is, how can I make this service generic and still keep the parameters from the constructor of the entity?
To be clear, I would like a generic Service class, which can add both books and customers, but with the same method. So for example:
Add(<constructor parameters of T>)
This method will be called from a Controller class which will have all of the Service. The GUI can then access these service methods through the controller.
Is this possible? If yes, how? If no, is there a better solution to achieve this?
Thanks in advance!
One option is to accept the parameters as desired, e. g.:
public void Add<T>(params object param) { ... }
T is of the type which you want to create. Via reflection you check which constructors are available, chose the one that fits the list of parameters. Then instantiate the object. Voila.
They way I have typically seen it done is that you have the following:
1.
An interface that defines generic Add, Update, Delete, etc:
public interface IGenericRepository<T> where T : class
{
Add(T entity);
Update(T entity);
Delete(T entity);
GetAll();
}
2.
A generic repository that accepts the different types you are going to be persisting to your data store.
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
private connectionInformation;
public GenericRepository<T>(object connectionInformation)
{
// do something with the connection info, dbContext, etc...
}
public T Add(T entity)
{
// implementation...
}
public T Update(T entity)
{
// implementation...
}
public T Delete(T entity)
{
// implementation...
}
public List<T> GetAll()
{
// implementation...
}
}
3.
A unit of work that sets up instances of your generic repositories for the different concrete types.
public class UnitOfWork
{
private object connectionInformation;
public UnitOfWork(object connectionInformation)
{
// set up your connection information
this.connectionInformation = connectionInformation;
this.CustomerRepository = new GenericRepository<Customer>(connectionInformation);
this.BookRepository = new GenericRepository<Book>(connectionInformation);
}
public GenericRepository<Book> BookRepository { get; private set; }
public GenericRepository<Customer> CustomerRepository { get; private set; }
}
4.
A service/services layer that can instantiate a "unit of work" from which you can interact with. It is the service that would be responsible for working with the different properties of each type, but the data saving and retrieving would be handled via the unitOfWork. In your service, you could have a method like this:
public void DeleteFirstBook()
{
var unitOfWork = new UnitOfWork(connnectionInformation);
var books = unitOfWork.BookRepository.GetAll();
if(books.Any())
{
unitOfWork.BookRepository.Delete(books.First());
}
}
You could also do the same for Customers. Having the service layer in place helps your code from being too tightly coupled. The "Data Layer" should only be responsible for interacting with the database (Create, Read, Update, Delete). As you stated, your UI layer should also not know how to create new objects, or interact with the database. So the service becomes important because it knows how to set up data, where to put the data, and how to return it back to the UI.
Hope it helps!
I would change the IRepository<t> method Add(T entity) to Add(object entity). That way you can use the exact same code for any entity.
Pretty standard approach is to pass "creator" delegate to your repository, also I'm not sure if it fits your goals:
public interface IGenericRepository<T> where T : class{
Add(Func<T> creator);
...
}
Repository<T> : IGenericRepository<T> where T : class
{
public Add(Func<T> creator)
{
T newOne = creator();
....
}
}
// usage
bookRepository.Add(() => new Book(42, "some title", ...));
So I'm using interfaces more these days. But I am coming across a brick wall this time.
Just for context only let me show you the RESTful WCF contract here that I designed to show you how I'm using IPaymentRequest:
[ServiceContract]
public interface IPaymentService
{
[WebInvoke(Method = "POST", UriTemplate = "/PreAuthorization")]
PreAuthorizeResponse SendTransaction(PreAuthorizeRequest request);
[WebInvoke(Method = "POST", UriTemplate = "/Capture")]
CaptureResponse SendTransaction(CaptureRequest captureRequest);
... and so on
}
The implementation of the Service Contract for example has some methods that look like this:
public PreAuthorizeResponse SendTransaction(PreAuthorizeRequest request)
{
.....
Processor.SetSettings(request);
}
(Note/disclaimer on clean code principals. I have better names for stuff like the name SetSettings() but for privacy I've named stuff more simple such as "SetSettings" and "Process" for this Stack post. In reality I have what kind of processor in its class name so just FYI).
Second, let me make you aware that I have a Processor class that is basically kinda like a utility class to do some things such as send the request fields to an outside REST API. And in this class one example of another method I'm setting up is a SetSettings method that I'll set some stuff based on the type of request that comes in. Mostly, I'm going to get the stuff I need from its Transaction property.
public class Processor
{
private void SetSettings(IPaymentRequest request)
{
var someValue = request.Transaction.SomeProperty1;
...
}
}
Now here's what the IPaymentRequest looks like:
public interface IPaymentRequest
{
string Token { get; set; }
int ClientID { get; set; }
}
Now here are a couple examples of my domain models (the Models my Service Contract expects to be sent in from client requests) that implement IPaymentRequest:
public class PreAuthorizeRequest : IPaymentRequest
{
public string Token { get; set; }
public int ClientID { get; set; }
public int Amount { get; set; }
public PreAuthTransaction Transaction { get; set; }
}
public class CaptureRequest : IPaymentRequest
{
public string Token { get; set; }
public int ClientID { get; set; }
public int BankID { get; set; }
public CaptureTransaction Transaction { get; set; }
}
I'm using IPaymentRequest throughout my WCF service (it's the type that's expected to be sent into my payment service's method contracts) and using these interfaces elsewhere in my service to make some good reuse of methods that these requests can flow through such as SendRequest(IPaymentRequest request), and so on.
Here is the dilema/problem I have:
In methods where I want to reuse the logic for any kind of request that comes in, I end up having to check for what type it is incoming to my methods sometimes in my processor class. So I am having to create a bunch of messy if statements in order to determine and cast the incoming ITransaction in order to start using it in my utility mehtods here.
So lets continue more so I can explain more about my First method SetSettings()
Notice that I need values from the transaction object in the request and to be able to work with properties in that TYPE of request.
Now lets take a look at the CaptureTransaction object for example for a CaptureRequest
public class CaptureTransaction : ITransaction
{
public string Reference { get; set; }
public decimal Amount { get; set; }
public string CurrencyCode { get; set; }
public CreditCard CustomerCreditCard { get; set; }
}
So as you can see, for each Request Type I have a related concrete Transaction Type that implements ITransaction and holds info that the transaction needs to send over to an external API.
Note: All requests WILL always have a transaction (ITransaction) so I thought it'd be therefore a good idea to maybe throw ITransaction in my IPaymentRequest so something like this:
public interface IPaymentRequest
{
string Token { get; set; }
int ClientID { get; set; }
ITransaction Transaction {get; set; }
}
And here is ITransaction. Every request that comes into our service will require a currency now and in the future so this field was a good candidate/reason to use an Interface:
public interface ITransaction
{
string CurrencyCode { get; set; }
}
So adding that to my IPaymentRequest now requires me to change the Property Name in my Custom Types to "Transaction", for example:
public class CaptureRequest : IPaymentRequest
{
public string Token { get; set; }
public int ClientID { get; set; }
public int BankID { get; set; }
public ITransaction Transaction { get; set; }
}
I thought ok fine.
But now if I try to work with Transactions in my utility method, since it's an Interface variable, it has no idea what type of Transaction it is. So I end up having to cast it before I can use it:
private void SetSettings(IPaymentRequest request)
{
ITransaction transaction;
if (request is CaptureRequest)
transaction = request.Transaction as CaptureTransaction;
if (request is PreAuthorizeRequest)
transaction = request.Transaction as PreAuthorizeTransaction;
... etc.
var someValue = request.Transaction.Some1;
...carry on and use SomeProperty1elsewhere in this method for whatever reason
}
IMO it just feels strongly like huge code smell. So obviously I am not doing something right or I don't yet know something about Interfaces that I should know...that allows me to use them better here or without so much casting. And just too much casting IMO is bad, performance-wise.
Maybe this is a good case to use Generics instead of interface parameters in methods I want to create for reuse across different types of Concrete Request types (Capture, PreAuth, Void yada yada)?
The whole point here is I want to be able to specify interface params in some methods to make them DRY (don't repeat yourself) / reusable...and then use the concrete type that came in via polymorphism and work with the request instance.
If every request has a transaction, then this is the right way to go:
interface IPaymentRequest
{
string Token { get; set; }
int ClientID { get; set; }
ITransaction Transaction { get; set; }
}
Obviously, to process the custom request, you'll need a custom processor:
class Processor
{
protected virtual void OnSetSettings(IPaymentRequest request)
{
}
private void SetSettings(IPaymentRequest request)
{
// do the common stuff
// ...
// set custom settings
OnSetSettings(request);
}
}
class PreAuthorizeRequestProcessor : Processor
{
protected override void OnSetSettings(IPaymentRequest request)
{
base.OnSetSettings(request);
// set custom settings here
var customRequest = (PreAuthorizeRequest)request;
}
}
As you can see, this requires a little type casting. Yo can avoid casting with generics, but this brings a complexity in types declaration:
interface IPaymentRequest<TTransaction>
where TTransaction : ITransaction
{
string Token { get; set; }
int ClientID { get; set; }
TTransaction Transaction { get; set; }
}
class Processor<TRequest, TTransaction>
where TRequest : IPaymentRequest<TTransaction>
where TTransaction : ITransaction
{
protected virtual void OnSetSettings(TRequest request)
{
}
private void SetSettings(TRequest request)
{
// do the common stuff
// ...
// set custom settings
OnSetSettings(request);
}
}
class PreAuthorizeRequestProcessor : Processor<PreAuthorizeRequest, PreAuthTransaction>
{
protected override void OnSetSettings(PreAuthorizeRequest request)
{
base.OnSetSettings(request);
// set custom settings here
}
}
Explanation to my comment (how to use visitor pattern in this case):
interface IPaymentRequest
{
void Process(IPaymentRequestProcessor processor);
}
class CaptureRequest : IPaymentRequest
{
public void Process(IPaymentRequestProcessor processor)
{
processor.Process(this);
}
}
class PreAuthorizeRequest : IPaymentRequest
{
public void Process(IPaymentRequestProcessor processor)
{
processor.Process(this);
}
}
interface IPaymentRequestProcessor
{
void Process(CaptureRequest request);
void Process(PreAuthorizeRequest request);
}
Where:
private void SetSettings(IPaymentRequest request)
{
IPaymentRequestProcessor processor = new PaymentRequestProcessor();
request.Process(processor);
}
The Visitor pattern is one obvious solution - it allows you to step around the fact that C# can't resolve which subtype of ITransaction you're using at runtime in order to choose a method overload by using a trick called double dispatch. The result of the Visitor pattern is to move the type-specific processing code from a conditional (which can miss cases) to a type definition, which the compiler can enforce the completeness of. The cost, however, is code that bounces around through virtual methods in a way that can be a bit complicated to figure out when you're trying to comprehend it from scratch.
Here's how it works.
ITransaction gains a method Accept(ITransactionVisitor visitor). ITransactionVisitor is an interface which has a Visit method with an override for each ITransaction subclass you want to deal with:
interface ITransactionVisitor {
void Visit(PreAuthTransaction t);
void Visit(VoidTransaction t);
// etc.
}
Then of course you need to implement these methods. Accept is easy, but it does need to be implemented for each implementation of ITransaction. Why? Not just because it's an interface method, but because within that method body the compiler will concretely know the type of the transaction at compile time, so it can choose the right overload in ITransactionVisitor.
public void Accept(ITransactionVisitor visitor) {
visitor.Visit(this);
}
Then all you need to do is implement an appropriate ITransactionVisitor. One of the advantages of this pattern is that you can implement as many as you like with completely different behaviours, and the ITransaction needs no further knowledge or modification (this is why the visitor is specified with an interface or an abstract class).
public class TransactionProcessorVisitor : ITransactionVisitor {
public TransactionProcessorVisitor(/* some suitable context in the constructor so it can do its job perhaps */) { ... }
public void Visit(PreAuthTransaction t) {
// do stuff
}
public void Visit(VoidTransaction t) {
// do other stuff
}
}
So yes, the visitor classes have to know about all the types of transaction, but
It's not in a giant if statement
It's not part of the ITransaction implementations
It's compile-time checked - if you add a new ITransaction type and try to feed it through the processor, the compiler will be able to figure out that there's no Visit method for it and throw an error, rather than waiting until runtime which is the best you can do with the if version.
This is not necessarily the best answer, but it's an answer.
First, let me tell you that your SetSettings method is wrong. var doesn't work like that. From there, your entire thread of reasoning is wrong. Add to it fact you are using some kind of "utility methods" and you have recipe for very bad architecture.
First, I would change those utility methods into some kind of full-featured classes with some interface. I'm sure you could create IProcessor interface and have PreAuthorizeProcessor and CaptureProcessor. From there, you either have IProcessor GetProcessor() method on your IPaymentRequest, which then forces each request into being able to have it's own processor. Or you could use a factory to create specific processor for given request via IProcessor CreateProcessor(IPaymentRequest). Here, you could either hard-code the preocessors or use some kind of subscribe mechanism.
Also, using type checking and casting is not wrong as long as it is properly encapsulated, like inside a factory. And using a visitor pattern is not much different from doing a manual type checking. You still get same kind of advantages and disadvantages from both.