0

I have a class model using GRASP pattern design, specifically Controller pattern, so ProductController handles all the Product instances, and this handle all the Kardex instances.

ProductController can save, edit and delete products.
Product can save and delete kardex movements.

enter image description here

Only using OOP I can handle this, but I need that some methods calls an API to save and/or get data from a DB.

My app will be constantly doing operations with this API, so I decide to use IHttpClientFactory with DI.

If i inject controllers with their respectives interfaces is not a problem, but if a Product instance have to SaveKardex, i will have to inject it to use HttpClientFactory, preventing instantiate. So i did this:

public class Product
{
  private readonly IHttpClientFactory _httpClientFactory;
  public int prop1 { get; set; }
  
  public Product(IHttpClientFactory httpClientFactory) //this works
  {
     _httpClientFactory = httpClientFactory;
  }
  
  public Product() //this also works
  {
     prop1 = 10;
  }

  public async Task<bool> SaveKardex(Kardex kardex)
  {
     var client = _httpClientFactory.CreateClient("BdClient");
     var uriString = client.BaseAddress + $"product/kardex";
     var message = RequestBuilder.RequestMessage(HttpMethod.Post, uriString);
     var res = await client.SendAsync(message);
     ....
     ....
     return true;
  }
}

If I instanciate a new Product or is deserialized, i could not call SaveKardex method because i will not use the injected instance and httpClientFactory would be null.

What can i do? Any suggestions?

I thought using HttpClient instances, but is this the best way considering a lot of connections to the API?

Thanks!

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
  • 5
    Don't try to make your models DI objects. Separate the two from one another. Have a `ProductModel` that is more-or-less a POCO and have DI services consume them. – Andy May 19 '21 at 03:28
  • 4
    Your model suffers from tight coupling and low cohesion. – Nkosi May 19 '21 at 03:34

1 Answers1

0

I solved this redesigning my model as follow: (This was suffering from tight coupling and low cohesion as @Nkosi said).

An EntityLayer was created with all properties that would be send to BD and vice versa.

An Interface was created where operations with BD are declared.

A "Service class" was created wich implement the interface. This class is inyected on .net core and not other one.

Finally, all instances of Product receives this Interface (wich is implemented by ProductService), so this way i can call "bd operations".

public class EntityProduct
{
  public int prop1 {get; set; }
}

public interface IProductOperations
{
  Task<bool> SaveKardex(int prop);
}

public class ProductService : IProductOperations //only this class is inyected
{
  private IHttpClientFactory httpClientFactory;
  public ProductService(IHttpClientFactory httpClientFactory)
  {
     this.httpClientFactory = httpClientFactory;
  }

  public async Task<bool> SaveKardex(int prop)
  {
     var client = _httpClientFactory.CreateClient("BdClient");
     var uriString = client.BaseAddress + $"product/kardex/{prop}";
     var message = RequestBuilder.RequestMessage(HttpMethod.Post, uriString);
     var res = await client.SendAsync(message);
     ....
     ....
     return true;
  }
}

public class Product
{
  private IProductOperations _productOperations;
  public EntityProduct Entity { get; set; }
  
  public Product(IProductOperations productOperations)
  {
     _productOperations = productOperations;
  }
  
  public async Task<bool> SaveKardex(int prop)
  {
     var result = await _productOperations.SaveKardex(prop);
     ....
     ....
     return true;
  }
}

I could do this thanks to this answer by @robert-paulen

  • I think you have modelled the relationship in a wrong way. Why should a `Product` depend on the `ProductOperations`? `ProductService` should work with `Product`s. – Peter Csala May 26 '21 at 11:47
  • Is the only way that i found. How to call SaveKardex from Product? ProductService implements that Interface, and Product use that implementation to call to the API. Any suggestions are welcome – Gonzalo Bustamante May 27 '21 at 04:18
  • If I can assume that the `Product.SaveKardex` is just a wrapper around `ProductService.SaveKardex` then all you need to do is: Get rid of the `Product` class entirely and pass the `EntityProduct` to the `SaveKardex`: `Task SaveKardex(EntityProduct product);`. Therefore the `ProductContoller` should depend on the `ProductService`. – Peter Csala May 27 '21 at 07:17