Linq To Sql Entity Base And Silverlight 3 And ASMX

Feb 2, 2010 at 1:59 PM
Edited Feb 2, 2010 at 3:03 PM

All --

Does anyone have any guidance, hints, thoughts, comments, etc, regarding...

...using Linq To Sql Entity Base (L2SEB) And Silverlight 3 And ASMX web services?

(Note that my project does not use WCF services and cannot-- it needs to use ASMX web services.)

In particular, I am wondering how generate (or otherwise quickly express?) the data transfer object definition, such that I have a strongly-typed-object (or pseudo-strongly-typed-object?) on either side of the ASMX web service.

The design idea, so far, is as follows....

Data-Going-out:

  1. Call to Linq, 
  2. get object that has L2SEB as a base class,
  3. the object uses LinqHelper.SerializeEntity to get a string containing Xml, 
  4. return the string via an ASMX return value
  5. Silverlight catches the string on the other side
  6. Silverlight makes a transfer-object out of the Xml string (ideally with minimal work-- where the "transfer object" is perhaps a generated, and is a lightweight class and has column names for surre, and maybe has data types for each column, and does not need to manage relations beyond holding FK vaues)
  7. Silverlight binds to the transfer-object to UI element
  8. Done.

Data-Going-In

  1. Silverlight takes the data from the UI.
  2. Silverlight converts the object to a string containing Xml (ideally with minimal work)
  3. Silverlight makes a call to an ASMX web service passing in the Xml, which is the object(s) to be update/created/deleted
  4. The ASMX service catches the Xml string.
  5. The BusinessLayer converts the Xml string into an object that has L2SEB as a base class (ideally with minimal work)
  6. The BusinessLayer sets SetAsChangeTrackingRoot if necessary (???)
  7. The businessLayer passes the object to a Linq DataContext and the DataContext saves it.
  8. Done.

 

Going-out does not seem to be a problem or much work-- the SerializeEntity is already done in the LinqHelper etc.

Going-in seems to be an issue because the auto-convert from Xml back to LinqEntity seems tricky, especially where the DataContext needs to be "told" how to "hook up" the changed/new/deleted object.

What do you think?

Is that even possible?

Please advise.

(Any and all comments welcome.)

Thank you.

-- Mark Kamoski

Feb 2, 2010 at 5:51 PM

All --

This is a follow-up to my post above.

For single L2SEB entity objects

It looks like serializing an IQueryable does not work with the LinqHelper.SerializeEntity method.

It looks like serializing an IQueryable<My_L2SEB_Object_Type> does not work with the LinqHelper.SerializeEntity method.

Below is the output of the RTEs that occur when it happens.

Do you have any ideas on how to make this work?

Basically, I need to know-- how can one serialize a set of L2SEB objects?

 

//First I have this to get a set of L2EB objects...
//
//BusinessLayer.BusinessManagers.CategoryCodeManager myManager =
// new BusinessLayer.BusinessManagers.CategoryCodeManager();
//
//IQueryable<BusinessLayer.BusinessEntities.CategoryCode> myEntitySet =
// (IQueryable<BusinessLayer.BusinessEntities.CategoryCode>)myManager.RetrieveAll();
//
//...then I try to serialize it


