Can I Create a Web Service that has Properties? - c#

I'm trying to test code around a web service that is not available yet. I'm trying to dummy up my own version. According to the specs it will be called like this.
var service = new Service();
service.SD = new ServiceData();
service.SD.ID = "ABC123";
service.SD.Auth = "00000";
string result = service.DoMyThing();
This is the closest I've gotten.
var service = new Service();
service.set_SD(new ServiceData());
service.get_SD().ID = "ABC123";
service.get_SD().Auth = "00000";
service.DoMyThing();
The problem is with the SD property. How do I write the service so that Visual Studio 2008 generates the web reference correctly?
Here is my current dummy web service code.
public class Service : System.Web.Services.WebService
{
// This doesn't show up in the generated proxy at all
public static ServiceData SDTest;
// For extra credit explain why this needs to be static for it to work
private static ServiceData _sd;
public ServiceData SD
{
[WebMethod(EnableSession = true)]
get { return _sd; }
[WebMethod(EnableSession = true)]
set { _sd = value; }
}
[WebMethod]
public string DoMyThing()
{
// Presumably the real service accesses SD in here
return "";
}
}
public class ServiceData
{
public string ID { get; set; }
public string Auth { get; set; }
}

Your design is flawed. Web services are not meant to have properties. They should only expose methods, the reason being that the HTTP protocol is stateless (and web services assume this too), so exposing a property doesn't make sense unless you want it to apply to all callers of the instance (and still, even in that situation, it doesn't make sense to expose it).
Rather, what you want to do is have the DoMyThing method take the instance of ServiceData (if required) and operate on that, returning the appropriate result set.
If you really have a need to expose properties of the service, you would have a GetProperties method (or something like that) which takes no parameters and returns the appropriate data structure with the service information.

I'm with casperOne on this. I think using fakie properties are more annoying than useful.
Still, if you're married to this just eliminate the getter for the property. You don't need it. Do this instead:
var service = new Service();
ServiceData sd = new ServiceData();
sd.ID = "ABC123";
sd.Auth = "00000";
service.SD = sd;
string result = service.DoMyThing();
If Visual Studio still names the setter property incorrectly you can use one of the soap attributes to rename it.
EDIT: You'll also need to define SD as a SOAP Header.

You can't do this, so don't try to "fake it". The best you can do is:
var service = new Service();
ServiceData sd = new ServiceData();
sd.ID = "ABC123";
sd.Auth = "00000";
string result = service.DoMyThing(sd);

