PDA

View Full Version : [Ajax related]Web services not working


rolandz
02-11-2007, 06:00 PM
Hi,

I am using M$ AJAX (sandbox integration project for atlas). And just wanted to expose my DAO via web service. However I received the following message: Only Web services with a [ScriptService] attribute on the class definition can be called from script.

What exactly should I provide? My configuration for web services looks as follows:


<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
xmlns:db="http://www.springframework.net/database">

<description>Services' Configuration</description>

<object id="ItemDaoFactory" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services">
<property name="ProxyType" value="CMS.Core.Dao.BasicItemDao, CMS.Core" />
<property name="ServiceInterface" value="CMS.Core.Dao.IItemDao, CMS.Core" />
</object>

<object id="ItemDaoService" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
<property name="TargetName" value="ItemDaoFactory" />
<property name="Name" value="ItemDao" />
<property name="Namespace" value="http://localhost/Services" />
<property name="Description" value="CMS service" />
</object>
</objects>
Formerly I remember that something like this worked somehow. But I did not use AJAX then...

Bruno Baia
02-12-2007, 02:43 AM
Hi,

This looks like a bug... :s
The ScriptServiceAttribute should not be necessary if you are not calling it from script.

I'll take a look to it.

To make it work for now, you have to add the ScriptServiceAttribute :

<object id="ItemDaoService" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
<property name="TargetName" value="ItemDaoFactory" />
<property name="Name" value="ItemDao" />
<property name="Namespace" value="http://localhost/Services" />
<property name="Description" value="CMS service" />
<property name="TypeAttributes">
<list>
<object type="System.Web.Script.Services.ScriptServiceAttribute, System.Web.Extensions"/>
</list>
</property>
</object>



Bruno

rolandz
02-12-2007, 07:18 AM
Hi,

I still have the problem. Maybe it helps you if I attach the exception details. Here they are:


[InvalidOperationException: Only Web services with a [ScriptService] attribute on the class definition can be called from script.]
System.Web.Script.Services.WebServiceData..ctor(Ty pe type, Boolean pageMethods) +156

[TargetInvocationException: Exception has been thrown by the target of an invocation.]
System.RuntimeMethodHandle._InvokeConstructor(Obje ct[] args, SignatureStruct& signature, IntPtr declaringType) +0
System.RuntimeMethodHandle.InvokeConstructor(Objec t[] args, SignatureStruct signature, RuntimeTypeHandle declaringType) +13
System.Reflection.RuntimeConstructorInfo.Invoke(Bi ndingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +366
System.Reflection.ConstructorInfo.Invoke(Object[] parameters) +14
Spring.Web.Script.Services.ScriptHandlerFactory.Ge tHandler(HttpContext context, String requestType, String url, String pathTranslated) in c:\Documents and Settings\Roland\Moje dokumenty\SharpDevelop Projects\Lib\Spring.Net\sandbox\Integrations\Sprin g.Web.Atlas\src\Spring.Web.Atlas\Web\Script\Servic es\ScriptHandlerFactory.cs:97
System.Web.HttpApplication.MapHttpHandler(HttpCont ext context, String requestType, VirtualPath path, String pathTranslated, Boolean useAppConfig) +326
System.Web.MapHandlerExecutionStep.System.Web.Http Application.IExecutionStep.Execute() +139
System.Web.HttpApplication.ExecuteStep(IExecutionS tep step, Boolean& completedSynchronously) +146


Currently I am trying to access the service via http://localhost:9000/Services/ItemDao.asmx request.

Bruno Baia
02-12-2007, 03:03 PM
Hi,

Are you sure you are using the latest version ?
I've changed the code to support the ASP.NET AJAX 1.0 release version.
If you are using the integration for RC1 with the release version of ASP.NET AJAX, you 'll get this error.

I recommend you to get the Spring.Web.Extensions instead, even if I also updated Spring.Web.Atlas code in sandbox.



Bruno

rolandz
02-13-2007, 05:41 PM
I've built it from latest CVS just before a minute and still get the exception... Maybe it is a problem with adding attribute by WebServiceExporter.

Bruno Baia
02-13-2007, 05:46 PM
Weird :s

The example coming with the integration is working...
Have you checked your Web.config file ?


Bruno

rolandz
02-13-2007, 06:11 PM
I thin I know the problem. I assumed that the service name is ItemDao as a property Name is set to this value. But I tried to access it via ItemDaoService as it is object id in the configuration. So the url was http://localhost/Services/ItemDaoService.asmx instead of http://.../ItemDao.asmx.

However now I have another issue related to NHibernate12 integration - I get You must implement a default accessor on Iesi.Collections.ISet because it inherits from ICollection message. Indeed I use ISet implementation as a set placeholder for items in my domain objects but I don't understand the message. What accessor is required there?

rolandz
02-13-2007, 06:24 PM
However now I have another issue related to NHibernate12 integration - I get You must implement a default accessor on Iesi.Collections.ISet because it inherits from ICollection message. Indeed I use ISet implementation as a set placeholder for items in my domain objects but I don't understand the message. What accessor is required there?

Well... I see it is the problem with indexer...


[InvalidOperationException: You must implement a default accessor on Iesi.Collections.ISet because it inherits from ICollection.]
System.Xml.Serialization.TypeScope.GetDefaultIndex er(Type type, String memberInfo) +703
System.Xml.Serialization.TypeScope.ImportTypeDesc( Type type, MemberInfo memberInfo, Boolean directReference) +857
System.Xml.Serialization.TypeScope.GetTypeDesc(Typ e type, MemberInfo source, Boolean directReference, Boolean throwOnError) +135
System.Xml.Serialization.StructModel.GetPropertyMo del(PropertyInfo propertyInfo) +72
System.Xml.Serialization.StructModel.GetFieldModel (MemberInfo memberInfo) +120
System.Xml.Serialization.XmlReflectionImporter.Imp ortStructLikeMapping(StructModel model, String ns, Boolean openModel, XmlAttributes a) +1769
System.Xml.Serialization.XmlReflectionImporter.Imp ortTypeMapping(TypeModel model, String ns, ImportContext context, String dataType, XmlAttributes a, Boolean repeats, Boolean openModel) +668

Does it mean that the implementation of ISet is incomplete??

Bruno Baia
02-13-2007, 07:14 PM
I don't know if NHibernate entities are XmlSerializable...


Bruno

Erich Eichinger
02-13-2007, 11:46 PM
Hi,

a little bit of googling showed, that the ISet implementation most likely is lacking a default constructor and therefore unapplicable for XML-Serialization.

I see only 2 possible solutions:

a) report this to the makers of Iesi.Collections and hope that they will fix it.

