Implementing async/await pattern for manually generated WCF (client-side) proxies - c#

Given this interface
[ServiceContract]
public interface IProductService
{
[OperationContract]
Product Get(int id);
}
I would like to manually (that is, without using scvutil or Add Service Reference in VS) create a client-side proxy.
I do it in the following way
public class ProductService: IProductService
{
readonly ChannelFactory<IProductService> factory;
public ProductService()
{
factory = new ChannelFactory<IProductService>("*");
}
public Product Get(int id)
{
var channel = factory.CreateChannel();
return channel.Get(id);
}
}
My problem is that I also want async/await version of this method, only on client-side, server side is still synchronous.
I want this to be a generic solution because I have many methods and services of this sort.

If you're using ChannelFactory to allow for async-await your interface needs to return a Task or Task<T>.
It will force your server side to also return a task but you can do that synchronously with Task.CompletedTask and Task.FromResult if you insist on keeping it synchronous (though why would you if you have the option).
For example:
[ServiceContract]
interface IProductService
{
[OperationContract]
Task<Product> GetAsync(int id);
}
class ProductService : IProductService
{
ChannelFactory<IProductService> factory;
public ProductService()
{
factory = new ChannelFactory<IProductService>("*");
}
public Task<Product> GetAsync(int id)
{
var channel = factory.CreateChannel();
return channel.GetAsync(id);
}
}
class ProductAPI : IProductService
{
public Task<Product> GetAsync(int id) => Task.FromResult(Get(id))
}

You can actually do that without changing the service itself. You can simply define a second interface which contains async and Task returning versions of the methods and is marked with [ServiceContract(Name = "NameOfTheIterfaceWhichIsActuallyExposedOnTheServer")]
In the example you mentioned you would define a second interface with GetAsync() operation:
[ServiceContract(Name = "IProductService")]
public interface IProductServiceAsync
{
[OperationContract]
Task<Product> GetAsync(int id);
}
and even though your service still implements and exposes IProductService you can use ChannelFactory<IProductServiceAsync> to call into it. As long as the method names match the GetFoo/GetFooAsync pattern everything will just work. That's how Add Service Reference in Visual Studio can generate you an async service reference to a synchronous service.
See Calling a synchronous WCF method asynchronously using ChannelFactory for a more detailed explanation on how this work.

Related

Transaction management in dependent services