//This...
//
//string myXml = Team.Framework.Common.Linq.LINQHelper.SerializeEntity<IQueryable<BusinessLayer.BusinessEntities.CategoryCode>>(myEntitySet, convertToUtf8);
//
//...yields the following RTE...
//
//System.Runtime.Serialization.InvalidDataContractException:
//Type 'System.Data.Linq.DataQuery`1[Test.Northwind01.BusinessLayer.BusinessEntities.CategoryCode]' cannot be serialized.
//Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute.
//See the Microsoft .NET Framework documentation for other supported types.
//at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type)
//at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type)
//at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type)
//at System.Runtime.Serialization.DataContract.GetDataContract(RuntimeTypeHandle typeHandle, Type type, SerializationMode mode)
//at System.Runtime.Serialization.DataContractSerializer.get_RootContract()
//at System.Runtime.Serialization.DataContractSerializer.InternalWriteStartObject(XmlWriterDelegator writer, Object graph)
//at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph)
//at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph)
//at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph)
//at Team.Framework.Common.Linq.LINQHelper.SerializeEntity[T](T entitySource) in C:\Code\Team\Framework\Common\Linq\LINQHelper.cs:line 64
//at Team.Framework.Common.Linq.LINQHelper.SerializeEntity[T](T entitySource, Boolean convertToUtf8)
//in C:\Code\Team\Framework\Common\Linq\LINQHelper.cs:line 83
//at Test.Northwind01.PresentationLayer.UiComponents.Forms.Public.TestArea.EntitySerializationTest01.LoadEntitySetButton_Click(Object sender, EventArgs e)
//in c:\Code\Test\Northwind01\Northwind01.PresentationLayer.WebSite\UiComponents\Forms\Public\TestArea\EntitySerializationTest01.aspx.cs:line 155


//This...
//
string myXml = Team.Framework.Common.Linq.LINQHelper.SerializeEntity<IQueryable>(myEntitySet, convertToUtf8);
//
//...yields the following RTE...
//
//System.Runtime.Serialization.InvalidDataContractException: Type 'System.Data.Linq.DataQuery`1[Test.Northwind01.BusinessLayer.BusinessEntities.CategoryCode]'
//cannot be serialized. Consider marking it with the DataContractAttribute attribute,
//and marking all of its members you want serialized with the DataMemberAttribute attribute. See the Microsoft .NET Framework documentation for other supported types.
//at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.ThrowInvalidDataContractException(String message, Type type)
//at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.CreateDataContract(Int32 id, RuntimeTypeHandle typeHandle, Type type)
//at System.Runtime.Serialization.DataContract.DataContractCriticalHelper.GetDataContractSkipValidation(Int32 id, RuntimeTypeHandle typeHandle, Type type)
//at System.Runtime.Serialization.DataContract.GetDataContract(RuntimeTypeHandle typeHandle, Type type, SerializationMode mode)
//at System.Runtime.Serialization.DataContractSerializer.get_RootContract()
//at System.Runtime.Serialization.DataContractSerializer.InternalWriteStartObject(XmlWriterDelegator writer, Object graph)
//at System.Runtime.Serialization.DataContractSerializer.InternalWriteObject(XmlWriterDelegator writer, Object graph)
//at System.Runtime.Serialization.XmlObjectSerializer.WriteObjectHandleExceptions(XmlWriterDelegator writer, Object graph)
//at System.Runtime.Serialization.DataContractSerializer.WriteObject(XmlWriter writer, Object graph)
//at Team.Framework.Common.Linq.LINQHelper.SerializeEntity[T](T entitySource) in C:\Code\Team\Framework\Common\Linq\LINQHelper.cs:line 64
//at Team.Framework.Common.Linq.LINQHelper.SerializeEntity[T](T entitySource, Boolean convertToUtf8)
//in C:\Code\Team\Framework\Common\Linq\LINQHelper.cs:line 83
//at Test.Northwind01.PresentationLayer.UiComponents.Forms.Public.TestArea.EntitySerializationTest01.LoadEntitySetButton_Click(Object sender, EventArgs e)
//in c:\Code\Test\Northwind01\Northwind01.PresentationLayer.WebSite\UiComponents\Forms\Public\TestArea\EntitySerializationTest01.aspx.cs:line 165

Please advise.

Thank you.

-- Mark Kamoski

Coordinator
Feb 2, 2010 at 7:03 PM

Hi Mark,

I'm doing some work in the Silverlight area at the moment (without the entitybase).  There's a couple of options here:

1. Use a web service (asmx) - this will work but there's not a lot of built in support for managing entities etc...

2. Use Ado.Net Data Services (renamed to WCF data services in .NET 4.0) which allows richer support for querying accross boundries

3. Use WCF Ria data services (.NET 4.0 only (however i think u can use it with SL3 and there is a beta for .NET 3.0) which give an even richer experience and includes change tracking on the SL side etc (no need for L2SQL EB)

Also, I'm not sure if you have the syntax correct, IQueryable probably isn't the way to go.  IQueryable is used for passing unexecuted LINQ expressions around not the actual data - so this is not likely to work properly.  Try changing <IQueryable> to <IEnumerable> or <IList> etc...   

Also, do you have Unidirectional set? As you're trying to use the DataContract Serializer, it needs the serialization mode to be set.  This adds the appropriate DataContract attributes.

Also, from memory, if the serializer is based on my Entity base code, the serializer only supports serializing the one object, not a collection, so you may need to look at creating a quick light weight class and marking it with CollectionDataContract attribute (see here: http://msdn.microsoft.com/en-us/library/aa347850.aspx)

Cheers

Matthew

Feb 4, 2010 at 6:24 PM
Edited Feb 4, 2010 at 6:25 PM
mhunter wrote:

...Also, I'm not sure if you have the syntax correct, IQueryable probably isn't the way to go.  IQueryable is used for passing unexecuted LINQ expressions around not the actual data - so this is not likely to work properly.  Try changing <IQueryable> to <IEnumerable> or <IList> etc...   

...

 

 

Mike --

Regarding the use of IQueryable, you have me a bit worried.

This is my code...

 

/// <summary>
/// This retrieves all entities.
///</summary>
/// <returns>This the entity set, which must be cast at the call-site if desired.</returns>
/// <remarks>
/// Note that the call-site must cast to the appropriate type.
/// Note that if a "using" block on the DataContext is used here, then the following RTE occurs... 
/// "The DataContext instance has been disposed and can no longer be used for operations that require a connection"
/// ...when one tries a foreach loop, or similar operation, at the call-site.
///</remarks>
public IQueryable RetrieveAll()
{
	IQueryable<Team.Clear.BusinessLayer.BusinessEntities.CitizenshipCode> myEntity = null;

	//or sometimes this..
	//IQueryable myEntity = null;

	Team.Clear.BusinessLayer.BusinessEntities.DataClassesContext myContext =
		new Team.Clear.BusinessLayer.BusinessEntities.DataClassesContext(Team.Clear.BusinessLayer.BusinessComponents.EntityHelper.GetConnectionString());

	myContext.ObjectTrackingEnabled = true;
	myContext.DeferredLoadingEnabled = myContext.ObjectTrackingEnabled;

	myEntity = from n in myContext.CitizenshipCodes
			   select n;
	
	return myEntity;
}


...and (AFAIK) that type of Linq query is intrinsically going to return an IQueryable and (AFAIK) an IQueryable does not auto-convert to IEnumerable...

...but that is AFAIK, which I admit is not far, so please DO correct me, hint, comment, etc, if you please-- with specific reference to the code above because that is what I would need to refactor.

Thank you.

-- Mark Kamoski

Feb 4, 2010 at 6:39 PM

Mike --

Here are some replies to your comments, etc.


>>>Use a web service (asmx) - this will work but there's not a lot of built in support for managing entities etc...

Yes, this is basically what I am doing (as of now) where I use simple XML to represent my Data Transfer Objects (DTOs) and I use T4 to generate a simple DtoHelper to get simple IntelliSense for column-names and tables-names. Actually, it is quite nice. I have XML on the web service side which I currently persist without Linq. My Phase 2 is to make a converter that can convert To/From my Dto to a L2SEB-style object and then plug-in my L2SEB Repository model onto the DB side-- maybe I will do that or maybe not.

>>>Use Ado.Net Data Services (renamed to WCF data services in .NET 4.0) which allows richer support for querying accross boundries

Yes, I am trying to stay away from WCF for now. I think it is too proprietary. I need to be assured of at least some interoperability. I wish MS would have extended the ASMX model rather than rewritten it-- my sense is that they threw out the baby with the bath water and started from scratch with an "we can do it better" mentality. Ug.  I like the ASMX standardization, etc. But, I may change my mind as I really do not know much about WCF at all. Who knows. Since my ASMX web services are working now, I may just use them until the end. Maybe.

>>>Use WCF Ria data services (.NET 4.0 only (however i think u can use it with SL3 and there is a beta for .NET 3.0) which give an even richer experience and includes change tracking on the SL side etc (no need for L2SQL EB)

Hmm. I was thinking something like that. In fact, I am waiting for VS.NET 2010 and then I will use Self-Tracking entities and (dag gum it they better have built-in serialization support or I am going to pop)-- so that will probably be a point to set aside L2SEB-- but if and only if self-tracking entites actually work.

>>>Also, I'm not sure if you have the syntax correct, IQueryable probably isn't the way to go.  IQueryable is used for passing unexecuted LINQ expressions around not the actual data - so this is not likely to work properly.  Try changing <IQueryable> to <IEnumerable> or <IList> etc...  

Please see my post above on this.

>>>Also, do you have Unidirectional set? As you're trying to use the DataContract Serializer, it needs the serialization mode to be set.  This adds the appropriate DataContract attributes.

Yes. A single L2SEB object can serialize just fine. It is the set of objects that does not.

>>>Also, from memory, if the serializer is based on my Entity base code, the serializer only supports serializing the one object, not a collection, so you may need to look at creating a quick light weight class and marking it with CollectionDataContract attribute (see here: http://msdn.microsoft.com/en-us/library/aa347850.aspx)

Ug. Articles like that are nice in theory but they always leave with a "there must be a simpler way" feeling. Ug. All that just to serialize? I just do not see it. The depth of the architechture is nice. The flexiblity is great. Well done. But the complexity and clarity of those "inner workings" leaves A LOT to be desired, IMHO. That said, it is nice to know it CAN be done "by the book". I would probably opt for a dirt-simple loop and property-name/property-value grab, live with shallow-serialization, and move on wtih my life before I had time to delve in too deep. Ug. I am rambling again. Sorry. Too much time on my hands. Have to cut this short now.

:-)

As always, I appreciate the help and please do get back to me about IQueryable because I think I have missed something important there and could really use some guidance, etc.

Thank you.

-- Mark Kamoski

Coordinator
Feb 5, 2010 at 12:06 AM

Hi Mark,

The example you gave of IQueryable above makes sense if you call

var temp = RetrieveAll().Where(x=> x.name == "bob").ToList();

Why is this so? because RetrieveAll builds a query, the where extends the Query, and then the ToList() executes the query.  Without the too list, until something executes/enumerates there query - it's still just a query expression - no actual data.

however it may not work when you try to:

string myXml = Team.Framework.Common.Linq.LINQHelper.SerializeEntity<IQueryable>(myEntitySet, convertToUtf8);

Because it's like saying: 

here's my collection of data (i.e. myEntitySet - assuming it's of type IQueryable as well and is acutally underneath IEnumerable), treat it as a query (meaning that you intend to extend the query potentially), and give me something serialized based on a query type?  My question is, is it trying to execute it or will it try to serialize the actual query?

However if the code inside SerializeEntity actually executed the query first (as ToList() or trying to enumerate it) and then tried to serialize it you'd probably be ok - but if it's just trying to call serialization on it straight away this might not work because it might be trying to serialize the query expression.

I'm not 100% sure about this, but I would try probably an IEnumeable type instead of IQueryable when using Serialize Entity.  In all truth, it may work, but it just looked a bit odd to me.

Cheers

Matt.