Big-Picture Question (Web Services, RegNow)

J

Jonathan Wood

I was wondering if some of you could advise me on the following issue.

I ported my code that generates shareware licenses to a C# class. I'd like
to get to the point where this is done online (and eventually done
automatically).

What is the best way to expose this functionality on the Web? Are Web
services the answer--I know almost nothing about them?

I'm also wanting to work with RegNow. While they don't seem to currently
support registration via Web services, they might if pushed.

Am I on track here? Does this seem like a reasonable approach? Can anyone
point me to where I could find more information on these issues?

Thanks!
 
M

Mark Rae

Webservices are the solution when you are faced with the following two
situations:

1) your code needs to be available over the web (duh!)

2) your code needs to be available in more than one web app

Are you intending to provide some sort of web portal which other
organisations can use to provide on-line licence keys for their apps, or
maybe for online software activation the way Microsoft (and now others)
do...?
Can anyone point me to where I could find more information on these
issues?

There are literally hundreds of examples on the net - just Google...

Writing a webservice really is very simple these days with Visual
Studio.NET - it pretty much isolates the developer from all the SOAP stuff
(though it's still there, of course) - you pretty much just point and click
to add a webservice to an existing site, fill in the code, compile, test and
deploy.
 
L

Laurent Bugnion

Hi Mark,

Mark said:
Webservices are the solution when you are faced with the following two
situations:

1) your code needs to be available over the web (duh!)

2) your code needs to be available in more than one web app

I see at least one more reason to use web services:

3) You want to transmit (relatively) complex objects without having to
deal with the serialization/deserialization mechanisms.

To me, it's the main reason why I would use SOAP web services over a
simple ASHX custom handler, for example. It's neat to add a web
reference to your application and to have the whole proxy created
automatically for you...

HTH,
Laurent
 
J

Jonathan Wood

Thanks for the input. I'm contacting RegNow to see why this is not an
option.

BTW, does anyone know of a way to return multiple fields from a Web service?
I'm only able to see how to return a single field (the function's return
value).

Thanks.
 
L

Laurent Bugnion

Hi,

Jonathan said:
Thanks for the input. I'm contacting RegNow to see why this is not an
option.

BTW, does anyone know of a way to return multiple fields from a Web service?
I'm only able to see how to return a single field (the function's return
value).

Thanks.

The constraints for web methods are exactly the same as for "normal"
methods: One return value only, but it can be anything including array
(if you have many "fields" of the same type), or an object including
different properties.

All public properties/attributes of the returned object will be
serialized and transmitted.

However, if you can, I recommend to keep it simple. First you'll avoid
serialization problems, and second, remember that web services clients
are not only C#, but could be anything, including JavaScript (it's quite
easy to make a web service call using JavaScript). For example, instead
of transmitting a DateTime object, why not transmit a string
representation of that DateTime... you get the idea.

HTH,
Laurent
 
J

Jonathan Wood

Laurent,
The constraints for web methods are exactly the same as for "normal"
methods: One return value only, but it can be anything including array (if
you have many "fields" of the same type), or an object including different
properties.

That's interesting. I tried using out parameters but the VS 2005 testing
mechanism for asmx files does not support that. I just tried returning a
string array. This is supported. Unfortunately, there is no way to name each
of the string fields returned.
However, if you can, I recommend to keep it simple. First you'll avoid
serialization problems, and second, remember that web services clients are
not only C#, but could be anything, including JavaScript (it's quite easy
to make a web service call using JavaScript). For example, instead of
transmitting a DateTime object, why not transmit a string representation
of that DateTime... you get the idea.

Yes, and I agree wholeheartedly. However, I'm writing a service that
provides a registration code for my software. It seems like it would be more
straightforward (and more standard?) to include two return values: One that
indicate success or failure, and another that provides the registration code
(or error message on failure).

