I previously posted this before and the answer I got made sense, but I was not able to test it and would crash with null return. Honestly, I got frustrated with myself and shelved this.
Now I'm going to re-attempt this and rephrase it. URL to original post for reference: ( C# Repeating Logic for Multiple Object Types, candidate for generics?)
The goal is the same, one method that can be called for different return type and argument type. There are 8 methods with the same code except 1 line and each method has 60 lines of code. The line actually calls the Channel method.
Current sample signatures (what I want to replace with generic code):
private CreateIndiciumResponse GetResponse(CreateIndiciumRequest req)
{
SwsimV111Soap swsim = swsimFactory.CreateChannel();
return swsim.CreateIndicium(req);
}
private GetRatesResponse GetResponse(GetRatesRequest req) {...}
//Same code except "CreateIndicium(req) is "GetRates(req)" also the return and parameter types match data types for the call.
I've tried to do this:
private TResponse GetResponse<TRequest, TResponse>(TRequest req) where TResponse : class where TRequest : class
{
TResponse res;
SwsimV111Soap swsim = swsimFactory.CreateChannel();
switch (req.GetType().Name)
{
case "GetAccountInfoRequest":
res = null;
break;
case "GetCreateIndicium":
res = swsim.CreateIndicium(req);
break;
default:
res = null;
break;
}
return res;
}
I'm unsuccessful in actually calling it and the line: res = swsim.CreateIndicium(req);
actually shows IDE error: Error CS1503 Argument 1: cannot convert from 'TRequest' to 'USPostal.StampsIM.CreateIndiciumRequest'
The WSDL reference.cs is auto generated by IDE tooling defines CreateIndicium as follows:
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
USPostal.StampsIM.CreateIndiciumResponse USPostal.StampsIM.SwsimV111Soap.CreateIndicium(USPostal.StampsIM.CreateIndiciumRequest request)
{ return base.Channel.CreateIndicium(request); }
CreateIndiciumRequest as:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
[System.ServiceModel.MessageContractAttribute(WrapperName="CreateIndicium", WrapperNamespace="http://stamps.com/xml/namespace/2021/01/swsim/SwsimV111", IsWrapped=true)]
public partial class CreateIndiciumRequest {...}
And the response type as:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
[System.ServiceModel.MessageContractAttribute(WrapperName="CreateIndiciumResponse", WrapperNamespace="http://stamps.com/xml/namespace/2021/01/swsim/SwsimV111", IsWrapped=true)]
public partial class CreateIndiciumResponse {...}
So
1 I could not call the generic GetResponse correctly but to no avail
2 I get the error.
Current call I have is:
req = BuildCreateIndiciumRequest();
resp = GetResponse(req);
Clearly My limited Knowledge in this area is where I need help, thanks in advance..
Related
I would like to gather your advice on the following problem:
Task
There are two microservices running A and B. At some time microservice_A will create a request message (or array of RequestMessage[]) which is an example "RequestMessage" and send it to microservice_B.
public class RequestMessage
{
public Guid guid;
public string result;
public DateTime expirationDateTime;
public RequestMessage()
{
guid = Guid.NewGuid();
result = "no_result";
}
}
The way of getting a response is built in the way that there is a service class that implements the method, which will be called at some time after the request was sent. The call (of the function ResolveRequestedMessage()) will be performed under the hood and only function implementation lay on the developer.
public class RequestMessageResolver : IRequestMessageResolver
{
public bool TResolveRequestedMessage(Guid Id, string result)
{
// find the Request e.g in Request[] by id, Implementation lay on the developer side and can be various.
RequestCollection.Get(id).result = "resolved";
}
}
Problem
The RequestMessage should be awaitable, however the interface of the RequestMessage is defined and cant be changed.
Microservise_A should not proceed to any further action (within the call scope, however still be valid for some other requests e.g status/error/etc) until it id not get the resolution of the requested message.
My idea and thoughts
First I tried to create a wrapper class that will have TaskCompletionSource and can be set from outside (example 1). This works but required a lot of extra wrappers to achieve the desired results.
Another idea is to modify the wrapper to implement INotifyCompletion instead of having TaskCompletionSource, but not sure if this will bring big overhead and make the solution complex for no reason - did not try yet.
Code Example 1:
public class RequestMessageWrapper
{
public TaskCompletionSource<bool> completionSource;
public RequestMessage requestMessage;
public RequestMessageWrapper(RequestMessage requestMessage)
{
completionSource = new TaskCompletionSource<bool>();
this.requestMessage = requestMessage;
}
public async Task GetResponseAsync()
{
// also need to be cancelled somehow if (DateTime.Now > requestMessage.expirationDateTime)
await completionSource.Task;
}
}
I am having a service class which contains a method GetSalespersonsAsync(). It will create an object for Data Service and calls a method dataService.GetSalesPersonsAsync(). Here I need all the Data Service method calls to pass to another method in the service InitiateApiCallAsync().
public async Task<List<Salesperson>> GetSalespersonsAsync()
{
using (var dataService = DataServiceFactory.CreateDataService())
{
var response = await InitiateApiCallAsync(dataService.GetSalesPersonsAsync());
return response?.code == 0 ? response.data : null;
}
}
public async Task<TResponse> InitiateApiCallAsync<TResponse>(Task<TResponse> dataService) where TResponse : BaseAPIResponse
{
TResponse response = await dataService;
await BaseProcess(response); // async process of some internal actions with response from the method call
return response;
}
If any of the data service methods failed to pass into the InitiateApiCallAsync() method, then the compiler need to show compile time error, so that it won't be missed to pass into it in future.
Kindly provide the possible solution to handle this to avoid missing this behavior in future.
I'm loading some endpoints defined in external assemblies, into my web app on startup with:
// Add channel endpoints.
var endpointsFolder = Configuration.GetSection("EndpointsFolder").Get<string>();
foreach (var file in Directory.GetFiles(endpointsFolder))
services.AddMvc().AddApplicationPart(Assembly.LoadFrom(file));
All of the loaded endpoints derive from a custom class called SecureEndpoint which requires an interface ISecurityContext:
[ApiEndpoint]
public class SecureEndpoint : ControllerBase {
public SecureEndpoint(ISecurityContext securityContext) { ... }
}
So, for example:
[Route("test")]
public sealed class TestEndpoint : SecureEndpoint {
public TestEndpoint(ISecurityContext securityContext) : base(securityContext) { ... }
[HttpGet]
public string Get() {
return "This is a test.";
}
}
The problem I'm having is that when I invoke the endpoint, I get an error saying:
System.InvalidOperationException: Unable to resolve service for type Namespacing.ISecurityContext while attempting to activate Namespacing.TestEndpoint.
A few searches on the matter give a pretty simple solution:
services.AddScoped<ISecurityContext, SecurityContext>();
However, I have one problem; ISecurityContext is given to me in the form of the interface only. The value that is given to me, is the value I should use everywhere it's required. As a result, I need a way to say "if ISecurityContext is needed, use this value".
Is there a way to always send a specific value for a given type?
Edit: services.AddSingleton(...) did the trick, but I don't have time to type an answer up for it currently. I'll circle back later this afternoon.
If there are any compilation issues, they are only the result of typing this question.
I just dived a little deeper into it and discovered new details.
Not the 'UriTemplate' in general causes the 2nd invocation of 'AfterReceiveRequest' but the optional parameter within!
If I call the method by
http:/ /myserver/result/val1
AfterReceiveRequest will be invoked twice.
If I pass all possible parameters like
http:/ /myserver/result/val1/val2/val3
there will be no useless invocation. Is that behavior as intended?
UriTemplate = "result/{para1=null}/{para2=null}/{para3=null}"
--- following the initial post, just for information ---
While implementing a WCF REST Service System I stuck on a problem with the http-headers.
In my ServiceContract there is a method with an UriTemplate definition at the WebGet attribute, so it can be called via
http://server/resource/val1/val2 ...
instead of
http://server/resource?para1=val1¶2=val2 ...
(I need this because of compatibility reasons.)
Also there is a significant value in the http-headers collection, that I need to read. Therefore I implement IDispatchMessageInspector and add this inspector to the EndpointDispatchers MessageInspectors collection. By that the AfterReceiveRequest will be invoked by WCF and I can access WebOperationContext.Current.IncomingRequest.Headers to read the desired Value.
The Problem: WCF solves the UriTemplate-mapping by generating a second request to the destination method, but does not pass the header entries from the original call to the generated second call. So AfterReceiveRequest (and of course BeforeSendReply, too) will be invoked twice, but the header-values from the real client call are only included in the first call.
Also I found no way to correlate the first and the second AfterReceiveRequest call, to implement a "special way" for passing the header-value from the first to the second call.
Is there a way to tell WCF to route the headers to the UriTemplate-redirected second call?
Here are some code fragments to make it clear:
[ServiceContract]
public interface IMyService
{
[WebGet(UriTemplate = "result/{para1=null}/{para2=null}/{para3=null}")]
bool MyMethod(string para1, string para2, string para3);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
[MyServiceInspectorBeavior]
public class MyService : IMyService
{
public bool MyMethod(string para1, string para2, string para3)
{
return DoTheWork();
}
//...
}
public class MyServiceInspectorBeavior : Attribute, IServiceBehavior
{
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (EndpointDispatcher epDispatcher in serviceHostBase.ChannelDispatchers.OfType<ChannelDispatcher>().SelectMany(cDispatcher => cDispatcher.Endpoints))
{
epDispatcher.DispatchRuntime.MessageInspectors.Add(new MyInspector());
}
}
//...
}
public class MyInspector : IDispatchMessageInspector
{
//this is invoked twice for each client request,
//but only at the first call the header is present...
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
WebHeaderCollection webOpContext =
WebOperationContext.Current.IncomingRequest.Headers;
string xForwardedIp = webOpContext["X-FORWARDED-IP"];
WriteLog(xForwardedIp);
return OperationContext.Current.IncomingMessageProperties["ActivityId"];
}
//...
}
Does anyone have a good example for common error handling for service calls? I just started a new project and I see a lot of duplicate code that I don't like and would like to get rid of. I manage to do so in several other layers but in the proxy layer making the calls to services it is a little bit harder. The code is basically structured as follows:
ResponseType MyProxyFunc(different, parameters)
{
var client = MyServiceClient();
using (Tracer functionTracer = new Tracer(Constants.TraceLog))
{
try
{
var response = client.MyRequest(different, parameters);
if (response.ErrorCode != Constants.OK)
{
ProxyCommon.ThrowError(besvarelseDS.Status[0].ErrorCode);
}
}
finally
{
ProxyCommon.CloseWcfClient(client);
}
return response;
}
}
The code above is just a sample, the ProxyCommon object is a static class with various methods (should probably not be abstract instead of static but that is another discussion). So does anyone have a good suggestion how to abstract this piece of code? I want to have the using, the try/catch and the if-statment in somekind of abstract method, but it's hard since MyServiceClient differs, the numbers of parameters differs and the request is not the same.
EDIT: A pattern I've used before is to use a generic Execute function like public T Execute<T>(Func<T> func). But I can't get that type of pattern to work in this case.
EDIT #2: I've updated the code but I'm not 100 % satisfied, more like 60-75 %. The problem with the code below is that it uses the service objects and they will not be the same for all services, but this will work for service calls against the service in the example that has a wrapped request and response object which it's ok. But I still don't think this is the solution to the problem:
public IList<PGSA.Data.Cargo.PGSAReportCargo> GetPGSAReport(DateTime dateFrom, DateTime? dateTo)
{
var reportService = new ReportServiceClient();
var request = new GetPGSAReportRequest()
{
SystemCode = Settings.SystemID,
FromDate = dateFrom,
ToDate = dateTo
};
var response = Execute(reportService, reportService.GetPGSAReport, request);
return response.PGSAReport.ToList();
}
public L Execute<T, K, L>(T client, Func<K, L> serviceFunc, K request)
where T : ICommunicationObject
where K : RequestBase
where L : ResponseBase
{
using (Tracer functionTracer = new Tracer(Constants.TraceLog))
{
try
{
L response = serviceFunc(request);
if (response.ErrorCode != Constants.OK)
{
ProxyCommon.ThrowError(response.ErrorCode);
}
return response;
}
finally
{
ProxyCommon.CloseWcfClient(client);
}
}
}
EDIT #3: The ResponseBase and RequestBase in EDIT #2 are base classes defined by the service.
Your last approach looks fine to me - I would simplify it slightly as follows:
public R Execute<T, R>(this T client, Func<R> serviceFunc)
where T : ICommunicationObject
where L : ResponseBase
{
using (Tracer functionTracer = new Tracer(Constants.TraceLog))
{
try
{
R response = serviceFunc();
if (response.ErrorCode != Constants.OK)
{
ProxyCommon.ThrowError(response.ErrorCode);
}
return response;
}
finally
{
ProxyCommon.CloseWcfClient(client);
}
}
}
And use it
reportService.Execute(() => reportService.GetPGSAReport(request));
Idea here is to eliminate dependency on not needed request object.