0

If I have a service that relies on data obtained through runtime, what is the best way to inject it into a class?

I have an Order class:

class Order {
   string OrderID { get; set; }
   string CustomerName { get; set; }
   ...
}

I want to encapsulate a lot of logic from the database, so I have a service:

class OrderService {
    private readonly IOrderRepository _orderRepository;
    private readonly IOrder _order;

    public OrderService(IOrderRepository orderRepository, IOrder order) {
        _orderRepository = orderRepository;
        _order = order;
    }

    // some methods that compile data from the repository
    public bool CheckAlreadyExists() { ... }
    public string GetLatestShippingStatus() { ... }
    ...
    ...
    public void Create() { ... }
}

Controller logic:

public class OrderController {
    private readonly IOrderRepository _orderRepository

    public OrderController(IOrderRepository orderRepository)
    {
         orderRepository = _orderRepository
    }

    public IActionResult Create(Order order)
        // this is bad because now I have a dependency on IOrderRepository and OrderService
        OrderService orderService = new OrderService(orderRepository, order)

        if (!orderService.CheckAlreadyExists()) {
            orderService.Create();
        }
    end
}

The two options I am aware of:

  1. Refactor the code to pass runtime data into each of the functions instead
  2. Create a factory OrderServiceFactory

I do not want to have to pass the parameter into every method which all rely on the same object. It seems like overkill to create a factory for every time I need this pattern, which seems like it should be a common use-case.

I think I'm fundamentally misunderstanding something.

  • Is there a pattern that I'm unaware of?
  • Could I create a service that keeps track of the runtime data?
  • Or am I just being stubborn and should create a factory?
5
  • If the order service needs an order, coulnd the service not be better embedded in the order? Alternatively you could create a setter for the order in the service. Commented Apr 19, 2022 at 0:53
  • 2
    Reference Dependency Injection Code Smell: Injecting runtime data into components Commented Apr 19, 2022 at 1:26
  • @Stefan Imagine a similar scenario but it’s composed of another runtime class (say, UserData). I thought of using the setter but something feels “wrong” about it. Commented Apr 19, 2022 at 2:12
  • @Nkosi I read this and had trouble wrapping my head around what I would need to do. Would the suggestion here be to create something like “CurrentOrderContext” which I just use to store the runtime data? Commented Apr 19, 2022 at 2:14
  • 2
    @Kevin, in your case, you should "change the public API to expose the runtime data through its contract so that the request-specific information can be passed through." In other words, add an Order argument to the CheckAlreadyExists and Create methods of IOrderService and pass the Order runtime data through to OrderService through those mehtod calls. Commented Apr 19, 2022 at 7:41

1 Answer 1

3

I would simply comment but I don't have the reputation. Long story short, you need to be passing runtime data to OrderService. Read the link provided by Nkosi.

Having OrderService instantiated with a particular Order does not make sense. That means that you have to new up OrderService for every Order you get. Instead, OrderService should have per-lifetime scope. Ie - you can use the same instance of OrderService with multiple Orders. It's not overkill to pass runtime data to every method of a service; it's standard. You're overcomplicating things by forcing your service to rely on an instance of the object it is servicing. And your OrderRepository should not be injected in your controller at all. Use the service to call repository methods.

Sign up to request clarification or add additional context in comments.

2 Comments

I understand to a certain extent. I definitely understand I should not inject my repository into the controller, I only did so in the above example to show that I understood why directly "new"ing the service was not a good idea. What I am trying to do in my case is to store some expensive query data in memory related to that specifc Order, so I can re-use it in other parts of my logic in child services without requerying the database. I'm having trouble figuring out to do this in a pattern that fits in with models, services, repositories, and controllers.
If you need to cache data related to specific orders, maybe you need a dictionary keyed by OrderID. You could make this dictionary a field in OrderService, and before querying, check if the value is already in the dictionary. If this data will never change, you could make the dictionary static, but be cautious of memory consumption.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.