b) Replace ISet usage in your code

cheers,
Erich

rolandz
02-14-2007, 07:56 AM
a little bit of googling showed, that the ISet implementation most likely is lacking a default constructor and therefore unapplicable for XML-Serialization


But I looked at the source and I see a default constructor in the HashedSet there... Maybe the problem is somwhere else? I have a domain classes lokking like (they're mapped via NHibernate of course):


public class Item
{
// ...

int id;

public virtual int Id {
get { return id; }
set { id = value; }
}

DateTime date = DateTime.Now;

public virtual DateTime Date {
get { return date; }
set { date = value; }
}

string title;

public virtual string Title {
get { return title; }
set { title = value; }
}

string shortcut;

public virtual string Shortcut
{
get { return shortcut; }
set { shortcut = value; }
}

string contents;

public virtual string Contents {
get { return contents; }
set { contents = value; }
}

ISet categories = new HashedSet();

public virtual ISet Categories
{
get { return categories; }
set { categories = value; }
}

User owner;

public virtual User Owner
{
get { return owner; }
set { owner = value; }
}

ISet resources = new HashedSet();

public virtual ISet Resources
{
get { return resources; }
set { resources = value; }
}
}
Maybe there is a problem due to ISet type declaration? I internally have HashedSet but outside it is always ISet (but this is most natural way...)

Erich Eichinger
02-14-2007, 08:09 AM
Hi,

try changing the property-type from ISet to HashedSet and see if it works. Chances are good that your last assumption is right. Asfaik XML serialization only looks at the public interface of a type.

cheers,
Erich

Aleks Seovic
02-14-2007, 01:54 PM
Guys, this issue has nothing to do with a default constructor, but with the fact that ISet does not have an indexer.

XML Serializer is looking for a default member, which can be defined by applying System.Reflection.DefaultMemberAttribute to the class and specifying property name as a constructor argument for this attribute. However, if class has an indexer, compiler will not allow you to specify default member explicitly -- it will automatically create this attribute during compilation and set it to indexer property name ("Item", in most cases).

Now, ISet does not have an indexer because it is an unordered, unkeyed collection type, so XML Serializer is unable to retrieve items from it (technically, it could retrieve them using enumerator, but there is no way to deserialize generated XML back into an ISet instance).

You might be able to make your class serializable by applying XmlArray and XmlArrayItem attributes to Categories and Resources properties, but I believe you will have to change their type to HashedSet, as MSDN clearly states that "the serialization of a field or property that returns an interface or array of interfaces is not supported." This is typically not something you want to do, as it will expose implementation details to the clients of your class.

As an alternative, you could consider modifying your implementation to use regular arrays instead of sets, which will make your class compliant with XML serializer requirements without any additional work, but I'm not sure how it will mesh with NHibernate. Of course, if you need to be able to add/remove elements to/from Categories and Resources collection, arrays won't do the job. You could use a hybrid approach where you use sets for everything except for serialization (mark them with XmlIgnore attribute), and expose array properties for serialization purposes only, but that tends to be messy.

In that case, cleaner alternative is to implement IXmlSerializable interface directly. This will give you complete control over the serialization and deserialization of your class, but it requires more work on your end.

Overall, it is important to realize that XML Serialization has some serious limitations and that you need to pay a lot of attention to how you design your objects if you want to avoid the additional work of implementing IXmlSerializable manually. My personal opinion is that domain objects should be designed with a domain, not infrastructure in mind, but that's probably why I end up implementing IXmlSerializable for all but the simplest classes ;)

Regards,

Aleks

Erich Eichinger
02-14-2007, 04:09 PM
Hi,

1:0 for Aleks. Seems you have already been (de-)serializing some domain objects...

btw: A missing default ctor gives the same exception.

cheers,
Erich

rolandz
02-16-2007, 06:45 AM
Guys, this issue has nothing to do with a default constructor, but with the fact that ISet does not have an indexer.

Overall, it is important to realize that XML Serialization has some serious limitations and that you need to pay a lot of attention to how you design your objects if you want to avoid the additional work of implementing IXmlSerializable manually. My personal opinion is that domain objects should be designed with a domain, not infrastructure in mind, but that's probably why I end up implementing IXmlSerializable for all but the simplest classes ;)

I've found suggestions to use RPC formatting (does it mean SOAP RPC?) for serialization at NHibernate forum. However I see lots of limitations that this formatting might introduce... The question is whether M$ AJAX will still be functional if I change the formatting? What about general use of such service from JS? Will it still be able to consume such service?

Below there are few articles from NH formum that I've found:
http://forum.hibernate.org/viewtopic.php?t=951776&highlight=web+service
http://forum.hibernate.org/viewtopic.php?t=965181&highlight=web+service
http://forum.hibernate.org/viewtopic.php?t=961354&highlight=web+service

Anyway. Even if I change the service formatting to SOAP, shall the problem with formatting of returned value (ISet being returned from a web service method) remain? Or perhaps it'll disappear once the service has different formating?

How do I change the formatting in Spring.Net? I don't want to introduce ANY dependencies or knowledge to domain classes - they should not even know that they're going to be used as web service method result being returned...

Shall following pattern be correct?

<object id="ItemDaoService" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
<property name="TargetName" value="ItemDaoFactory" />
<property name="Name" value="ItemDao" />
<property name="Namespace" value="http://localhost/Services" />
<property name="Description" value="CMS service" />
<property name="TypeAttributes">
<list>
<object type="System.Web.Script.Services.ScriptServiceAttribute, System.Web.Extensions"/>
<object type="System.Web.SoapRpcServiceAttribute, System.Web"/>
</list>
</property>
</object>

Bruno Baia
02-17-2007, 08:20 AM
Hi,

I don't know if using RPC will resolve this pb, but you can try and let us know.

Shall following pattern be correct?

<object id="ItemDaoService" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
<property name="TargetName" value="ItemDaoFactory" />
<property name="Name" value="ItemDao" />
<property name="Namespace" value="http://localhost/Services" />
<property name="Description" value="CMS service" />
<property name="TypeAttributes">
<list>
<object type="System.Web.Script.Services.ScriptServiceAttribute, System.Web.Extensions"/>
<object type="System.Web.SoapRpcServiceAttribute, System.Web"/>
</list>
</property>
</object>

Quite ;)

<object id="ItemDaoService" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
<property name="TargetName" value="ItemDaoFactory" />
<property name="Name" value="ItemDao" />
<property name="Namespace" value="http://localhost/Services" />
<property name="Description" value="CMS service" />
<property name="TypeAttributes">
<list>
<object type="System.Web.Script.Services.ScriptServiceAttribute, System.Web.Extensions" />
<object type="System.Web.Services.Protocols.SoapRpcServiceAttrib ute, System.Web.Services" />
</list>
</property>
</object>



If you are using WebServiceClientFactory in the client side, your WSDL needs to be conform to WS-I specifications :
(I've just added support for rpc-literal soap message style, so you'll need to get the latest version)

<object id="ItemDaoService" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
<property name="TargetName" value="ItemDaoFactory" />
<property name="Name" value="ItemDao" />
<property name="Namespace" value="http://localhost/Services" />
<property name="Description" value="CMS service" />
<property name="TypeAttributes">
<list>
<object type="System.Web.Script.Services.ScriptServiceAttribute, System.Web.Extensions" />
<object type="System.Web.Services.Protocols.SoapRpcServiceAttrib ute, System.Web.Services">
<property name="Use" value="Literal" />
</object>
</list>
</property>
</object>



Bruno