I am interested in the architectural solution of the following moment.
I have:
public class GenericRepository<T> : IDisposable {
public GenericRepository(ISession session){
_session = session;
};
public T InsertAsync(T entity){...};
public IQueryable<T> Read(){...};
public T UpateAsync(T entity){...};
public void DeleteAsync(T entity){...};
public Task Commit(){
return _session.Transaction.Commit();
};
public void Dispose(){
if(_session.Transaction.IsActive){
_session.Transaction.Rollback();
}
};
}
public class UserService{
public UserService(GenericRepository<User> repository){...}
public long CreateUser(string userName){
...
_repository.Commit(); // [1]
};
}
public class OrganizationService{
public OrganizationService(GenericRepository<Organization> repository){...}
public int CreateOrganization(string code){
...
_repository.Commit(); // [2]
};
}
The following registration is used:
services.AddScoped<ISession>(x => x.GetRequiredService<NHSessionProvider>().OpenSession());
services.AddScoped(typeof(GenericRepository<>));
services.AddScoped<UserService>();
services.AddScoped<OrganizationService>();
These CreateOrganization and CreateUser can be used independently in any parts of the code:
public IActionResult Post([FromServices] OrganizationService service, [FromBody] string code){
service.CreateOrganization(code);
return Ok();
}
public IActionResult Post([FromServices] UserService service, [FromBody] string userName){
service.CreateUser(userName);
return Ok();
}
However, now I have a new service:
public class MyBillingService{
public MyBillingService(GenericRepository<Contractor> repository, OrganizationService organizationService, UserService userService){...}
public int CreateNewContractor(string organizationCode, string userName){
...
_organizationService.CreateOrganization(organizationCode);
...
_userService.CreateUser(userName);// [3]
...
_repository.Commit(); // [4]
}
}
In this implementation, CreateOrganization and CreateUser have their own transactions, and if [3] throws an exception, then the organization will be created anyway.
Ok, because ISession is registered as Scoped, then I can delete _repository.Commit from CreateOrganization and CreateUser([1] and [2]). In this case, [4] will be responsible for committing all changes.
But what then to do when OrganizationService and UserService are used independently? After all, now they have become non-independent services and cannot save data without delegating the commit of changes to some other service:
public IActionResult Post([FromServices] UserService service, [FromServices] TransactionService transaction, [FromBody] string userName){
service.CreateUser(userName);
transaction.Commit();
return Ok();
}
As far as this decision is a good one?
Transactions requires a unit of work. There is no other way to coordinate repositories. The reason you're facing issues here is that your entire design is wrong.
First and foremost, you should not have these repositories at all. You're using EF Core, which is an ORM, and already implements the repository and unit of work patterns. Using an ORM is opting to use a third-party library for your DAL. Wrapping your own DAL layer around that is pointless and imposes needless maintenance and testing costs on your application with zero benefit. Your services should depend on your context directly.
Then, services should be self-contained units of functionality. If they depend on other services, you're doing it wrong. The service should correspond with a particular subdomain of your application. If users and organization need to be managed together transactionally, then you should have one service that encompasses both.
Alternatively, if you want/need to keep the two separate, then you would need to incorporate the concept of sagas.
So I've started to move more towards what Chris mentioned in his answer and use the ISession directly, but I have used a generic repository in the past. Your repos can't correctly handle transactions that are already started.
So my generic repo has a couple of methods
protected virtual TResult Transact<TResult>(Func<TResult> func)
{
if (_session.Transaction.IsActive)
return func.Invoke();
TResult result;
using (var tx = _session.BeginTransaction(IsolationLevel.ReadCommitted))
{
result = func.Invoke();
tx.Commit();
}
return result;
}
protected virtual void Transact(System.Action action)
{
Transact(() =>
{
action.Invoke();
return false;
});
}
Then the methods that are implementing the repo functionality look like this
public bool Remove(T item)
{
Transact(() => _session.Delete(item));
return true;
}
This allows the method to use an existing Transaction if it is already started, otherwise create your transaction for this work.
You also should not have a Dispose in your repo since you don't own the reference to ISession. It's life cycle should be handled by whoever created that instance.
The generic repository also shouldn't have commit functionality except when it is explicitly starting a new transaction. So now you need to have something that handles starting and committing said transaction. In a web scenario you are typically in a session per request scenario. This would mean you are creating your session in BeginRequest and disposing of it in EndRequest. I then use a transaction attribute to manage creating transactions prior to executing the controller action and commit/rollback after the execution of the controller method.

BaseController for HttpClient Instance

I have some API client to make request. Those are described in startup.
Simply, is it make sense to create HttpClient via base class and calling common request methods from base. Or each controller should create own client ? Is there will be a problem ?
Base
public class BaseController : ControllerBase
{
public HttpClient client;
public BaseController(IHttpClientFactory factory, string clientName)
{
client = factory.CreateClient(clientName);
}
public async Task<IActionResult> Get(string query)
{
}
}
Foo
public class FooController : BaseController
{
public FooController(IHttpClientFactory factory) : base(factory, "fooclient")
{
}
[HttpGet]
public async Task<IActionResult> Get(int id)
{
return await Get($"Foo/Get/{id}");
}
}
There's nothing technically wrong with this approach, but it's preferable to use typed clients. The way that is done is by creating a "service" class which will own the client:
public class FooService
{
private readonly HttpClient _httpClient;
public FooService(HttpClient httpClient)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
...
}
Then, you register this in ConfigureServices:
services.AddHttpClient<FooService>(c =>
{
// configure your HttpClient
});
Finally, you inject this service class into your controller:
public class FooController : ControllerBase
{
private readonly FooService _fooService;
public FooController(FooService fooService)
{
_fooService = fooService ?? throw new ArgumentNullException(nameof(fooService));
}
...
}
This then serves to encapsulate your HttpClient logic. You simply add methods to the service to do things the controller needs and the service makes the actual HttpClient requests to do that. That makes it infinitely easier to change things if the API you're utilizing should change. You just change the service and you're good to go, instead of having to track down every place you used HttpClient to interact with this API, which is a much more difficult task.
Additionally, having the client be typed gives you the ability to configure it once for all, as well as add things like retry and exception handling policies in one place. Since the client is injected for a particular type (i.e. FooService) there's no magic strings for the client name that you could fat finger or otherwise mess up.

