In a generated Service Reference (imported from a WSDL), I have the following methods in the Client class, in the Reference.cs:
public Namespace.Service.SalesOrderDetail newService(Namespace.Service.Contact orderContact, Namespace.Service.Contact installationContact, string customerReference, Namespace.Service.ServiceDetails[] serviceDetailsList) {
Namespace.Service.newServiceRequest inValue = new Namespace.Service.newServiceRequest();
inValue.orderContact = orderContact;
inValue.installationContact = installationContact;
inValue.customerReference = customerReference;
inValue.serviceDetailsList = serviceDetailsList;
Namespace.Service.newServiceResponse retVal = ((Namespace.Service.ServiceRequestPortType)(this)).newService(inValue);
return retVal.salesOrder;
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
System.Threading.Tasks.Task<Namespace.Service.newServiceResponse> Namespace.Service.ServiceRequestPortType.newServiceAsync(Namespace.Service.newServiceRequest request) {
return base.Channel.newServiceAsync(request);
}
public System.Threading.Tasks.Task<Namespace.Service.newServiceResponse> newServiceAsync(Namespace.Service.Contact orderContact, Namespace.Service.Contact installationContact, string customerReference, Namespace.Service.ServiceDetails[] serviceDetailsList) {
Namespace.Service.newServiceRequest inValue = new Namespace.Service.newServiceRequest();
inValue.orderContact = orderContact;
inValue.installationContact = installationContact;
inValue.customerReference = customerReference;
inValue.serviceDetailsList = serviceDetailsList;
return ((Namespace.Service.ServiceRequestPortType)(this)).newServiceAsync(inValue);
}
I've seen Python code that uses the same WSDL, and it is able to access the method as response = client.newService(request).
I'd also like to access the method in that fashion, albeit var task = client.newService(request); Task.WaitAll(task); var response = task.Result;, but I can't seem to find the right combo of creating the service reference, without being forced to have expanded input parameters to the service.
Is there a magic combo for Service Reference creation that will allow me to just pass the request as a single object?
I'm not fussed on keeping the async functionality.
The client of a service implements the interface that represents the service. It just so happens, and is shown in this example, that it doesn't necessarily make all those implemented method public.
So, to get around this, if I cast the client object to the service interface, I get to call the service as intended, regardless of what the client has made public.
var client = new ServiceClient();
var service = (Service)client;
var request = new newServiceRequest() { ... };
var response = service.newService(request);
client.Close();
Related
I made API that downloads data from a shop database and compares it to data in SQL.
However to not mess up things we are testing new things on separate website that connects via different address and use different reference with methods + connectedservice.json.
There are 2 namespaces, A (live) and B (test).
Would like to have simple solution to switch between versions without duplicating code.
Thing i am aiming at is way to pass 3 variables
await DDD.Api.Commands.UpdateOrders(client, _auth, _message, "2");
where client,auth,message can be from live version or test version
My original code:
Class Application
//TestAtomstore.AtomApiServicePortTypeClient client = new //TestAtomstore.AtomApiServicePortTypeClient();
//TestAtomstore.message _message = new ProdAtomstore.message();
//TestAtomstore.auth _auth = new ProdAtomstore.auth();
//_auth.login = Settings1.Default.TESTUsername;
//_auth.password = Settings1.Default.TESTPassword;
ProdAtomstore.AtomApiServicePortTypeClient client = new ProdAtomstore.AtomApiServicePortTypeClient();
ProdAtomstore.message _message = new ProdAtomstore.message();
ProdAtomstore.auth _auth = new ProdAtomstore.auth();
_auth.login = Settings1.Default.PRODUsername;
_auth.password = Settings1.Default.PRODPassword;
await DDD.Atomstore.Api.Commands.UpdateOrders(client, _auth, _message, "2");
Class Commands
public static async Task UpdateOrders(
ProdAtomstore.AtomApiServicePortTypeClient client,
ProdAtomstore.auth _auth,
ProdAtomstore.message _message,
string tmpStr)
{
string result = await client.GetOrdersAsync(_auth);
etc...
}
What i want to do is to pass a variable which can be ProdAtomstore or TestAtomstore.
ProdAtomstore.AtomApiServicePortTypeClient client = new ProdAtomstore.AtomApiServicePortTypeClient();
ProdAtomstore.message _message = new ProdAtomstore.message();
ProdAtomstore.auth _auth = new ProdAtomstore.auth();
or
TestAtomstore.AtomApiServicePortTypeClient client = new TestAtomstore.AtomApiServicePortTypeClient();
TestAtomstore.message _message = new ProdAtomstore.message();
TestAtomstore.auth _auth = new ProdAtomstore.auth();
I was trying to pass it as a generic type where
public static async Task UpdateOrders<C,A,M>(C client, A _auth, M _message, string str)
where C : ProdAtomstore.AtomApiServicePortTypeClient, TestAtomstore.AtomApiServicePortTypeClient
where A : ProdAtomstore.AtomApiServicePortTypeClient, TestAtomstore.AtomApiServicePortTypeClient
where M : ProdAtomstore.AtomApiServicePortTypeClient, TestAtomstore.AtomApiServicePortTypeClient
I want await DDD.Atomstore.Api.Commands.UpdateOrders(client, _auth, _message, "2"); to accept client,auth,message from ProdAtomstore and TestAtomstore.
Maybe this explanation will help a little.
This sounds like a classic example of when to use dependency injection and a factory pattern.
For example (pseudo code):
interface IDataLayer
{
List<SomeData> GetData();
}
class DataLayerA :IDataLayer
{
string authData = "xxxxx"; //auth data specific to this data type
List<SomeData> GetData()
{
List<SomeData> data = SomeApiWebCall(authData);
return data
}
}
class DataLayerB :IDataLayer
{
string authData = "yyyyy"; //auth data specific to this data type
List<SomeData> GetData()
{
List<SomeDifferentData> data =GetFromAnotherWebAPIOne(authData);
List<SomeMoreData> moreData = GetFromAnotherWebAPITwo(auth);
List<SomeData> convertedData = ConvertData(data, moreData);
return convertedData;
}
}
Your main code uses IDataLayer variable not DataLayerA or DataLayerB.
You then use a class factory to create your concrete instance of either DataLayerA or DataLayerB at run time and inject it into your code.
This will mean inserting another layer to remove your current code further away from its data source.
I am using EasyNetQ and need to retry failed messages on the original queue. The problem is: even though I successfully increment the TriedCount variable (in the body of every msg), when EasyNetQ publishes the message to the default error queue after an exception, the updated TriedCount is not in the msg! Presumably because it just dumps the original message to the error queue without the consumer's changes.
The updated TriedCount works for in-process republishes, but not when republished through EasyNetQ Hosepipe or EasyNetQ Management Client. The text files Hosepipe generates do not have the TriedCount updated.
public interface IMsgHandler<T> where T: class, IMessageType
{
Task InvokeMsgCallbackFunc(T msg);
Func<T, Task> MsgCallbackFunc { get; set; }
bool IsTryValid(T msg, string refSubscriptionId); // Calls callback only
// if Retry is valid
}
public interface IMessageType
{
int MsgTypeId { get; }
Dictionary<string, TryInfo> MsgTryInfo {get; set;}
}
public class TryInfo
{
public int TriedCount { get; set; }
/*Other information regarding msg attempt*/
}
public bool SubscribeAsync<T>(Func<T, Task> eventHandler, string subscriptionId)
{
IMsgHandler<T> currMsgHandler = new MsgHandler<T>(eventHandler, subscriptionId);
// Using the msgHandler allows to add a mediator between EasyNetQ and the actual callback function
// The mediator can transmit the retried msg or choose to ignore it
return _defaultBus.SubscribeAsync<T>(subscriptionId, currMsgHandler.InvokeMsgCallbackFunc).Queue != null;
}
I have also tried republishing myself through the Management API (rough code):
var client = new ManagementClient("http://localhost", "guest", "guest");
var vhost = client.GetVhostAsync("/").Result;
var errQueue = client.GetQueueAsync("EasyNetQ_Default_Error_Queue",
vhost).Result;
var crit = new GetMessagesCriteria(long.MaxValue,
Ackmodes.ack_requeue_true);
var errMsgs = client.GetMessagesFromQueueAsync(errQueue,
crit).Result;
foreach (var errMsg in errMsgs)
{
var pubRes = client.PublishAsync(client.GetExchangeAsync(errMsg.Exchange, vhost).Result,
new PublishInfo(errMsg.RoutingKey, errMsg.Payload)).Result;
}
This works but only publishes to the error queue again, not on the original queue. Also, I don't know how to add/update the retry information in the body of the message at this stage.
I have explored this library to add headers to the message but I don't see if the count in the body is not being updated, how/why would the count in the header be updated.
Is there any way to persist the TriedCount without resorting to the Advanced bus (in which case I might use the RabbitMQ .Net client itself)?
Just in case it helps someone else, I eventually implemented my own IErrorMessageSerializer (as opposed to implementing the whole IConsumerErrorStrategy, which seemed like an overkill). The reason I am adding the retry info in the body (instead of the header) is that EasyNetQ doesn't handle complex types in the header (not out-of-the-box anyway). So, using a dictionary gives more control for different consumers. I register the custom serializer at the time of creating the bus like so:
_defaultBus = RabbitHutch.CreateBus(currentConnString, serviceRegister => serviceRegister.Register<IErrorMessageSerializer>(serviceProvider => new RetryEnabledErrorMessageSerializer<IMessageType>(givenSubscriptionId)));
And just implemented the Serialize method like so:
public class RetryEnabledErrorMessageSerializer<T> : IErrorMessageSerializer where T : class, IMessageType
{
public string Serialize(byte[] messageBody)
{
string stringifiedMsgBody = Encoding.UTF8.GetString(messageBody);
var objectifiedMsgBody = JObject.Parse(stringifiedMsgBody);
// Add/update RetryInformation into objectifiedMsgBody here
// I have a dictionary that saves <key:consumerId, val: TryInfoObj>
return JsonConvert.SerializeObject(objectifiedMsgBody);
}
}
The actual retrying is done by a simple console app/windows service periodically via the EasyNetQ Management API:
var client = new ManagementClient(AppConfig.BaseAddress, AppConfig.RabbitUsername, AppConfig.RabbitPassword);
var vhost = client.GetVhostAsync("/").Result;
var aliveRes = client.IsAliveAsync(vhost).Result;
var errQueue = client.GetQueueAsync(Constants.EasyNetQErrorQueueName, vhost).Result;
var crit = new GetMessagesCriteria(long.MaxValue, Ackmodes.ack_requeue_false);
var errMsgs = client.GetMessagesFromQueueAsync(errQueue, crit).Result;
foreach (var errMsg in errMsgs)
{
var innerMsg = JsonConvert.DeserializeObject<Error>(errMsg.Payload);
var pubInfo = new PublishInfo(innerMsg.RoutingKey, innerMsg.Message);
pubInfo.Properties.Add("type", innerMsg.BasicProperties.Type);
pubInfo.Properties.Add("correlation_id", innerMsg.BasicProperties.CorrelationId);
pubInfo.Properties.Add("delivery_mode", innerMsg.BasicProperties.DeliveryMode);
var pubRes = client.PublishAsync(client.GetExchangeAsync(innerMsg.Exchange, vhost).Result,
pubInfo).Result;
}
Whether retry is enabled or not is known by my consumer itself, giving it more control so it can choose to handle the retried msg or just ignore it. Once ignored, the msg will obviously not be tried again; that's how EasyNetQ works.
I'd like to use a web service from a database to gather informations. Right now, I implemented to web service, turned it into a proxy class via wsdl.exe but I'm slightly irritated by the outcome. The normal way to call that class is new object -> method -> parameters ->happiness. This thing only consists of partial classes and wants strange parameters. I'm not even sure if I got the right method to get the wanted information.
This seems to be the needed method:
public UniProtId2DomainIdsRecordType[] UniProtId2DomainIds (UniProtId2DomainIdsRequestRecordType UniProtId2DomainIdsRequestRecord)
{
object[] results = this.Invoke("UniProtId2DomainIds", new object[] {
UniProtId2DomainIdsRequestRecord});
return ((UniProtId2DomainIdsRecordType[])(results[0]));
}
This seems to be one of the needed classes:
public partial class UniProtId2DomainIdsRequestRecordType
{
private string uniprot_accField;
/// <remarks/>
public string uniprot_acc
{
get
{
return this.uniprot_accField;
}
set
{
this.uniprot_accField = value;
}
}
}
(That's the whole class, generated by wsdl.exe -> https://www.dropbox.com/s/yg909ibdq02js5a/GetCath.cs)
But as soon as I try to use it as I think it should work... well... my experiments on this (none of them working):
UniProtId2DomainIdsRequestRecordType Uni2Cath = new UniProtId2DomainIdsRequestRecordType();
Uni2Cath.uniprot_acc = "P0A7N9";
UniProtId2DomainIdsRecordType[] UniProtId2DomainIds;
UniProtId2DomainIdsRecordType test = new UniProtId2DomainIdsRecordType();
test.uniprot_acc = "P0A7N9";
UniProtId2DomainIdsRecordType[] UniProtId2DomainIds(test);
All I need is to get a string like P0A7N9 to be passed to the server.
(The reference to this webservice: http://api.cathdb.info/api/soap/dataservices/wsdl#op.o159501052 )
Can someone give me a hint how to handle this, please?
The easiest way would be to add this web service as Service Reference to your project. Then you can call the different methods. Use this as the address: http://api.cathdb.info/api/soap/dataservices/wsdl
using (var ser = new DataServicesPortTypeClient())
{
var results = ser.UniProtId2DomainIds(new UniProtId2DomainIdsRequestRecordType
{
uniprot_acc = "P0A7N9"
});
if (results != null)
{
var geneName = results.gene_name;
var speciesName = results.species_name;
}
}
If you want to use your generated class do this:
using (var service = new DataServices())
{
var results = service.UniProtId2DomainIds(new UniProtId2DomainIdsRequestRecordType
{
uniprot_acc = "P0A7N9"
});
if (results != null && results.Length >0)
{
var geneName = results[0].gene_name;
var speciesName = results[0].species_name;
}
}
As John suggested in the comments, ASMX and wsdl.exe are deprecated technologies. You should be using Service References and svcutil.exe
I am using Silverlight with WCF RIA Services.
There is a class in my entity model called Activation. It has properties: Code1 and Code2 along with other properties.
On my silverlight client I need to send an Activation to the server where it picks out values from objects associated with it and populates the Code1 and Code1 attributes. E.g:
Public Sub ServerMethod(ByRef myActivation as Activation)
Dim x as Integer = myActivation.Licence.NumberOfDays
Dim y as Integer = myActivation.Product.ProductSeed
myActivation.Code1 = GetCode1(x,y)
myActivation.Code2 = GetCode2(x,y)
End Sub
Note that the activation codes are not persisted to the database, they simply go back to the client where the user can decide to save if they like from there.
What is the best way to achieve this using WCF RIA Services? At first I thought a named update in the domain service might do the job but there seems to be no Async callback for that.
Any ideas would be greatly appreciated!
It's exactly what the InvokeAttribute is meant for, just put it on your "ServerMethod". About the Async, every single call in wcf ria services is asynchronous and you have to supply a callback to the method if you want to be notified.
EDIT:
I didn't see in your question that you need to pass "Association" properties along the wire. In that case a NamedUpdate, though semantically incorrect, could be easier. Just remember that your context has to be "clean" or you'll submit unintended changes to the server (remember that you have to call the SubmitChanges on the DomainContext).
In case you prefer to use the InvokeAttribute, (and this is the way I'd go) then, yes, as you pointed out, return the "updated" entity to the client and to workaround the problem with the association, use Serialization on your own, i.e ,Serialize your entity and send it to the server, than Deserialize server side and serialize it again before return it to the client, where you'll finally deserialize it.
I'm attaching a piece of code that I use both server and client side that I use with this purpose.
public static class Serialization
{
public static string Serialize<T>(T obj)
{
//Create a stream to serialize the object to.
var ms = new MemoryStream();
// Serializer the User object to the stream.
var ser = new DataContractSerializer(typeof (T));
ser.WriteObject(ms, obj);
byte[] array = ms.ToArray();
ms.Close();
return Encoding.UTF8.GetString(array, 0, array.Length);
}
public static T Deserialize<T>(string obj) where T : class
{
if (obj == null)
return null;
var serializer = new DataContractSerializer(typeof (T));
var stream = new MemoryStream(Encoding.UTF8.GetBytes(obj));
var result = serializer.ReadObject(stream) as T;
return result;
}
}
HTH
If I use code like this [just below] to add Message Headers to my OperationContext, will all future out-going messages contain that data on any new ClientProxy defined from the same "run" of my application?
The objective, is to pass a parameter or two to each OpeartionContract w/out messing with the signature of the OperationContract, since the parameters being passed will be consistant for all requests for a given run of my client application.
public void DoSomeStuff()
{
var proxy = new MyServiceClient();
Guid myToken = Guid.NewGuid();
MessageHeader<Guid> mhg = new MessageHeader<Guid>(myToken);
MessageHeader untyped = mhg.GetUntypedHeader("token", "ns");
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
proxy.DoOperation(...);
}
public void DoSomeOTHERStuff()
{
var proxy = new MyServiceClient();
Guid myToken = Guid.NewGuid();
MessageHeader<Guid> mhg = new MessageHeader<Guid>(myToken);
MessageHeader untyped = mhg.GetUntypedHeader("token", "ns");
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
proxy.DoOtherOperation(...);
}
In other words, is it safe to refactor the above code like this?
bool isSetup = false;
public void SetupMessageHeader()
{
if(isSetup) { return; }
Guid myToken = Guid.NewGuid();
MessageHeader<Guid> mhg = new MessageHeader<Guid>(myToken);
MessageHeader untyped = mhg.GetUntypedHeader("token", "ns");
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
isSetup = true;
}
public void DoSomeStuff()
{
var proxy = new MyServiceClient();
SetupMessageHeader();
proxy.DoOperation(...);
}
public void DoSomeOTHERStuff()
{
var proxy = new MyServiceClient();
SetupMessageHeader();
proxy.DoOtherOperation(...);
}
Since I don't really understand what's happening there, I don't want to cargo cult it and just change it and let it fly if it works, I'd like to hear your thoughts on if it is OK or not.
I think your refactored code doesn't put any added-value. Have you taken in account that the OperationContext can be null?
I think this will be a safer approach:
using(OperationContextScope contextScope =
new OperationContextScope(proxy.InnerChannel))
{
.....
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
proxy.DoOperation(...);
}
OperationContextScope's constructor will always cause replacement of the Operation context of the current thread; The OperationContextScope's Dispose method is called which restores the old context preventing problems with other objects on the same thread.
I believe your OperationContext is going to get wiped each time you new the proxy.
You should plan on adding the custom message headers prior to each call. This is good practice in any case as you should prefer per call services and close the channel after each call.
There are a couple patterns for managing custom headers.
You can create the header as part of the constructor to the proxy.
Alternatively, you can extend the binding with a behavior that automatically adds the custom header prior to making each call. This is a good example: http://weblogs.asp.net/avnerk...