I don't want to do anything that is out of the ordinary but it seems like
this would be a good, simple approach, one for which XML is well suited. If
this is not at all standard, I could include a pass/fail string at the start
of my return string. But only seems like it would be harder for the consumer
of my Web service to deal with.

Thanks!
 
L

Laurent Bugnion

Hi,

Jonathan said:
Laurent,


That's interesting. I tried using out parameters but the VS 2005 testing
mechanism for asmx files does not support that. I just tried returning a
string array. This is supported. Unfortunately, there is no way to name each
of the string fields returned.

An array's fields are not named. Once again, it's the same as for a
normal method.
Yes, and I agree wholeheartedly. However, I'm writing a service that
provides a registration code for my software. It seems like it would be more
straightforward (and more standard?) to include two return values: One that
indicate success or failure, and another that provides the registration code
(or error message on failure).

That's easy to do. Create a class:

public class ReturnValue
{
public bool success = false;
public string registrationCode = "";
}

then, in your web method, declare:

[WebMethod]
public ReturnValue GetRegistrationCode()
{
ReturnValue returnValue = new ReturnValue();
returnValue.success = true;
returnValue.registrationCode = "1234";
return returnValue;
}

As easy as that.
I don't want to do anything that is out of the ordinary but it seems like
this would be a good, simple approach, one for which XML is well suited. If
this is not at all standard, I could include a pass/fail string at the start
of my return string. But only seems like it would be harder for the consumer
of my Web service to deal with.

Thanks!

HTH,
Laurent
 
J

Jonathan Wood

Laurent,
That's easy to do. Create a class:

public class ReturnValue
{
public bool success = false;
public string registrationCode = "";
}

then, in your web method, declare:

[WebMethod]
public ReturnValue GetRegistrationCode()
{
ReturnValue returnValue = new ReturnValue();
returnValue.success = true;
returnValue.registrationCode = "1234";
return returnValue;
}

As easy as that.


Cool! That gives me all the control I need. The book I'm reading said it was
better to stick with basic types but I didn't realize that a class that
contained only public basic types would work as well! That gives me complete
flexibility on the results!

For giggles, I also tried accepting a class as the argument (since I have
quite a few input args). That would be cool also. Unfortunately, the VS Web
service test mechanism did not support this, which makes me think others
might not support it either, so I'll avoid that.

BTW, can you tell me the effect of initializing class variables as you did?
That would seem to bypass the constructor somewhat. I didn't realize you
could do that.

Thanks!
 
L

Laurent Bugnion

Hi,

Jonathan said:
Laurent,

Cool! That gives me all the control I need. The book I'm reading said it was
better to stick with basic types but I didn't realize that a class that
contained only public basic types would work as well! That gives me complete
flexibility on the results!

For giggles, I also tried accepting a class as the argument (since I have
quite a few input args). That would be cool also. Unfortunately, the VS Web
service test mechanism did not support this, which makes me think others
might not support it either, so I'll avoid that.

Show the code. Accepting custom types as parameters should also be
supported.

However, note that there are all kind of clients able to send a request
to a web service. The given client is responsible for serializing (i.e.
transforming into XML) the given parameters, and for deserialiazing
(i.e. transforming from XML) the returned value.

If your client is a .NET application, you don't have to worry too much
about it, because the serialization/deserialization mechanism is
automatically provided (by the web reference, also called web service
proxy).

BTW, can you tell me the effect of initializing class variables as you did?
That would seem to bypass the constructor somewhat. I didn't realize you
could do that.

What I did, for simplification, was provide public attributes in the
class. It is not recommended in normal cases, because anyone could
modify the attributes at any time. However, for simple "packing" when
using a web service, that might be sufficient.

When an attribute is public, you can set and get its value without using
properties. Normally, the usual way is rather to create private
attributes, and to provide public get and set accessors, named
properties. In that case, you can choose to initialize the variables
using the constructor.

The following 2 examples are stictly equivalent as for the result of the
operation.

Example 1:

public class Test1
{
public int attrib1 = 0;
}

Test1 test1 = new Test1();
test1.attrib1 = 5;

Example 2:

public class Test2
{
private int attrib2 = 0;
public int Attrib2
{
get { return attrib2; }
set { attrib2 = value; }
}
}

Test2 test2 = new Test2();
test2.Attrib2 = 5;

Example 3:

public class Test3
{
private int attrib3 = 0;
public int Attrib3
{
get { return attrib3; }
set { attrib3 = value; }
}

public Test3( int param3 )
{
attrib3 = param3;
}
}

The differences are:

In the case 2 and 3, you can introduce additional checks in the "set"
property. For example, you may throw an exception if the value is
smaller than 0.

In the case 3, you make sure that the attributes are always initialized.
There is no default constructor. (The examples 1 and 2 also don't have
an explicit default constructor, but the compiler provides one when no
constructor is defined.)

Note that this has nothing to do with web services, it's basic C#. Don't
hesitate to ask more if something is not clear.

HTH,
Laurent
 
J

Jonathan Wood

Laurent,
Show the code. Accepting custom types as parameters should also be
supported.

However, note that there are all kind of clients able to send a request to
a web service. The given client is responsible for serializing (i.e.
transforming into XML) the given parameters, and for deserialiazing (i.e.
transforming from XML) the returned value.

If your client is a .NET application, you don't have to worry too much
about it, because the serialization/deserialization mechanism is
automatically provided (by the web reference, also called web service
proxy).

My code is like what is shown below but while it may be supported by some
clients, Visual Studio won't run it for me. If I change the arguments to be
the members of RegArgs, then VS has no trouble with it. Note that while I
may want to access this service from a .NET application, I certainly want it
available to clients using different platforms as well.