For those that may be interested.
This more accurately reflects the spec than my sanitized version above (I didn't know "TypeNameValue" was a key clue, sorry!).
var service = new Service();
service.ServiceDetailsValue = new ServiceDetails();
service.ServiceDetailsValue.ID = "ABC123";
service.ServiceDetailsValue.Auth = "00000";
string result = service.DoMyThing();
And this is the dummy web service code that works.
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(Name="TestService", ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
public ServiceDetails SDTest;
[WebMethod]
[SoapDocumentMethod(Binding = "TestService")]
[SoapHeader("SDTest", Required = true)]
public string DoMyThing()
{
return "";
}
}
public class ServiceDetails : System.Web.Services.Protocols.SoapHeader
{
public string ID { get; set; }
public string Auth { get; set; }
}

Related

C# Assigning default value to System.Uri property

I have a project which is about RESTful API and I have as a property the basic URL, on which I need to add parts each time (that's in my methods). I need (a) to declare the default (unchanged) path, and then (b) some help on how do I add to the URL. Example:
public partial class APIParamaters
{
public System.Uri URL { get; set; } = (System.Uri) "http://192.100.106.657:8811/some/part/here/version1/api"; //throws error !!!
}
This is throwing an error and I don't know how to correct.
Also, how do I later add to the URL, for example, I am trying
class MyTest
{
public string SpecialPart = "Excellent";
public APIParamaters myParams = new APIParamaters
{
URL = URL + SpecialPart + "FirstCall", //trying to do: "http://192.100.106.657:8811/some/part/here/version1/api/Excellent/FirstCall"
SomethingElse = "Ok"
//etc..
};
}
The following code means (cast this string as System.Uri) but string can not be cast as System.Uri:
(System.Uri) "http://192.100.106.657:8811/some/part/here/version1/api";
You should instantiate System.Uri:
public System.Uri URL { get; set; } = new System.Uri("http://192.100.106.657:8811/some/part/here/version1/api");

C# How to get/set data from asmx Web Service

I'm using VisualStudio2013. Its important to note for readers that the code which this asmx is derived from works perfectly but I do not know how to use the asmx WebService. I downloaded the whole nine yards from here https://sourceforge.net/projects/shorturl-dotnet/
I cannot figure out how to get/set properties of the following CreateUrl() WebMethod. I want to learn how to use the entire WebService but started here.
In the example that follows I send a URL to the CreateURL() method which will shorten the URL and perform other tasks; I do not know how to get properties from the returned ShortUrl.Container Class: I have not been successful accessing the data after the class(es) are returned to my calling method.
// WebMethod
public class API : System.Web.Services.WebService {
[WebMethod]
public ShortUrl.Container CreateUrl(string real_url)
{
ShortUrl.Container oShortUrl = new ShortUrl.Container();
oShortUrl.RealUrl = real_url;
oShortUrl.ShortenedUrl = ShortUrl.Utils.UniqueShortUrl();
oShortUrl.CreateDate = DateTime.Now;
oShortUrl.CreatedBy = HttpContext.Current.Request.UserHostAddress;
ShortUrl.Utils.AddUrlToDatabase(oShortUrl);
oShortUrl.ShortenedUrl = ShortUrl.Utils.PublicShortUrl(oShortUrl.ShortenedUrl);
return oShortUrl;
}
}
// ShortUrl.Container class returned as oShortUrl
namespace ShortUrl
{
/// <summary>
/// Container for the ShortURL object
/// </summary>
public class Container
{
private string _real_url;
private string _short_url;
private DateTime _create_date;
private string _created_by;
public Container()
{
this.CreateDate = DateTime.Now;
this.CreatedBy = "tap";
this.RealUrl = null;
this.ShortenedUrl = "Unknown";
}
public string RealUrl
{
get { return _real_url; }
set { _real_url = value; }
}
public string ShortenedUrl
{
get { return _short_url; }
set { _short_url = value; }
}
public DateTime CreateDate
{
get { return _create_date; }
set { _create_date = value; }
}
public string CreatedBy
{
get { return _created_by; }
set { _created_by = value; }
}
}
}
In VS2013 I add the Service Reference to point to http://tap.tools.api.asmx as the service endpoint and name the VS2013 reference as ShortenUrl. VS2013 generates the APISoapClient and Container classes.
// get/set properties of the ShortUrl.Container class
// by means of APISoapClient
ShortenUrl.APISoapClient u = new ShortenUrl.APISoapClient();
u.CreateUrl("http://clintongallagher.com/tag-target-url.html");
// get/set properties of the ShortUrl.Container class
// by means of Container class
ShortenUrl.Container c = new ShortenUrl.Container();
string url = c.RealUrl;
I'm not getting anywhere with either and I think my problem is the instance of the oShortUrl object instantiated within the public ShortUrl.Container CreateUrl(string real_url) method. I do not know how to get any of the properties from that instance of oShortUrl the Container class returns to my methods.
// oShortUrl
ShortUrl.Container oShortUrl = new ShortUrl.Container();
Odd as it may sound as old and outdated the use of asmx happens to be I never worked with -any- WebServices yet which explains why I am weak and throw myself to the mercy of the court.
// EDIT: 2016-07-19 ~2:41pm
VS2013 generated several classes from the WSDL two of which appear to be useful as seen in Intellisense...
// class APISoapClient and class Container
When I use a local variable with APISoapClient a shortened URL is generated as I can see using SQL Management Studio and note all of the data is properly generated but I am not able to get/set on any other WebMethods or properties with to get/set data...
// Exposes two WebMethods: CreateUrl and GetUrl
ShortenUrl.APISoapClient u = new ShortenUrl.APISoapClient();
// Does generate the shortened URL
u.CreateUrl("http://clintongallagher.com/tag-target-url.html");
// Should return the URL that was shortened but doesn't
u.GetUrl("i2Z5H");
And...
// Exposes the properties in Intellisense but does not return data
ShortenUrl.Container c = new ShortenUrl.Container();
// returns 1/1/0001 12:00:00 AM
lblCreateDate.Text = "CreateDate: " + c.CreateDate.ToString();
// returns nothing
lblCreatedBy.Text = "CreatedBy: " + c.CreatedBy;
// returns nothing
lblRealUrl.Text = "RealUrl: " + c.RealUrl;
// returns ShortenUrl.Container
lblShortenedUrl.Text = "ShortenedUrl: " + u.GetUrl("i2Z5H");
If i understood what you're trying to get is the Container returned from the Web Method. If so then just create a variable type of Container and assign the method call to it. Like ShortUrl.Container c = u.CreateUrl(...) then from c you can get the values you're looking for.
Think about this #clintongallagher. When you do the following call,
ShortenUrl.APISoapClient u = new ShortenUrl.APISoapClient();
u.CreateUrl("http://clintongallagher.com/tag-target-url.html");
[WebMethod]
public ShortUrl.Container CreateUrl(string real_url)
{
ShortUrl.Container oShortUrl = new ShortUrl.Container();
oShortUrl.RealUrl = real_url;
//here you're assigning a value to this object, let's say 'A'
oShortUrl.ShortenedUrl = ShortUrl.Utils.UniqueShortUrl();
oShortUrl.CreateDate = DateTime.Now;
oShortUrl.CreatedBy = HttpContext.Current.Request.UserHostAddress;
//then here you're saving the object with the Shortened value 'A' you just got
ShortUrl.Utils.AddUrlToDatabase(oShortUrl);
/*
*finally you're replacing the Shortened value with another value,
*let's say 'B', which is the object you're going to return*/
oShortUrl.ShortenedUrl = ShortUrl.Utils.PublicShortUrl(oShortUrl.ShortenedUrl);
return oShortUrl;
}
I don't know how does GetUrl(shortened_value) is supposed to work but, assuming it will get from the DB the shortened_value passed in, of course the result would not be the same since the shortened value saved was 'A' and your asking for B.

The underlying connection was closed: The connection was closed unexpectedly. Data is too big?

I have got a server/client relationship where the client pulls an ArrayList from the server. If I set the server to always send an empty ArrayList then I don't get this error. So clearly the problem is that my data is too big for the connection and that it's closing before all the data can get through.
I have looked into this issue and I have added the features suggested by this question/answer:
https://stackoverflow.com/a/285542/3036134 Many solutions suggest the same thing.
I believe that I have implemented something incorrectly (I think it's most likely the Service Behaviour MaxItemsInObjectGraph as I am still getting the same error. I unfortunately can't figure out what's wrong with it though. Here's my code:
The error I'm receiving:
CommunicationException was unhandled. The underlying connection was closed: The connection was closed unexpectedly.
My WCF Service Code:
[ServiceContract]
public interface IModelData
{
[OperationContract]
ArrayList GetData();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, MaxItemsInObjectGraph = 2147483647)]
public class ModelDataClient
{
ChannelFactory<IModelData> HttpFactory;
IModelData HttpProxy;
public ModelDataClient()
{
HttpFactory = new ChannelFactory<IModelData>(
new BasicHttpBinding(),
new EndpointAddress("http://localhost:8000/ModelData"));
HttpProxy = HttpFactory.CreateChannel();
}
public ArrayList GetData()
{
return HttpProxy.GetData();
}
}
[ServiceBehavior(UseSynchronizationContext = false, InstanceContextMode = InstanceContextMode.Single, MaxItemsInObjectGraph = 2147483647)]
public class ModelDataServer : IModelData
{
public delegate ArrayList GetData();
public GetData _GetData { get; set; }
public ModelDataServer()
{
}
public ArrayList GetData()
{
return _GetData();
}
}
My Client Side Code:
public partial class MainForm : Form
{
private ModelDataClient Client;
public MainForm()
{
InitializeComponent();
Client = new ModelDataClient();
}
private void Refresh()
{
ArrayList dataList = Client.GetData();
// ********** ERROR POINTS TO LINE ABOVE!!!!!!!!!!!!!!!!!!!!
// do something with datalist
}
}
My Server Side Code:
public partial class ScraperForm : Form
{
ServiceHost Host;
ModelDataServer DataServer;
ArrayList Data;
public ScraperForm()
{
InitializeComponent();
#region Start Data Server
DataServer = new ModelDataServer();
DataServer._GetData = new ModelDataServer.GetData(this.GetData);
BasicHttpBinding bhttpb = new BasicHttpBinding();
bhttpb.MaxBufferSize = 2147483647;
bhttpb.MaxReceivedMessageSize = 2147483647;
bhttpb.ReaderQuotas.MaxDepth = 32;
bhttpb.ReaderQuotas.MaxStringContentLength = 8388608;
bhttpb.ReaderQuotas.MaxArrayLength = 16384;
bhttpb.ReaderQuotas.MaxBytesPerRead = 4096;
bhttpb.ReaderQuotas.MaxNameTableCharCount = 16384;
Host = new ServiceHost(DataServer, new Uri[]
{
new Uri("http://localhost:8000")
});
Host.AddServiceEndpoint(typeof(IModelData),
bhttpb,
"ModelData");
Host.Open();
Data = new ArrayList();
}
private void CloseSever()
{
Host.Close();
}
public void UpdateData() // Run on a timer
{
ArrayList Data = new ArrayList()
// Update Data
}
public ArrayList GetData() // This is called by server which is called by client
{
return Data; // no error if I return new ArrayList();
}
}
EDIT: Would the problem be being caused by not having DataContract/DataMembers?
UPDATE
I have rebuilt my new implementation from the ground up using this tutorial (and the related ones): http://blogs.msdn.com/b/brunoterkaly/archive/2013/10/28/wcf-programming-how-to-write-a-client-app-that-connects-to-a-wcf-service.aspx (For anyone interested).
Instead of using an ArrayList (lots of unboxing) and a Typed List (comes out as an array if used with WCF), I have instead opted to pass my data using a string with the following format:
"~" to denote the start of a new member of the list
"," to denote the end of one of the datatypes in my custom one.
So it might look like "~NAME,1.29,1,4,123.1~NAME,1.23,3,1,13.2" etc. I would suggest people who want to use lists use this instead.
I have run into a new problem with my new implementation, likely the same/similar problem. Please see my new question: Object reference not set to an instance of an object - WCF Service and Delegates (WCF Hosted before Delegate is instantiated)
Thanks for your help everyone.
What is your client-side configured as? You've displayed your server-side configuration, but don't forget that the client-side has it's own configuration settings.
Looking at your server-side configuration, it appears that the violation is occurring on the reception of the data on the client.
See here for an example. You can also do this programmatically as well.
Edit
Now I see in the comments that this ArrayList you are getting from the server contains your own user-defined type RFData. I believe now that this is likely the source of your problem.
Data Contracts describe the data that is being exchanged. Data Contracts are used between the Client and Server to serialize and de-serialize data that is being sent over the wire. You need to use Data Contracts/ Data Members when you are defining your own type to be used within the WCF model. Primitives as well as many of the built-in .NET types already have Data Contracts.
For your RFData type it would be something like this:
// Apply the DataContract to the type
[DataContract]
public class RFData
{
// Apply the DataMemberAttribute to the various properties
[DataMember]
public double RFDouble { get; set; }
[DataMember]
public int RFInt { get; set; }
[DataMember]
public string RFString { get; set; }
}
I know you have several integers and doubles, but you get the gist. Here is a really helpful guide on Data Contracts from MSDN.

ServiceStack: JsonServiceClient usage without IReturn in DTO

What I would like to do is the following:
var client = new JsonServiceClient(ServiceUrl);
var request = new FooQuery {Id = 1};
IEnumerable<Project> response = client.Get(request);
However, my FooQuery doesn't implement any IReturn, and I'd like it not to (it's in a library without ServiceStack references). Here's my service side:
Library of business objects:
public class ProjectQuery
{
public int Id { get; set; }
}
AppHost:
Routes.Add<ProjectQuery>("/project", "GET");
Service:
public object Get(Foo request)
{
// do stuff.
}
Is there some nice, clean way to create the JsonServiceClient without using the IReturn interface on my business object?
Looks like there's no way not to use IReturn if you don't want to provide a URL to the JsonServiceClient Get() requests. Just decided to create another set of DTOs in my ServiceStack implementation, that are essentially mirrors of the real DTOs in another library. Then when a request comes in to my SS DTO, I create the other library's DTO, set each property, and pass it along.
Not pretty, but that's the best I could find so far.
I had the same problem using IReturn and Routes, as I wanted to use the DTOs
in assemblies with business logic, without ServiceStack references.
It worked for me, using in the Client Model
public class TestRequest
{
public int vendorId {get; set; }
public string barcode {get; set; }
public string username { get; set; }
public string password { get; set; }
}
then in the AppHost
Routes.Add<TestRequest( "/TestAPI/Reservation/{vendorId}/{barcode}"," GET,OPTIONS")
.Add<TestRequest>("/TestAPI/Reservation", "POST, OPTIONS")
and the call for JsonServiceClient with POST
request.vendorId=12344;
request.barcode="AAS1223";
TestResponse response = client.Post<TestResponse>(server_ip + "/TestAPI/Reservation", request);
OR with GET
TestResponse response = client.Get<TestResponse>(server_ip + "/TestAPI/Reservation/12344/AAS1223?username=John&password=99");
Then in the service Get or Post functions
public TestResponse Get(TestRequest request)
{
// request members hold the values of the url.
return DoBusinessLayerWork(request);
}
Using the Send() method from the JsonServiceClient type is the way to go about doing this.

WCF Guid DataMember not Serialized properly

I have a WCF service that passes back and forth the following DataContracts:
[DataContract]
public class RequestWrapper
{
[DataMember]
public FooDataContract FooDataContract;
}
[DataContract]
public class ResponseWrapper
{
[DataMember]
public FooDataContract FooDataContract;
}
[DataContract]
public class FooDataContract
{
public FooDataContract(string data, Guid id)
{
Data = data;
ID = id;
}
[DataMember]
public string Data { get; set; }
[DataMember]
public Guid ID { get; set; }
}
It's called via a proxy class like this:
void CallService(string data)
{
var id = Guid.NewGuid();
var response = proxy.CallService(new RequestWrapper
{
new FooDataContract(data, id);
});
}
This is then passed (over the service) to the database via a repository using EF:
public void RepoMethod(FooDataContract foo)
{
var guid = foo.ID; // - Breakpoint here shows all zeros!
efContext.DoSomething(foo.Data, foo.ID);
}
Here's the service call:
public ResponseWrapper CallService(RequestWrapper request)
{
var foo = request.FooDataContract;
repository.RepoMethod(foo);
var response = new ResponseWrapper{ FooDataContract = foo };
return response;
}
Here's the proxy:
public class Proxy : IMyService
{
static readonly ChannelFactory<IMyService> channelFactory =
new ChannelFactory<IMyService>("IMyService");
ResponseWrapper CallService(RequestWrapper request)
{
return channelFactory.UseService(s => s.CallService(request));
}
}
internal static class UseServiceFunction
{
internal static R UseService<T, R>
(this ChannelFactory<T> channelFactory, Func<T, R> useService)
{
var service = channelFactory.CreateChannel();
try
{
R response = useService(service);
return response;
}
finally
{
var channel = service as ICommunicationObject;
try
{
if (channel.State != CommunicationState.Faulted) channel.Close();
}
catch { channel.Abort(); }
}
}
}
I've put a watch on the Guid in the VS debugger. When the service is called from a client web application, the generated Guid is a valid Guid of seemingly random hex characters. Great, that's working.
But when the data is serialized, goes over the wire, and comes out the other side (in my repository), the Guid is all zeros!
I've double, triple checked that the Guid is indeed marked with the [DataMember] attribute. I'm wondering if the extra layer of DataContract (how a FooDataContract is wrapped with the RequestWrapper data contract) is causing a serialization issue?
I think your problem here is that the constructor you've made in your DataContract class doesn't get passed to the proxy on the client side. WSDL won't know anything about this. Think of your data contracts as just a place to stick data with no other functionality. To confirm, you can look in the reference.cs class that got generated in the client when you added the service reference.
I'd suggest re-writing the code so that you explicitly set each of the values in your data contract rather than relying on the constructor.
You can also write a hand coded proxy that has whatever behavior you want and then share that file with the client. That would work, but then you'll be more tightly coupling your client to your service.
Turns out, my translation layer wasn't updated to convert between the DTOs! Whooooops!

Categories