Call async method in AddTransient in Startup - Asp.Net Core

I have a service which is used to get some information and the method has a bunch of async calls in the chain.
public interface IFooService
{
Task<IFoo> GetFooAsync();
}
The concrete class,
public class FooService : IFooService
{
public async Task<IFoo> GetFooAsync()
{
// whole bunch of awaits on async calls and return IFoo at last
}
}
I register this service on StartUp,
services.AddTransient<IFooService, FooService>();
Several other services are injected with this service. One among them,
public class BarService : IBarService
{
private readonly IFooService _fooService;
public BarService(IFooService fooService)
{
_fooService = fooService;
}
public async Task<IBar> GetBarAsync()
{
var foo = await _fooService.GetFooAsync();
// additional calls & processing
var bar = SomeOtherMethod(foo);
return bar;
}
}
IFoo is integral to the application and used across several services. Most of my code is async just due to this one IFooService and the one method it has which returns IFoo.
Considering this use case, I would like to be able to just inject IFoo to all other services as opposed to injecting them with IFooService.
I gave this a shot,
services.AddTransient<IFoo>(provider =>
{
var fooService = provider.GetService<IFooService>();
var foo = fooService.GetFooAsync().GetAwaiter().GetResult();
return foo;
});
but it raises a red flag to me as I'm doing sync over async and I'm unsure if this will cause any issues like race conditions. Would startup be blocked by doing this. Im looking for a clean way to handle this, any recommendation for when we need something like this? Thank you for your help.
I guess what you want is an async constructor, which is not recommended.
here is more info about it and contains a solution using Lazy<T>.
but it raises a red flag to me as I'm doing sync over async
For this red flag, it is caused by that you call method GetFoo which is not defined in IFooService, it is not related with async or sync method.
Try the method which is defined
services.AddTransient<IFoo>(provider =>
{
var fooService = provider.GetService<IFooService>();
var foo = fooService.GetFooAsync().Result;
return foo;
});
2 Other options
Inject Func<Task> as the transient, the caller can await !
Task.Run( async () await abc).Wait(); // this deadlocks much less
due to creating a task and having less issues .

How to call new methods from a class which implements interface