[WebService(Namespace = "https://www.mydomain.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class ProductRegistration : System.Web.Services.WebService
{
public class RegArgs
{
public string name = "";
public string password = "";
public int productId = 0;
public int cpus = 1;
public string custName = "";
public string custCompany = "";
public string custAddress1 = "";
public string custAddress2 = "";
public string custCountry = "";
public string custPhone = "";
public string custEmail = "";
}

[WebMethod(Description = "Returns registration information for a
product.")]
public string RegisterProduct(RegArgs args)
{
return "BlahBlahBlah";
}
}
In the case 3, you make sure that the attributes are always initialized.
There is no default constructor. (The examples 1 and 2 also don't have an
explicit default constructor, but the compiler provides one when no
constructor is defined.)

I understood the part about making variables private and exposing them
through properties, and also why that might be beneficial.

I just wasn't used to initializing member variables in place rather than in
the constructor. As you probably know, you cannot initialize a member
variable like that in a C++ class. I wasn't aware you could do it that way
in a C# class. I'm assuming the effect of this is the same as initializing
the variable in a constructor: i.e. the variable is initialized to the
specified value when an instance is created.

Thanks!
 
L

Laurent Bugnion

Hi,

Jonathan said:
My code is like what is shown below but while it may be supported by some
clients, Visual Studio won't run it for me. If I change the arguments to be
the members of RegArgs, then VS has no trouble with it. Note that while I
may want to access this service from a .NET application, I certainly want it
available to clients using different platforms as well.

[WebService(Namespace = "https://www.mydomain.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class ProductRegistration : System.Web.Services.WebService
{
public class RegArgs
{
public string name = "";
public string password = "";
public int productId = 0;
public int cpus = 1;
public string custName = "";
public string custCompany = "";
public string custAddress1 = "";
public string custAddress2 = "";
public string custCountry = "";
public string custPhone = "";
public string custEmail = "";
}

[WebMethod(Description = "Returns registration information for a
product.")]
public string RegisterProduct(RegArgs args)
{
return "BlahBlahBlah";
}
}

That works fine for me. Here is the code I used to test. I created a web
service, and a .NET application. In the .NET application, I added a web
reference to the said web service. Note that when you create the
arguments, you must use the proxy types, not the web service's type.
That is also true for the RegArgs class: A proxy representation of this
class will be created by .NET when the web reference is created.

Web service:

namespace WebService1
{
/// <summary>
/// Summary description for Service1
/// </summary>
[WebService( Namespace = "http://tempuri.org/" )]
[WebServiceBinding( ConformsTo = WsiProfiles.BasicProfile1_1 )]
[ToolboxItem( false )]
public class Service1 : System.Web.Services.WebService
{
public class RegArgs
{
public string name = "";
public string password = "";
public int productId = 0;
public int cpus = 1;
public string custName = "";
public string custCompany = "";
public string custAddress1 = "";
public string custAddress2 = "";
public string custCountry = "";
public string custPhone = "";
public string custEmail = "";
}

[WebMethod]
public string TestArgs( RegArgs args )
{
return args.name + "|" + args.password;
}
}
}

Application:

namespace WebApplication1
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load( object sender, EventArgs e )
{
WebApplication1.Service1.RegArgs args
= new WebApplication1.Service1.RegArgs();
args.name = "Hello";
args.password = "World";

WebApplication1.Service1.Service1 service
= new WebApplication1.Service1.Service1();
string result = service.TestArgs( args );
}
}
}

The "result" string, after the call, has the value "Hello|World" which
is what was expected.
I understood the part about making variables private and exposing them
through properties, and also why that might be beneficial.

I just wasn't used to initializing member variables in place rather than in
the constructor. As you probably know, you cannot initialize a member
variable like that in a C++ class. I wasn't aware you could do it that way
in a C# class. I'm assuming the effect of this is the same as initializing
the variable in a constructor: i.e. the variable is initialized to the
specified value when an instance is created.

Actually, not quite when the instance is created (the attributes are
first initialized to their default value, since the default constructor
is used), but right after the instance's creation. Once again, it's not
the preferred way of doing this, it was only for simplification's sake.
I missed that goal I guess ;-)

HTH,
Laurent
 
J

Jonathan Wood

Laurent,
That works fine for me. Here is the code I used to test. I created a web
service, and a .NET application. In the .NET application, I added a web
reference to the said web service.

Okay, I appreciate that. So that will work. However, again, I want to make
sure I'm compatible with as many consumers as possible (.NET or otherwise).
My concern is that since the VS IDE is unable to test the service with a
structure argument, that there is something somewhat non-standard about
that. Since the gain from using a class is minimal, it may not be worth
taking that route for me.

Jumping ahead, the next items I'm wondering about have to do with deployment
of the service. Although I'm somewhat mystified that the default is to
deploy Web pages uncompiled, my understanding is that they can be
precompiled. So there are a few issues I'm starting to wonder about. Any
input you might have would be appreciated.

1. How can I precompile a Web service before placing it on the Web.

2. What does a Web service compile to, and when would something like this be
compiled to a DLL rather than other types of pages?

3. And where would I typically upload the compiled file to (which folder).

Thanks!
 
L

Laurent Bugnion

Hi,

Jonathan said:
Laurent,


Okay, I appreciate that. So that will work. However, again, I want to make
sure I'm compatible with as many consumers as possible (.NET or otherwise).
My concern is that since the VS IDE is unable to test the service with a
structure argument, that there is something somewhat non-standard about
that. Since the gain from using a class is minimal, it may not be worth
taking that route for me.

SOAP encoding and decoding is really in the responsibility of the
client. That said, SOAP is an acknowledged standard now, and more and
more platforms will support it. SOAP was a problem for JavaScript in the
past, because there was no satisfying engine (the terrible HTC component
released by Microsoft was causing huge memory leaks) to handle SOAP
encoding and decoding, but now you have ASP.NET AJAX (ex ATLAS) to do
that, so it's all good.

Additionally, classes (or arrays) are easy to handle in SOAP, so as long
as your properties are simple types, it's really OK to "pack" them.
Jumping ahead, the next items I'm wondering about have to do with deployment
of the service. Although I'm somewhat mystified that the default is to
deploy Web pages uncompiled, my understanding is that they can be
precompiled. So there are a few issues I'm starting to wonder about. Any
input you might have would be appreciated.
1. How can I precompile a Web service before placing it on the Web.

When you use the VS2005 website model, you can publish your web service
just like you publish a website, by using the menu "Build / Publish".
It's the same process, exactly.

However, I recommend you to switch to the Web Application Projet, which
is an add-on to Visual Studio and which allows you to develop your
websites like you did in VS2003, with total control on the DLL.

http://webproject.scottgu.com/

After installing, choose "New project", and then "Web" and then "ASP.NET
Web Service Application".
2. What does a Web service compile to, and when would something like this be
compiled to a DLL rather than other types of pages?

A web service is made of a front-end file (.ASMX), which is the entry
point, and code-behind (C#). The compilation model is exactly the same
as for ASPX pages: The code behind gets compiled in the DLL, additional
referenced DLLs get copied too, and the ASMX file remains as a text file.
3. And where would I typically upload the compiled file to (which folder).

You don't have a choice: Except by tweaking the configuration (which I
wouldn't), the DLLs must be placed in the "bin" folder under the root of
your virtual folder.

Try publishing your web service to a local folder, and then you'll see
that the folders are created, and the files copied.

HTH,
Laurent
 
J

Jonathan Wood

Hi Laurent,
Additionally, classes (or arrays) are easy to handle in SOAP, so as long
as your properties are simple types, it's really OK to "pack" them.

If it's really not an issue, do you have any idea why the VS environment
does not support it for testing?

Thanks for your additional comments. I've printed them out and will go
through them in more detail later.

Thanks again!
 
L

Laurent Bugnion

Hi,

Jonathan said:
Hi Laurent,


If it's really not an issue, do you have any idea why the VS environment
does not support it for testing?

It really should support it. Something else must be wrong.

HTH,
Laurent
 
J

Jonathan Wood

Well, I understood you tried it. Did you try running it within the
environment?

Thanks.
 
L

Laurent Bugnion

Jonathan,

Jonathan said:
Well, I understood you tried it. Did you try running it within the
environment?

Thanks.

I am sorry, I don't understand what you mean with "the environment". Do
you mean that you cannot debug?

This works:

SERVICE:

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
public Service ()
{
}

public class ContainerClass
{
private string m_strValue = "";
private int m_iValue = 0;

public string strValue
{
get { return m_strValue; }
set
{
if (value == null)
{
value = "";
}
m_strValue = value;
}
}

public int iValue
{
get { return m_iValue; }
set
{
if (value < 0)
{
value = 0;
}
m_iValue = value;
}
}

public ContainerClass()
{
// Only there for serialization
}

public ContainerClass(string strValue, int iValue)
{
this.strValue = strValue;
this.iValue = iValue;
}
}

[WebMethod]
public ContainerClass Execute(ContainerClass param)
{
ContainerClass oReturn
= new ContainerClass(param.strValue, param.iValue);
return oReturn;
}
}

ASPX:

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<asp:TextBox runat="server" ID="tfStringValue" Text="" />&nbsp;(string)
<br />
<asp:TextBox runat="server" ID="tfIntValue" Text="0" />&nbsp;(int)
<br />
<asp:Button runat="server" ID="bnExecute" Text="Execute" />
<hr />
<asp:Label runat="server" ID="lblResult" Text="" />
</form>
</body>
</html>

ASPX.CS:

public partial class _Default : System.Web.UI.Page
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
this.bnExecute.Click += new EventHandler(bnExecute_Click);
}

private void bnExecute_Click(object sender, EventArgs e)
{
try
{
string strValue = tfStringValue.Text;
int iValue = Int32.Parse(tfIntValue.Text);

Service.ContainerClass oParam = new Service.ContainerClass();
oParam.strValue = strValue;
oParam.iValue = iValue;

Service.Service oService = new Service.Service();
Service.ContainerClass oReturn = oService.Execute(oParam);

lblResult.Text = oReturn.strValue + "|" + oReturn.iValue;
lblResult.ForeColor = System.Drawing.Color.Black;
}
catch (Exception ex)
{
lblResult.ForeColor = System.Drawing.Color.Red;
lblResult.Text = ex.Message;
}
}

protected void Page_Load(object sender, EventArgs e)
{
}
}

Note the following: The check introduced in the properties of the class
ContainerClass are not passed to the proxy. It is possible to pass
negative values in the proxy, because the WSDL file only describes the
interface, not the content of the properties or the methods. That can be
confusing.

Other than that, this proves that you can use objects as parameters and
as return values.

HTH,
Laurent
 
J

Jonathan Wood

Hi Laurent,
I am sorry, I don't understand what you mean with "the environment". Do
you mean that you cannot debug?

If you build a Web service, you can run it (with or without debugging) right
in the environment without writing a consumer for that service. Visual
Studio pops up an instance of your browser with each of the services you
implemented, along with their optional descriptions. When you select a
service, a page is displayed for you to enter all the arguments and clicking
the Invoke button will display the results.

However, this only works for basic argument and return types. And it does
not work when the argument is a class.

My concern was (rightly or wrongly) that this is an indication that
accepting a class for the argument is not as standard as accepting separate
arguments of basic types.
 
L

Laurent Bugnion

Hi Jonathan,

Jonathan said:
Hi Laurent,


If you build a Web service, you can run it (with or without debugging) right
in the environment without writing a consumer for that service. Visual
Studio pops up an instance of your browser with each of the services you
implemented, along with their optional descriptions. When you select a
service, a page is displayed for you to enter all the arguments and clicking
the Invoke button will display the results.

OK. What Visual Studio actually does when you press F5 or Ctrl-F5 is
start an instance of the integrated development web server, and then
start an instance of IE pointing to that URL. It's the
http://localhost: said:
However, this only works for basic argument and return types. And it does
not work when the argument is a class.

The code I gave you in my last post works in the integrated development
web server. I did test it explicitly there because I suspected it was
what you meant.

I really don't understand why your code doesn't run as you expect. I can
propose you to zip the project files and email them to me, but I cannot
guarantee that I can look at the files before the weekend.
My concern was (rightly or wrongly) that this is an indication that
accepting a class for the argument is not as standard as accepting separate
arguments of basic types.

It's either an indication that something in your code is not correct, or
an indication that something in your configuration is not correct,
because it works fine here ;-)

HTH,
Laurent
 
J

Jonathan Wood

Hi Laurent,

Thanks for following up on this. It took me a few days before I could get to
this.
The code I gave you in my last post works in the integrated development
web server. I did test it explicitly there because I suspected it was what
you meant.

I still think we're talking about different things here.

Note that I did not create any UI code. I notice that you had some ASP stuff
that included text boxes for entering the various parameters. I did not do
that. Instead, VS has a built-in feature for testing Web services. If you
set the Web service as your start page, you should be able to see this when
you run the project.

It is that built-in feature for testing Web services that does not work when
the argument to my Web services is a class (even if it's a class of basic
types). It displays a message in the results that says "The test form is
only available for methods with primitive types as parameters."

I tried everything to make my code work that way. I reduced the number of
members from about 11 to 2. I added constructors like you had. I added code
to verify the assignment to string members was not null like you had. I
changed it so the method returns the same class type as the argument like
you had it. But the built-in feature for testing Web services would not work
when the argument to the Web service method is a class.

And, as I indicated, I'm concerned that if the default feature to test Web
services does not support this, that this may be an indication that other
consumers may not support it either.

Thanks again.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top