I created a project that have WCF service and client consume it asynchronously
(like the one in http://msdn.microsoft.com/en-us/library/bb885132.aspx)
The problem comes with me if the client is MVC4
When I call the wcf from client-side, the code is
ChannelFactory<IMathService> myChannelFactory =
new ChannelFactory<IMathService>(myBinding, myEndpoint);
myChannelFactory.Open();
IMathService instance = myChannelFactory.CreateChannel();
// Call Service.
IAsyncResult ar = instance.BeginFunction(param,
(are) =>
{
result = ((IMathService)are.AsyncState).EndFunction(are);
}
, instance);
return View(result);
With this code, my "result" is always default value.
How can I make the action wait until the result has value from EndFunction?
Thanks
You need to use ASP .NET MVC AsyncController
Full explanation - http://msdn.microsoft.com/en-us/library/ee728598(v=vs.98).aspx
Edited sample for your scenario-
public class PortalController : AsyncController {
public void NewsAsync(string city) {
AsyncManager.OutstandingOperations.Increment();
ChannelFactory<IMathService> myChannelFactory =
new ChannelFactory<IMathService>(myBinding, myEndpoint);
myChannelFactory.Open();
IMathService instance = myChannelFactory.CreateChannel();
// Call Service.
IAsyncResult ar = instance.BeginFunction(param,
(are) =>
{
AsyncManager.Parameters["result"] = ((IMathService)are.AsyncState).EndFunction(are);
AsyncManager.OutstandingOperations.Decrement();
}
, instance);
}
public ActionResult NewsCompleted(string[] result) {
return View(result);
}
}
Related
net 5.0 lover.
I am new in blazor and .net 5.0, I develop the application with blazor WebAssembly and WebApi.
There are two major Projects: Client, Server.
Client is Blazor WebAssembly and Server is WebApi Controller Project.
In server side, in controller, HttpGet Method, i add a value to Response header:
[HttpGet]
public async Task<ActionResult<IList<Country>>> GetAsync([FromQuery] Pagination paginationDto)
{
/...
httpContext.Response.Headers.Add("TotalPages", totalPages.ToString());
//...
IList<Country> = ...
return result;
}
In Client project razor page, call the api with following method from generic calss:
protected virtual async Task<PaginatedResponse<O>> GetAsync<O>(Pagination pagination)
{
HttpResponseMessage response = null;
try
{
response = await httpClient.GetAsync(RequestUri);
if (response.IsSuccessStatusCode)
{
try
{
//This response Header always is null!
System.Console.WriteLine("response.Headers: " + response.Headers.ToString());
O result = await response.Content.ReadFromJsonAsync<O>();
var paginatedResponse = new PaginatedResponse<O>
{
Response = result,
TotalPages = totalPages
};
return paginatedResponse;
}
//...
return default;
}
When Api call from postman the result and Header is fine and TotalPages is there.
In Client App, the result is ok, but the Header is null.
Any information will save me ;-)
Thanks in Advance.
I think you're overcomplicating this by trying to use headers to pass back a result that can be passed more easily as part of the content. You even sort of realise this you're trying to use a PaginatedResponse in the Blazor client.
So instead of the API returning just a list, have a PaginatedResponse class in a shared library somewhere.. e.g.
/// <summary>
/// Paged result class
/// </summary>
/// <typeparam name="T"></typeparam>
public class PaginatedResponse<T>
{
public int TotalPages { get; set; }
public int Page { get; set; }
public List<T> Data { get; set; }
}
Your API then returns this
[HttpGet]
public async Task<ActionResult<PaginatedResponse<Country>>> GetAsync([FromQuery] Pagination paginationDto)
{
// ... query results here
var result = new PaginatedResponse<Country>()
{
Page = x,
TotalPages = totalPages,
Data = countrylist // from query
};
return result;
}
Your Blazor client can then use the same PaginatedResponse class and just use the standard GetFromJsonAsync method:
var result = await Http.GetFromJsonAsync<PaginatedResponse<Country>>("yourApiUri");
This is why I love Blazor!
This is the exactly answer for how search for answer:
in Server project, in startup.cs, in ConfigureServices method, add following code for CORS or update your CORS rule:
services.AddCors(options => options.AddPolicy(name: "WebApiProjectName or somthing", builder =>
{
builder.WithOrigins("http://localhost:xxxx") //xxxxx is server port
.AllowAnyMethod()
.AllowAnyHeader()
//.AllowCredentials() // its optional for this answer
.WithExposedHeaders("*"); // this is the code you need!
}));
I have an wcf SERVICE that get a request of branch code and return 5 products list values.
In server side, each product process takes about 6 sec to be completed, so if I run synchronous it will take 30 seconds. I altered my server code and run them in parallel. It works fine, but it seems that each parallel process get threads from IIS threadspool.
Now I am thinking to run asynchronous each product processes and when all will be finished then return the whole list of products to client. It will be grateful if you help me about that..
How can I run asynchronous these processes in server side ? No matter if client run sync or async. That what I need is to run async product processes in order to serve the results faster.
Try this asynchronous approach with generics type:
namespace InterNameSpace
{
[ServiceContract]
public interface ITestClass
{
[OperationContract]
Task<AnyComplexType> xpto(string baseUrl, string getAsyncUrl);
}
}
namespace Test
{
ServiceBehaviorAttribute(InstanceContextMode = InstanceContextMode.PerSession)]
public class TestClass : ITestClass
{
public async Task<AnyComplexType> xpto(string baseUrl, string getAsyncUrl)
{
try
{
return await TestAux.Auxiliar.RetrieveDeserializeObject<AnyComplexType>(baseUrl, getAsyncUrl);
}
catch (Exception)
{
return null;
}
}
}
}
Create a Product complex-type, pass it to your call method. In this method use the keyword async and task to call the deserialized method as asynchronous.
Your deserialized method recieve the baseurl and the controller route. As a genetic type you can pass different complex-type and deserialaze this object and return it.
namespace TestAux
{
public class Auxiliar
{
public static async Task<T> RetrieveDeserializeObject<T>(string baseUrl, string getAsyncUrl) where T : class
{
try
{
T deserializeObject = null;
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(baseUrl);
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await httpClient.GetAsync(getAsyncUrl);
//throw an exception if not successful
if (response.EnsureSuccessStatusCode().IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
// DeserializeObject our concrete class into a JSON String
deserializeObject = await Task.Run(() => JsonConvert.DeserializeObject<T>(content));
}
return deserializeObject;
}
catch (Exception)
{
return null;
}
}
}
}
I'm hosting a duplex wcf service using windows service with castle windsor wcffacility using TCP binding.
There is no problem with hosting, I think, when I add a service reference to a console application.I'm able to access the duplex service without any issues.
Problem arises when I use castle windsor at the client side while resolving. Below is the code am using for adding the wcf services through code based on config file.
public static IWindsorContainer RegisterWcfClients(IocBuildSettings iocBuildSettings,
IWindsorContainer container)
{
//Register callback methods for duplex service first.
container.Register(Component.For<INotificationCallback>()
.ImplementedBy<NotificationCallbackCastle>()
.LifestyleTransient());
// get dictionary with key = service class, value = service interface
var servicesWithWcfInterfaces = Assembly.GetAssembly(typeof (IApplicationService))
.GetTypes()
.Where(x => (x.IsInterface || x.IsClass) && HasServiceContract(x))
.ToList();
var registrations = new List<IRegistration>();
//get the client section in System.ServiceModel from web.config file
var clientSection = ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection;
//get the endpointsCollection from childSection
var endpointCollection =
clientSection.ElementInformation.Properties[string.Empty].Value as ChannelEndpointElementCollection;
foreach (var serviceInterface in servicesWithWcfInterfaces)
{
//get the childEndpoint name from web.config file
var endpointName = GetClientEndpointName(endpointCollection, serviceInterface);
//register services which are declared in web.config file only.
if (string.IsNullOrEmpty(endpointName)) continue;
// attribute is either on the service class or the interface
var attribute =
(ServiceContractAttribute)
(Attribute.GetCustomAttribute(serviceInterface, typeof (ServiceContractAttribute)));
if (attribute != null)
{
WcfClientModelBase model = null;
// handle duplex differently
if (attribute.CallbackContract != null)
{
model = new DuplexClientModel
{
Endpoint =
WcfEndpoint.ForContract(serviceInterface).FromConfiguration(endpointName)
}.Callback(container.Resolve(attribute.CallbackContract));
registrations.Add(WcfClient.ForChannels(model).Configure(c => c.LifestyleSingleton()));
}
else
{
//regular attributes
model = new DefaultClientModel
{
Endpoint = WcfEndpoint.ForContract(serviceInterface).FromConfiguration(endpointName)
};
registrations.Add(WcfClient.ForChannels(model).Configure(c => c.LifestyleTransient()));
}
}
}
return container.Register(registrations.ToArray());
}
Am hosting only one duplex service and the below are the servicecontracts -
[ServiceContract(CallbackContract = typeof(INotificationCallback))]
public interface INotificationService
{
[OperationContract(IsOneWay = false)]
void Subscribe(Guid subscriptionId, string userName, string[] eventNames);
[OperationContract(IsOneWay = true)]
void EndSubscribe(Guid subscriptionId);
}
[ServiceContract]
public interface INotificationCallback
{
[OperationContract(IsOneWay = true)]
void ReceiveNotification(NotificationResultDto notificationResult);
}
[DataContract]
public class NotificationResultDto
{
[DataMember]
public string UserName { get; set; }
[DataMember]
public string NotificationMessage { get; set; }
}
When I try to resolve the duplex service using the below statement.
var temp = _container.Resolve();
I get error -
WcfClientActivator: could not proxy component c2a216c2-af61-4cb2-83ba-e4d9a5cc4e68
with inner exception - The Address property on ChannelFactory.Endpoint was null. The ChannelFactory's Endpoint must have a valid Address specified.
in the web.config file under client section -
<endpoint address="net.tcp://localhost:9877/NotificationService" binding="netTcpBinding"
bindingConfiguration="netTcpBindingConfiguration" contract="ServiceContracts.INotificationService"
name="INotificationService_Endpoint" />
After few hours of struggling, I found a work around for this problem.
I think this could a bug in Castle Windsor, while creating DuplexClientModel, endpoint cannot be created using "FromConfiguration". It fails while resolving during runtime. However samething works fine with "DefaultClientModel".
My workaround was to read the config file and get the address, binding and contract details and use them to create Endpoint in code.
model = new DuplexClientModel
{
//Endpoint = WcfEndpoint.ForContract(serviceInterface).FromConfiguration(endpointName)
//FromConfiguration method is failing for some reason,could be b.u.g in castle,
//so had to do this workaround by reading the web.config file and creating the Endpoint
//from there manually.
Endpoint = WcfEndpoint.ForContract(serviceInterface)
.BoundTo(CreateBindings(clientEndpoint.Binding))
.At(clientEndpoint.Address)
}.Callback(container.Resolve(attribute.CallbackContract));
How do i acces my methods in my webapi when there are multiple get,post and delete methods from my console application, how do i differentiate them this is my api controller
public IQueryable<Store> GetAll()
{
return StoreRepository.All;
}
//GetAll Stores including all relation tables
public IQueryable<Store> GetAllIncluding()
{
return StoreRepository.AllIncluding();
}
//Get store by id/id=5
public Store Find(long storeid)
{
stores = StoreRepository.Find(storeid);
return stores;
}
//Insert or Update Store
public void InsertorUpdateWithGraph(Store store)
{
StoreRepository.InsertOrUpdateWithGraph(store);
}
//Insert or Update StoreDetail
public void InsertOrUpdateStoreDetail(StoreDetail storedetail)
{
StoreRepository.InsertOrUpdateStoreDetail(storedetail);
}
//Get StoreDetail by id/id=5
public StoreDetail FindStoreDetail(long storedetailid)
{
storedetail = StoreRepository.FindStoreDetail(storedetailid);
return storedetail;
}
public List<StoreDetail> GetAllStoreDetails(long storedetailid)
{
List<StoreDetail> storedetails = StoreRepository.GetAllStoreDetails(storedetailid);
return storedetails;
}
public Sage FindSage(long sageid)
{
return StoreRepository.FindSage(sageid);
}
like this i may have more than two get,post,insert or update methods i have to acces this methods from my console application how can i map the methods i want,cana any one help me here how will i define the routes for this
You can have multiple "Get..." actions and you can get away without HttpGet attribute because they start with "Get". "Find..." methods need to be decorated with HttpGet
Those "Insert..." you need to decorate with HttpPost or HttpPut attributes.
Parameters to these methods can be configured in two ways. You can POST object like {id:"ddd",name:"nnn"} to action like
MyAction(int id, string name)
Web APi framework threats any methods that start with Post..., Delete..., Get..., Put... as corresponding Http Verbs. But you can name them the way you with and then decorate with Http attributes.
When it comes to parameters, it is about a correlation of your controller actions to the routes.
And now, to run it from the console application you can use HttpClient
string _webSiteUrl = "www.ffsdfds.com"
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(_webSiteUrl);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // for posting
HttpResponseMessage resp = httpClient.GetAsync("/api/area/getall").Result;
Has anyone been able to communicate using WCF on Windows Phone Series 7 emulator?
I've been trying for the past two days and it's just happening for me. I can get a normal Silverlight control to work in both Silverlight 3 and Silverlight 4, but not the phone version. Here are two versions that I've tried:
Version 1 - Using Async Pattern
BasicHttpBinding basicHttpBinding = new BasicHttpBinding();
EndpointAddress endpointAddress = new EndpointAddress("http://localhost/wcf/Authentication.svc");
Wcf.IAuthentication auth1 = new ChannelFactory<Wcf.IAuthentication>(basicHttpBinding, endpointAddress).CreateChannel(endpointAddress);
AsyncCallback callback = (result) =>
{
Action<string> write = (str) =>
{
this.Dispatcher.BeginInvoke(delegate
{
//Display something
});
};
try
{
Wcf.IAuthentication auth = result.AsyncState as Wcf.IAuthentication;
Wcf.AuthenticationResponse response = auth.EndLogin(result);
write(response.Success.ToString());
}
catch (Exception ex)
{
write(ex.Message);
System.Diagnostics.Debug.WriteLine(ex.Message);
}
};
auth1.BeginLogin("user0", "test0", callback, auth1);
This version breaks on this line:
Wcf.IAuthentication auth1 = new ChannelFactory<Wcf.IAuthentication>(basicHttpBinding, endpointAddress).CreateChannel(endpointAddress);
Throwing System.NotSupportedException. The exception is not very descriptive and the callstack is equally not very helpful:
at System.ServiceModel.DiagnosticUtility.ExceptionUtility.BuildMessage(Exception x)
at System.ServiceModel.DiagnosticUtility.ExceptionUtility.LogException(Exception x)
at System.ServiceModel.DiagnosticUtility.ExceptionUtility.ThrowHelperError(Exception e)
at System.ServiceModel.ChannelFactory`1.CreateChannel(EndpointAddress address)
at WindowsPhoneApplication2.MainPage.DoLogin()
....
Version 2 - Blocking WCF call
Here is the version that doesn't use the async pattern.
[System.ServiceModel.ServiceContract]
public interface IAuthentication
{
[System.ServiceModel.OperationContract]
AuthenticationResponse Login(string user, string password);
}
public class WcfClientBase<TChannel> : System.ServiceModel.ClientBase<TChannel> where TChannel : class {
public WcfClientBase(string name, bool streaming)
: base(GetBinding(streaming), GetEndpoint(name)) {
ClientCredentials.UserName.UserName = WcfConfig.UserName;
ClientCredentials.UserName.Password = WcfConfig.Password;
}
public WcfClientBase(string name) : this(name, false) {}
private static System.ServiceModel.Channels.Binding GetBinding(bool streaming) {
System.ServiceModel.BasicHttpBinding binding = new System.ServiceModel.BasicHttpBinding();
binding.MaxReceivedMessageSize = 1073741824;
if(streaming) {
//binding.TransferMode = System.ServiceModel.TransferMode.Streamed;
}
/*if(XXXURLXXX.StartsWith("https")) {
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
}*/
return binding;
}
private static System.ServiceModel.EndpointAddress GetEndpoint(string name) {
return new System.ServiceModel.EndpointAddress(WcfConfig.Endpoint + name + ".svc");
}
protected override TChannel CreateChannel()
{
throw new System.NotImplementedException();
}
}
auth.Login("test0", "password0");
This version crashes in System.ServiceModel.ClientBase<TChannel> constructor. The call stack is a bit different:
at System.Reflection.MethodInfo.get_ReturnParameter()
at System.ServiceModel.Description.ServiceReflector.HasNoDisposableParameters(MethodInfo methodInfo)
at System.ServiceModel.Description.TypeLoader.CreateOperationDescription(ContractDescription contractDescription, MethodInfo methodInfo, MessageDirection direction, ContractReflectionInfo reflectionInfo, ContractDescription declaringContract)
at System.ServiceModel.Description.TypeLoader.CreateOperationDescriptions(ContractDescription contractDescription, ContractReflectionInfo reflectionInfo, Type contractToGetMethodsFrom, ContractDescription declaringContract, MessageDirection direction)
at System.ServiceModel.Description.TypeLoader.CreateContractDescription(ServiceContractAttribute contractAttr, Type contractType, Type serviceType, ContractReflectionInfo& reflectionInfo, Object serviceImplementation)
at System.ServiceModel.Description.TypeLoader.LoadContractDescriptionHelper(Type contractType, Type serviceType, Object serviceImplementation)
at System.ServiceModel.Description.TypeLoader.LoadContractDescription(Type contractType)
at System.ServiceModel.ChannelFactory1.CreateDescription()
at System.ServiceModel.ChannelFactory.InitializeEndpoint(Binding binding, EndpointAddress address)
at System.ServiceModel.ChannelFactory1..ctor(Binding binding, EndpointAddress remoteAddress)
at System.ServiceModel.ClientBase1..ctor(Binding binding, EndpointAddress remoteAddress)
at Wcf.WcfClientBase1..ctor(String name, Boolean streaming)
at Wcf.WcfClientBase`1..ctor(String name)
at Wcf.AuthenticationClient..ctor()
at WindowsPhoneApplication2.MainPage.DoLogin()
...
Any ideas?
As scottmarlowe pointed out, the automagicly generated service refrence just works. I have set upon the mission to work out just why the bloody hell it works and the manual version doesn't.
I found the culprit and it is ChannelFactory. For some reason new ChannelFactory<T>().CreateChannel() just throws an exception. The only solution I found is to provide your own implementation of the channel. This involves:
Override ClientBase. (optional).
Override ClientBase.CreateChannel. (optional).
Subclass ChannelBase with a specific implementation of your WCF interface
Now, ClientBase already provides an instance of the channel factory thru ChannelFactory property. If you simply call CreateChannel off that you would get the same exception. You need to instantiate a channel that you define in step 3 from within CreateChannel.
This is the basic wireframe of how it all looks put together.
[DataContractAttribute]
public partial class AuthenticationResponse {
[DataMemberAttribute]
public bool Success {
get; set;
}
[System.ServiceModel.ServiceContract]
public interface IAuthentication
{
[System.ServiceModel.OperationContract(AsyncPattern = true)]
IAsyncResult BeginLogin(string user, string password, AsyncCallback callback, object state);
AuthenticationResponse EndLogin(IAsyncResult result);
}
public class AuthenticationClient : ClientBase<IAuthentication>, IAuthentication {
public AuthenticationClient(System.ServiceModel.Channels.Binding b, EndpointAddress ea):base(b,ea)
{
}
public IAsyncResult BeginLogin(string user, string password, AsyncCallback callback, object asyncState)
{
return base.Channel.BeginLogin(user, password, callback, asyncState);
}
public AuthenticationResponse EndLogin(IAsyncResult result)
{
return Channel.EndLogin(result: result);
}
protected override IAuthentication CreateChannel()
{
return new AuthenticationChannel(this);
}
private class AuthenticationChannel : ChannelBase<IAuthentication>, IAuthentication
{
public AuthenticationChannel(System.ServiceModel.ClientBase<IAuthentication> client)
: base(client)
{
}
public System.IAsyncResult BeginLogin(string user, string password, System.AsyncCallback callback, object asyncState)
{
object[] _args = new object[2];
_args[0] = user;
_args[1] = password;
System.IAsyncResult _result = base.BeginInvoke("Login", _args, callback, asyncState);
return _result;
}
public AuthenticationResponse EndLogin(System.IAsyncResult result)
{
object[] _args = new object[0];
AuthenticationResponse _result = ((AuthenticationResponse)(base.EndInvoke("Login", _args, result)));
return _result;
}
}
}
TLDR; If you want to use your own WCF code on WP7 you need to create your own channel class and not rely on ChannelFactory.
Dynamic proxy creation using ChannelFactory.CreateChannel() is not supported on Windows Phone. This is documented here - http://msdn.microsoft.com/en-us/library/ff426930(VS.96).aspx
Consuming a service using the 'Add service reference' mechanism in a async pattern would be the correct way to do.
I put a blog post together on this very subject: http://blogs.msdn.com/b/andypennell/archive/2010/09/20/using-wcf-on-windows-phone-7-walk-through.aspx
I haven't had any problems, but I went the "add service reference..." route which I had to do via "VS2010 Express for Windows Phone" b/c VS2010 RC doesn't yet support that feature for WP7 development. The Express version comes with the WP7 Developer's install.