I was previously using Grpc.AspNetCore but am trying to migrate to protobuf-net.grpc because I much prefer the code-first approach given that it's more idiomatically dotnet and the whole solution is built on dotnet, so having .proto files and code-gens just causes a bit of a mess.
So I've created my service as per this guide:
[ServiceContract]
public interface IAdService
{
[OperationContract]
Task<HorseSaleAdReply> GetHorseSaleAds(AdRequest adRequest, CallContext context = default);
}
As far as I can tell, this works nicely.
However, I now want to create a client for this service and have it injected. The docs suggest this approach:
using var channel = GrpcChannel.ForAddress("https://localhost:7184");
var client = channel.CreateGrpcService<IGreeterService>();
var reply = await client.SayHelloAsync(
new HelloRequest { Name = "GreeterClient" });
However, when I try and wrap that in a singleton:
builder.Services.AddSingleton(_ => {
using var channel = GrpcChannel.ForAddress("https://localhost:7184");
return channel.CreateGrpcService<IAdService>();
})
And inject it into one of my classes, I get an error about the channel having already been disposed.
Would you be able to advise on how I can correctly inject these clients?
My only other idea would be to create a client class that implements IDisposable, stores a reference to that channel and does it that way. However, this seems like it really ought to be unnecessary.
Related
I have a Web Api project which relies heavily on Azure Cosmos DB. Until now, having one Cosmos DB account (one connection string) was sufficient. Now a new requirement is to be able to connect to a different Cosmos (two connection strings) depending on an incoming parameter.
For customerId X we should fetch documents from Cosmos DB 1 and for another customer Y we have to look in Cosmos DB 2.
Until now my Startup.cs file registered a singleton instance of CosmosClient. Which in turn gets instantiated like this
cosmosClient = new CosmosClient(endpointUrl, primaryKey);
And this worked really well. The Web Api was easily able to process all requests. But now that we have to new up a CosmosClient per request, performance is really bad.
So my question is; Is there a way to have multiple instances of the same singleton? As in; can we create a single instance of the combination Class+EndPointUrl? (Would that still be a singleton?)
Right now, we are newing up thousands of CosmosClients every minute. And we really need just one more compared to what we had earlier.
There's multiple ways to do this, but an easy implementation would be to create a wrapper around each CosmosClient you use. The only use of the wrapper will be to allow you to use various instances of the CosmosClient and differentiate them by their types.
//Create your own class for each client inheriting the behaviour of CosmosClient
public class ContosoCosmosClient : CosmosClient
{
public ContosoCosmosClient(string connectionString, CosmosClientOptions clientOptions = null) : base(connectionString, clientOptions)
{
}
public ContosoCosmosClient(string accountEndpoint, string authKeyOrResourceToken, CosmosClientOptions clientOptions = null) : base(accountEndpoint, authKeyOrResourceToken, clientOptions)
{
}
public ContosoCosmosClient(string accountEndpoint, TokenCredential tokenCredential, CosmosClientOptions clientOptions = null) : base(accountEndpoint, tokenCredential, clientOptions)
{
}
}
//In Startup.up add a Singleton for each client
services.AddSingleton(new ContosoCosmosClient(...));
services.AddSingleton(new FabrikamCosmosClient(...));
Then in your business logic you can add both clients and depending on your logic choose which client you want to use:
public class MyService
{
public MyService(ContosoCosmosClient contosoClient, FabrikamCosmosClient fabrikamClient)
{
//...
}
}
Thanks for all comments and answers.
In the end, is this case, the best solution was the approach that was suggested by Mr. T. https://devblogs.microsoft.com/cosmosdb/httpclientfactory-cosmos-db-net-sdk/
I'm now still using one CosmosClient, Scoped. Which allows dynamic use of endpoints.
By injecting the IHttpClientFactory and setting the CosmosClientOptions like this;
{
HttpClientFactory = () => _httpClientFactory.CreateClient("cosmos")
});
we are now making full use of the HttpClient and its ability to reuse ports.
So I'm building an ASP.NET-Core API connecting to a mongoDB instance. I was reading through the official Microsoft tutorial regarding this topic and came across the linked code sample.
Basically they instantiate a BookService and create a new instance of MongoClient in the scope of the constructor.
private readonly IMongoCollection<Book> _books;
public BookService(IBookstoreDatabaseSettings settings)
{
var client = new MongoClient(settings.ConnectionString);
var database = client.GetDatabase(settings.DatabaseName);
_books = database.GetCollection<Book>(settings.BooksCollectionName);
}
As I understand this the _books collection would still work without the MongoClient instance present since it knows which collection it's assigned to and how to communicate with it BUT the mongoDB MongoClient re-use guidelines suggests to store a global/static instance of the client to re-use. (I guess for the same port-exhaustion, etc. reason you would want to re-use HTTPClients? Also it supports internal connection pooling, which is nice!)
Thinking further on what they imply I was quite sure it would be a bad idea to instantiate and immediately drop an instance for a client for each of my services. But I dont't know anything about MongoDB on this scope.
I know it's just a tutorial and they tend to be the "quick and dirty" way of coding but since I'm new to this whole topic I just wanted to make sure I would start out properly.
So is it OK doing it the "Microsoft way" or should I just create a global instance for the client or a factory altogether?
//Edit:
For clarification: Would it be better to register the MongoClient as a Singleton on Startup and inject it into classes that need it OR use the method described above?
This is how I typically add Mongo to my pipelines:
services.AddSingleton<IMongoClient>(sp =>
{
var connectionString = "";
return new MongoClient(connectionString);
});
services.AddScoped(sp =>
{
var client = sp.GetRequiredService<IMongoClient>();
var database = "";
return client.GetDatabase(database);
});
This gives me a scoped IDatabase instance I can inject anywhere I need it (while using just one singleton IMongoClient instance).
I have a plugin model architecture that creates my Restful WCF services.
(It will be a couple years before we move to Web Api from WCF so, moving to Web Api isn't exactly a solution.)
I have decoupled WCF Microservices that don't reference each other.
EntityAWebService
EntityBWebService
EnityAWebService knows that a service EntityBWebService exists from a configuration, but doesn't reference it.
EntityAWebService and EntityBWebService are plugins. As such, they could be on loaded on the same site.
EntityAWebService makes a call to EntityBWebService using configuration information. The EntityBWebService could be on the same server or a different server.
- If on a different server, the code will continue to use HttpClient.
- If on the same server, go cract the message and send it through the channel without going through HttpClient, operating system's network, and IIS.
Below is the architecture. The orange is what I want to create.
Using HttpClient means EntityAWebService sends a message that is going to hit the operating systems network layer and go through IIS. Neither of which is necessary. It causes performance issues, and as the Entity plugins increase, so does the number of sockets and even using a singleton httpclient, the sockets are leaking.
The orange in the architecture is what doesn't exist yet.
The code knows the Url to call for Entity B Web Service, the message content, and the headers. How would I simulate, in the code represented by the orange box, what IIS does to forward the call through the behaviors and to the Endpoint?
FYI, my current project is too complex to post, so I will create a sample and post it soon.
Sample project: https://github.com/rhyous/DecoupledWcfServices
Turns out I didn't need to use named pipes. However, investigating how to use named pipes taught me what I needed to know. I just needed to use reflection and ChannelFactory. As the ChannelFactory for IIS hosting already exists, named pipes would be redundant.
Example Project here: https://github.com/rhyous/DecoupledWcfServices
And the appropriate snippet (the meat of the solution) is below.
using System;
using System.Collections.Specialized;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Web;
namespace DecoupledWcfServices
{
/// <summary>
/// Service 1 and Service 2 are in the same namespace in this project
/// </summary>
public class MessageBus
{
public string CallOtherWcfService(string url, object content, NameValueCollection headers)
{
var service = GetServiceName(url);
try
{
var netPipeUrl = $"http://localhost:54412/{service}/{service}.svc";
var serviceContractType = typeof(IService2);
var genericChannelFactoryType = typeof(WebChannelFactory<>).MakeGenericType(serviceContractType);
var binding = new WebHttpBinding();
var channelFactory = Activator.CreateInstance(genericChannelFactoryType, binding, new Uri(netPipeUrl)) as WebChannelFactory<IService2>; // I actually won't know it is an IService2 in my project, but getting this far should be enough
var proxy = channelFactory.CreateChannel() as IService2;
using (new OperationContextScope((IContextChannel)proxy))
{
var task = proxy.GetData("some data"); // Might need more work here to know which method to call based on the Url
task.Wait();
return task.Result; // Serialized JSON
}
}
catch (Exception)
{
throw;
}
}
internal string GetServiceName(string url)
{
var index = url.IndexOf(".svc");
var sub = url.Substring(0, index);
index = sub.LastIndexOf("/") + 1;
var sub2 = url.Substring(index, sub.Length - index);
return sub2;
}
}
}
I have an application (IJobInit) that uses a list from JSON settings to create multiple instances of a class (IJob). This class does some work using two other dependencies, IInputClient and IOutputClient. It uses M.Extensions.DependencyInjection to create a container which is handed off to AutoFac to create an IContainer.
IJobInit(IContainer container)
I would like IInputClient to be configured different for each instance of IJob. Speficially, I'd like to pass in a secret for it to use. The result would be:
IInputClient(HttpClient client)
where HttpClient is configured using ConfigureHttpClient such that IJob does not know that it is pre-authenticated. This would also be suitable:
IInputClient(ISecretProvider secretsProvider, string secretName)
The end result is three instances of IJob with IInputClient configured differently.
IJob(IInputClient inputClient1, IOutputClient outputClient)
IJob(IInputClient inputClient2, IOutputClient outputClient)
IJob(IInputClient inputClient3, IOutputClient outputClient)
How do I achieve this? I was looking at Autofac scopes but those controlwhen an instance is created without any control over its configuration (unless I missed it).
A colleague suggested that I could host each instance of IJob in its own process with its own configuration which is possible but I'm trying to host all the jobs in a single Azure Function and use the list in config to create the inner jobs.
Thanks!
I'm not totally happy with this solution but it works for now.
private async Task<IInputClient> GetClientAsync(string secretId)
{
HttpClient httpClient = this.httpClientFactory.CreateClient();
string secret = await this.secretsProvider.GetSecretAsync(secretId);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Concat(":", secret))));
return this.scope.Resolve<IInputClient>(new TypedParameter(typeof(HttpClient), httpClient));
}
I was wondering if someone could show me how to log a simple request/response from my wcf rest service.
I am self hosting with a console application on the localmachine:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(RawDataService), new Uri(baseAddress));
WebHttpBinding binding = new WebHttpBinding();
//binding.Security.Mode = WebHttpSecurityMode.Transport;
host.AddServiceEndpoint(typeof(IReceiveData), new WebHttpBinding(), "").Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
Console.ReadLine();
}
}
}
I was really hoping all that would be required is something added to the hosting console app. I tryed following this but it was abit confusing http://blogs.msdn.com/b/carlosfigueira/archive/2011/04/19/wcf-extensibility-message-inspectors.aspx
Just to note I am not using any app.config or web.config files.
EDIT:
I also cannot use any third party products for this.
Are you talking about logging for debugging purposes or for monitoring in a live service?
If you are debugging you can just switch on WCF tracing. It will produce a very comprehensive log and there is a good free tool for viewing the log that comes as part of the Windows SDK - I presume when you say you can't use third party product it doesn't include built-in .Net and Windows SDK features...
http://msdn.microsoft.com/en-us/library/ms733025.aspx
A common way to handle this is with Aspect-Oriented Programming (AOP) using the Castle Dynamic Proxy library. The idea is that you can decorate/proxy your service implementation with a dynamic class that intercepts every single method called on your service. No matter what method is called on your service, they'll be "intercepted" by your proxy and sent to a single method where you can log what you want and then you can finish the original call. Here's a quick sample of what that looks like:
public class LoggingInterceptor : IInterceptor
{
// No matter what service method is called, it's funneled through here.
public void Intercept(IInvocation call)
{
MyLogger.Info("Starting call: " + call.Method.Name);
// Actually invoke whatever method was originally called
call.Proceed();
MyLogger.Info("Finished call: " + call.Method.Name);
}
}
Now you need to create a proxy of your service class that uses this interceptor for all of its method calls. You can pretty up and abstract as necessary, but this is the basic jist:
using Castle.DynamicProxy;
...
// Create your service object and then create a dynamic proxy of the object
// that will inject your logging interceptor logic.
ProxyGenerator generator = new ProxyGenerator();
RawDataService service = new RawDataService();
RawDataService proxy = generator.CreateClassProxyWithTarget<RawDataService>(
service,
new LoggingInterceptor());
// Register your proxy object, not the raw service w/ WCF
WebServiceHost host = new WebServiceHost(proxy, new Uri(baseAddress));
... rest of your code as it was ...
Now any call made to your RawDataService will go through the Intercept() method first and when it calls Proceed() your actual implemented service logic will happen. You can update the interceptor to handle exceptions, include a StopWatch and log parameters as needed but that's the basic idea.
My example shows you the brute force way of setting this up. The "cleaner" solution would be to use IoC to create your service instance/proxy but this should get your code doing what you want right now. For further reading, here's a link to the Castle project's tutorial on using its AOP hooks: