load WCF service by environment in .net core project - c#

I have a problem while adding WCF in the .NET core project.
When I used .net in the past I can add multiple environments in web.config so I can load the correct web service at runtime (Dev, Rec, Prod).
The problem in the .net core project when I added a reference of my WCF service as Connected Service it created one file ConnectedService.json that contains a URL for the WCF service.
{
"ProviderId": "Microsoft.VisualStudio.ConnectedService.Wcf",
"Version": "15.0.20406.879",
"GettingStartedDocument": {
"Uri": "https://go.microsoft.com/fwlink/?linkid=858517"
},
"ExtendedData": {
"Uri": "*****?singleWsdl",
"Namespace": "Transverse.TokenService",
"SelectedAccessLevelForGeneratedClass": "Public",
"GenerateMessageContract": false,
"ReuseTypesinReferencedAssemblies": true,
"ReuseTypesinAllReferencedAssemblies": true,
"CollectionTypeReference": {
"Item1": "System.Collections.Generic.List`1",
"Item2": "System.Collections.dll"
},
"DictionaryCollectionTypeReference": {
"Item1": "System.Collections.Generic.Dictionary`2",
"Item2": "System.Collections.dll"
},
"CheckedReferencedAssemblies": [],
"InstanceId": null,
"Name": "Transverse.TokenService",
"Metadata": {}
}
}
My question how can I load the correct service based on the used environment.
Note.
In my Project, I did not have an appsettings neither web config. It is a .net core class library and it is called in ASP.NET core Application as Middleware.

As I understand from this article, this is Microsoft's recommendation:
Add new class file
Add same Namespace of service reference.cs
Add Partial Class to expand reference service class (declared in Reference.cs)
And Partial Method to implement ConfigureEndpoint() (declared in Reference.cs)
Implement ConfigureEndpoint() Method by Setting a new value for Endpoint
Example:
namespace Your_Reference_Service_Namespace
{
public partial class Your_Reference_Service_Client
{
static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials)
{
serviceEndpoint.Address =
new System.ServiceModel.EndpointAddress(new System.Uri("http://your_web_service_address"),
new System.ServiceModel.DnsEndpointIdentity(""));
}
}
}
Here, you can take the value from the appsettings.json file
new System.Uri(configuration.GetValue("yourServiceAddress")

For whom are interested by the solution
I added an endpoint for my service in each appseetings.{environment}.json and in Service class I inject new Instance of my service based on the environment variable ASPNETCORE_ENVIRONMENT
services.AddTransient<Transverse.TokenService.ITokenService>(provider =>
{
var client = new Transverse.TokenService.TokenServiceClient();
client.Endpoint.Address = new System.ServiceModel.EndpointAddress(Configuration["Services:TokenService"]);
return client;
});
Maybe is not the best but it works fine.

I am using .Net Core 3.1, this is my way when I call WCF Service.
var sUserClientRemoteAddress = _configuration.GetValue<string>("WCFRemoteAddress:UserClient");
UserClient userService = new UserClient(UserClient.EndpointConfiguration.CustomBinding_IUser, sUserClientRemoteAddress);
First, Get the endpoint remote address from appsettings.json
Second, Call web service client using that address in CTOR WCF Client Class parameter
Thanks in advance.

Use a ChannelFactory to consume your service.
WCF ChannelFactory vs generating proxy
A ChannelFactory allows you to set an EndpointAddress.
How to: Use the ChannelFactory
The URL for the endpoint can be loaded from a configuration file. In a more advanced setup a directory lookup for the service can be performed to retrieve the URL for the environment where the application is deployed. https://en.wikipedia.org/wiki/Service_provider_interface
https://github.com/jolmari/netcore-wcf-service-proxy
Example of consuming multiple WCF services using a proxy
implementation in a ASP.NET Core Web-application.

I just went with the simple route. Note: I'm on .NET 6.
Step 1:
Add the service reference by following steps in the MSFT guide:
https://learn.microsoft.com/en-us/dotnet/core/additional-tools/wcf-web-service-reference-guide
Step 2:
Create a factory to create WCF service client. You can pass the wcfUriBasedOnEnvironment from the main project or anywhere you want.
public static class WCFServicesClientFactory
{
// APIWCFServicesClient is the service client inside Reference.cs generated in Step 1
public static APIWCFServicesClient CreateUsingUri(string wcfUriBasedOnEnvironment)
{
return new APIWCFServicesClient(APIWCFServicesClient.EndpointConfiguration.BasicHttpBinding_IAPIWCFServices,
new System.ServiceModel.EndpointAddress(new System.Uri(wcfUriBasedOnEnvironment)));
}
}
Step 3:
Use it like:
var wcfClient = WCFServicesClientFactory.CreateUsingUri("http://your_web_service_address");

Related

Self referencing loop detected for property in Web Application

I am trying to port a mobile service app to a web application. To do so I created a new web application and copied the relevant code from the working mobile service to the new web application that I created (using the mobile app template.
I have the following code in my Startup method in the new application:
public partial class Startup
{
public static void ConfigureMobileApp(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.UseDefaultConfiguration()
.ApplyTo(config);
// Use Entity Framework Code First to create database tables based on your DbContext
Database.SetInitializer(new MobileServiceInitializer());
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
app.UseWebApi(config);
MobileAppSettingsDictionary settings = config.GetMobileAppSettingsProvider().GetMobileAppSettings();
}
}
The config.Formatters were copied from the original application which returns the entity and its children to the json output of the api controller.
In the new application I had to add the [MobileAppController] to my api controllers. I get the following error from the controller in the web application app: Self referencing loop detected for property Teams
(The model has Teams --> Players and players has a teamid)
Based on this detailed question:
Self referencing loop detected - Getting back data from WebApi to the browser
The above code should work as it does in my mobile service app. The web service app appears to ignore the config.Formatters value as I have tried every value in the above question but I still get the same error.
If I place the [JSON Ignore] attribute before the Child list, then I do not get the error, but also do not get the children back in the json. How can I get this web application to accept the formatting values?
hank you for your useful comments. Thank you #dbc for pointing me to this question. I spent 3 days looking and didn't find it. So apparently I was correct that the mobile app is ignoring the json formatters. You let me to this post [json net error]. Adding the attribute [JsonObject(IsReference = true)] before the DTOs solved the problem with minimum code. [json net error]:stackoverflow.com/questions/7397207/… – Haim Katz Jul 24 at 13:41  

SignalR 404 error when trying to negotiate

So I am very new to SignalR, in fact I've only been using it for a couple of days now. Anyway, I am getting the error below when my application first starts up:
The code for the application in question is located in two projects, a Web API and a Single Page Application (SPA). The first one has my backend code (C#) and the second one my client-side code (AngularJS). I think the problem might be due to the fact that the projects in question run on different ports. The Web API, where my SignalR hub lives, is on port 60161 and the SPA is on 60813. My hub is declared like so:
public class ReportHub : Hub
{
public void SendReportProgress(IList<ReportProgress> reportProgress)
{
this.Clients.All.broadcastReportProgress(reportProgress);
}
public override Task OnConnected()
{
this.Clients.All.newConnection();
return base.OnConnected();
}
}
and then in my Startup.cs file for my Web API I initialize SignalR like this:
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
config.Services.Replace(typeof(IHttpControllerActivator), new NinjectFactory());
config.MessageHandlers.Add(new MessageHandler());
//set up OAuth and Cors
this.ConfigureOAuth(app);
config.EnableCors();
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
// Setting up SignalR
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
map.RunSignalR(new HubConfiguration { EnableJSONP = true });
});
//set up json formatters
FormatterConfig.RegisterFormatters(config.Formatters);
WebApiConfig.Register(config);
app.UseWebApi(config);
}
For my client-side code I use an Angular SignalR API called angular-signalr-hub (Angular-signalr-hub). The client-side follows:
angular
.module("mainApp")
.factory("reportHubService", ["$rootScope", "Hub", reportHubService]);
/// The factory function
function reportHubService($rootScope, Hub) {
var vm = this;
vm.reportName = "None";
// Setting up the SignalR hub
var hub = new Hub("reportHub", {
listeners: {
'newConnection': function(id) {
vm.reportName = "SignalR connected!";
$rootScope.$apply();
},
'broadcastReportProgress': function (reportProgress) {
vm.reportName = reportProgress.reportName;
$rootScope.$apply();
}
},
errorHandler: function(error) {
},
hubDisconnected: function () {
if (hub.connection.lastError) {
hub.connection.start();
}
},
transport: 'webSockets',
logging: true
//rootPath: 'http://localhost:60161/signalr'
});
I did some googling yesterday and one of the suggestions I came upon was to set the SignalR URL to the one of my Web API, which I did (the commented out line above). When I uncomment the line in question, that does seem to do something because if I now go to http://localhost:60161/signalr/hubs in my browser, it does show me the dynamically generated proxy file:
and when I run my application I no longer get the error above, but now it doesn't seem to connect. It gets to the negotiate line and it stops there:
I think it should look like this (this is from a SignalR tutorial I found):
In addition, none of my listeners (declared in my Angular code above) get called, so something is still now working quite right. There should be more lines in the log to the effect that connection was successfully established, etc. What could be the problem here?
UPDATE: upon further debugging i found out the problem is most likely being caused by the ProtocolVersion property being different between the client and the result here:
Because of that it seems it just exists and fails to establish connection.
I figured out what the problem was. My SignalR dependencies were out of date and because of that my client and server versions differed. All I had to do was update (via NuGet Package Manager) all SignalR dependencies to the latest version and now it works.
As a side note, SignalR was not very good at telling me what was wrong. In fact, no error message was displayed, unless of course there was some additional logging somewhere that had to be found or turned on, in addition to the logging I already had (turned on). Either way, it's either not logging certain errors or it makes it difficult to figure out how to turn on all logging. I had to go and debug the JQuery SignalR api to figure out what the problem was, which was a time consuming endeavour.

Proxy for interacting with WCF services on client

Please, Help me !
I've some problems with my project (WPF with WCF).
My project its client-server interaction. In server I've EF that interaction with PatternRepository . On server it`s a wcf interaction I have services.
Each service its repository. In each service I have a set of commands for communication between server and client . Between client and server data transfer occurs via Json . Example, it's service:
public class ProductRepositoryService : IProductRepositoryService
{
public void AddProduct(string json)
{
_productRepository.Add(wrapperProduct.DeserializeProduct(json));
_productRepository.Save();
}
public void DeleteProduct(string json)
{ productRepository.Delete(_productRepository.GetById(wrapperProduct.DeserializeProduct(json).Id));
_productRepository.Save();
}
}
Example, its ProductSeviceLogics that interaction with ProductService :
ProductRepositoryServiceClient _service1Client;
public ProductSeviceLogics()
{
this._service1Client = new ProductRepositoryServiceClient();
}
public void AddProduct(string json)
{
_service1Client.AddProduct(json);
}
public void DeleteProduct(string json)
{
_service1Client.DeleteProduct(json);
}
It's mean that if I'll create services. I'll be create those methods for each service on the server and the client. I think that it's very bad .
So my question, How can i do so that these methods will be for all services ?
That is, I want not to create this methods for each service and for each client.
I recommend you to have a look into this ASP.NET MVC Solution Architecture article. Download the code and have a look into that, how they maintain the Repositories/Services/Models in separate class library and make use in User interface or WCF or WebAPI.
Here I will provide some sample solution pattern.
Create a new blank solution : File -> New Project -> Other Project Type -> Blank Solution and name it as MyProject.
Create new class library listed below
MyProject.Model
Create POCO class like Product, Sale
MyProject.Data
Add a reference of Model.
Contains EF(DbSet and DbContext) and Repositories like ProductRepository, SalesRepository.
MyProject.Service
Add reference Model and Data.
Make a call to your repositories from this project.
Create User Interface and WCF services
MyProject.Web
MyProject.WCF
Add a reference of Model and Service.
Your work flow like this WCF or Web Calls --> Service --> Repositories --> EF, So you can avoid creating multiple service for client and server.
Hope this helps you.
I solved this issue.
Generate proxy for WCF Service
Generate Proxy by implementing ClientBase class*
Generating proxy by using ClientBase class option has an advantage that it creates proxy at run time, so it will accommodate service implementation changes. Let’s follow the steps to generate proxy.
Add a Client Project to solution named as “ClientApp2″ that is
basically a Console Application.
enter image description here
Add reference of StudentService Project to ClientApp2. Add following
proxy class using ClientBase as:
public class StudentServiceProxy : ClientBase<IStudentService>, IStudentService
{
public string GetStudentInfo(int studentId)
{
return base.Channel.GetStudentInfo(studentId);
}
}
Note: Don’t forget to add “using StudentService” to class.
Following is the code for program.cs class in ClientApp2. We are using above created proxy class to communicate with WCF Service
“StudentService“.
class Program
{
static void Main(string[] args)
{
StudentServiceProxy myclient;
myclient = new StudentServiceProxy();
int studentId = 1;
Console.WriteLine(“Calling StudentService with StudentId =1…..”);
Console.WriteLine(“Student Name = {0}”, myclient.GetStudentInfo(studentId));
Console.ReadLine();
}
}
Note: Don’t forget to add “using System.ServiceModel” to class.
App.Config file will have following configuration:
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name=”WSHttpBinding_IStudentService” />
</wsHttpBinding>
</bindings>
<client>
<endpoint address=”http://localhost:4321/StudentService”
binding=”wsHttpBinding”
bindingConfiguration=”WSHttpBinding_IStudentService”
contract=”StudentService.IStudentService”
name=”WSHttpBinding_IStudentService”>
</endpoint>
</client>
Now, when we run the client application, we will receive the following same output as we get in earlier option “Adding Service Reference”.

Web services and client DLL

I have a web service and a client DLL. The web service uses an Oracle database.
For testing the client DLL, I copied the web service and made it point to a test database. Then I copied the client DLL and added this test web service using "Add web reference".
What I would like to do is to use one web service and one client DLL but be able to tell the client DLL to use either use the test or production database rather than two identical web serivces and client DLLs.
Edit
I mis-stated the issue. What I need to do is use one client DLL and two web services (one production version, one development/test version) and be able to, somehow, tell the client DLL which web services to use.
This is a sample of how the web service, client DLL and client app are used:
public class DSSService : System.Web.Services.WebService
{
public DSSService()
{
}
[WebMethod(MessageName = "GetFacility", BufferResponse=true, Description = "blah.")]
public Facility GetFacility(string sFDBID, string sZip, string sFinNo)
{
Facility oFacility = ...;
...
return oFacility;
}
....
}
Client DLL:
namespace DSSConfig
{
string sWSURL;
public class Config
{
public Config()
{
}
public void SetWSURL(string sURL)
{
sWSURL = sURL;
}
public Facility GetFacility(string sFDBID, string sZip, string sFinNo)
{
DSSService Proxy = new DSSService();
proxy.Url = sWSURL;
Facility oFacility = Proxy.GetFacility(sFDBID, sZip, sFinNo);
return oFacility;
}
In client application, having DSSConfig DLL as reference:
DSSConfig oConfig = new DSSConfig();
oConfig.SetWSURL("http://myserver/WebService1/service.asmx");
oConfig.GetFacility("blah", "blah", "blah");
What you need to do is change the WEB Service to take a parameter that it will use to construct the connection string to the DB.
Then change client DLL to pass that parameter as part of the call or connection.
Then you can configure the Client DLL to using any technique you like to pass the parameter. My suggestion is perhaps derive a class from the generated proxy in the client DLL and use this in the client code.
Without specific implementation details I can't be more precise.

Do I need to change web service path everytime when I call it?

I am developing on Windows application in c# and I am using web server's web service in this Windows application.
The web service should be dynamic and I need to change it in the application.
I managed to do it by this code :
CallWebService.MyWS ws = new CallWebService.MyWS();
ws.Url = "new url";
This new url will be set as per client's web server url.
I am calling this web service (I mean web service functions) 20 to 25 times in my application.
Do I need to change this path everytime when I call it or for the first time will be ok ?
Use a fixed port number for your service and configure this url in your app/web.config file and use it in your code.
Create a helper class and use that. Make it configurable by using an app setting or better store in config table in database if you are using one.
If you are using WCF client, you can pass URL in client constructor. Otherwise create a partial class for your webservice to create that constructor.
public class MyWebServiceHelper
{
private string _url = null;
public MyWebServiceHelper()
{
this._url = GetWsUrlFromDbOrAppConfig();
}
public CallWebService.MyWS GetMyWebServiceProxy()
{
return new CallWebService.MyWS("WcfBindingConfig", _url);
}
}

Categories

Resources