How to call a new method which is implemented in the concrete class of an interface
I am using structure map IOC container.
public interface ICompanyRepository
{
IEnumerable<Company> GetAll();
Company Get(int id);
Company Add(Company item);
bool Update(Company item);
bool Delete(int id);
}
public class CompanyRepository: ICompanyRepository
{
// Provide implementation for all interface methods
//Class CompanyRepository will also have the new method called DisplayLog
public void DisplayLog()
{
//To do
}
}
I am trying to implement DI using structure map in my Customer controller class
how can I tell the that I need methods ofcompany2 to be called?
_.Scan(x =>
{
x.TheCallingAssembly();
x.AddAllTypesOf<ICompanyRepository>();
// or
});
My code:
private readonly ICustomerRepository customerRepository;
public CustomerController(ICustomerRepository CustomerRepository)
{
customerRepository = CustomerRepository;
}
// GET: Customer
public ActionResult Index()
{
var customers = customerRepository.DisplayLog()
//Here i need to call CompanyRepository class methods DisplayLog() how can i call it here ?
// method DisplayLog() is not be shown here
return View(customers);
}
On an Interface, you can only call what is defined in the Interface - Its a definition of a "common base" of all classes that implement it. Ask yourself: What should happen if the ICompanyRepository you get is of a type that does NOT implement DisplayLog?
That means: It is not possible to call anything else than the interface methods right away.
To call DisplayLog on customerRepository there are 3 ways:
Add DisplayLog() to the Interface
Cast the customerRepository to CompanyRepository. but this will cause an exception, if the customerRepository is of any other type than CompanyRepository
Use a second interface
After all, I'm not quite sure if what you're doing is DI. In my understanding of DI it should be something like this:
public ActionResult Index(ILogDisplay display)
{
var customers = display.DisplayLog(customerRepository);
return View(customers);
}
ILogDisplay is being a new Interface for a separate class to be injected
public interface ILogDisplay
{
public YourResultType DisplayLog(ICustomerRepository);
}
In this example you actually inject a dependency (the implementation of ILogDisplay) in your class.
There's a couple of questions to raise here:
Why does the repository know how to display a log?
What does DisplayLog() mean in the context of a CustomerRepository?
Why should the controller even care about what the repository is logging?
Why is DisplayLog assigning a variable called customers when its return type is clearly void?
Fundamentally, the behaviour of your repository should be unknown to your controller, this is the essence of the Inversion of Control principle. All it cares about is that given the explicit contract provided by the interface for a repository, a method call will return customers. Logging is a concern of the repository.
A fairly traditional setup, from a DI point of view us the following:
ICompanyRepository:
public interface ICompanyRepository() {
IEnumerable<Company> GetAll();
Company Get(int id);
Company Add(Company item);
bool Update(Company item);
bool Delete(int id);
}
CustomerRepository:
public class CompanyRepository: ICompanyRepository
{
private readonly ILogger logger;
public CompanyRepository(ILogger logger) {
this.logger = logger;
}
// Provide implementation for all interface methods
public Company Get(int id) {
var customers = this.randomCustomerSource.Get(id);
this.logger.Info("Whatever you want to log here");
return customers;
}
}
CustomerController:
public class CustomerController {
private readonly ICustomerRepository customerRepository;
public CustomerController(ICustomerRepository CustomerRepository)
{
customerRepository = CustomerRepository;
}
// GET: Customers
public ActionResult Index()
{
var customers = customerRepository.GetAll()
return View(customers);
}
}
So the repository requests an Ilogger, the controller requests a ICompanyRepository, and will just call GetAll() and return the results. Usually there's a bit more involved but that's the very basic gist of a workflow for a controller that returns data.

Async WCF Service and Call Duration Performance Counter

I am trying to measure the Calls Duration performance counter for a WCF service method.
I have a very simple WCF service as given below.
Service interface:
[ServiceContract]
public interface IFooService
{
[OperationContract]
string DoSomeExpensiveOperation();
}
The service implementation:
public class FooService : IFooService
{
public string DoSomeExpensiveOperation()
{
Thread.Sleep(3000);
return "Some valuable information";
}
}
When the implementation is synchronous (as given above), I can see the Calls Duration being populated.
However, when the service implementation is async (as given below), nothing is populated.
Service interface:
[ServiceContract]
public interface IFooService
{
[OperationContract]
Task<string> DoSomeExpensiveOperation();
}
The service implementation:
public class FooService : IFooService
{
public async Task<string> DoSomeExpensiveOperation()
{
Thread.Sleep(3000);
return await Task.FromResult("Some expensive value");
}
}
I've even tried publishing a custom performance counter after the await. Even this did not work.
Appreciate if anyone can shed some light on this.
Thanks!
Call Duration is not supported for async calls (Operation/Service or Endpoint).
Others WCF counters are still valid but not this one.
From MSDN,
When used on an asynchronous WCF service the Call Duration counter
will always return -1.

Categories