How do I pass object (ObjectProxy) from Flex back to .NET WebService?
So, there are a wealth of Flex articles online about how to handle a .NET WebMethod that returns a DataSet
or DataTable
. Here is an example:
Handling web service results that contain .NET DataSets or DataTables
So, I know how to use result.Tables.<tablename>.Rows
and the like. But what I cannot seem to figure out or find online is how to go the other dire开发者_如何学Pythonction - a method to pass objects or tables back to the .NET Webservice from Flex, without stooping to passing XML as a string, or making huge web service methods that have one parameter for each property/column of the object being stored. Surely others, smarter than I, have tackled this issue.
I am using ASP.NET 2.0 Typed DataSets, and it would be really nice if I could just pass one object or array of objects from Flex to the web service, populate my Typed DataTable
, and do an Update() through the corresponding typed TableAdapter. My dream would be a [WebMethod]
something like one of these:
public void SaveObject(TypedDataTable objToSave) { ... }
public void SaveObject(TypedDataSet objToSave) { ... }
I've had the typed datatables saving to the database, I know how to do that part and even a few tricks, but we had XML being passed back-and-forth as a string - eww. I'm trying to get to a more object-based approach.
The best object based approach is AMF. I assume its probably a bit late in your your development cycle to change your integration layer, but otherwise I dont know of a way to get around marshalling your object(s) back into XML or separating them out into their primitive components.
For .NET implementations of AMF check out:
- FlourineFX(FOSS)
- WebORB for .NET
Its amazing how easy things become once AMF is used, for example using the Mate MVC framework and an AMF call passing a complex object to the server looks something like this:
<mate:RemoteObjectInvoker instance="yourWebservice" method="saveComplexObject" showBusyCursor="true" >
<mate:resultHandlers>
<mate:CallBack method="saveComplexObjectSuccess" arguments="{[resultObject]}" />
</mate:resultHandlers>
<mate:faultHandlers>
<mate:MethodInvoker generator="{DataManager}" method="presentFault" arguments="{fault}" />
</mate:faultHandlers>
</mate:RemoteObjectInvoker>
With result and fault handlers being optional.
The direction I ended up going was close to what I hoped was possible, but is "hack-ish" enough that I would consider SuperSaiyen's suggestion to use AMF/ORM a better solution for new/greenfield projects.
For sake of example/discussion, let's say I am working with a Person
table in a database, and have a typed DataSet called PeopleDataSet
that has PersonTableAdapter
and PersonDataTable
with it.
READ would look like this in .NET web service:
[WebMethod]
public PeopleDataSet.PersonDataTable GetAllPeople() {
var adapter = new PersonTableAdapter();
return adapter.GetData();
}
... which in Flex would give you a result
Object that you can use like this:
// FLEX (AS3)
something.dataProvider = result.Tables.Person.Rows;
Check out the link I put in the question for more details on how Flex handles that.
CREATE/UPDATE - This is the part I had to figure out, and why I asked this question. The Flex first this time:
// FLEX (AS3)
var person:Object = {
PersonID: -1, // -1 for CREATE, actual ID for UPDATE
FirstName: "John",
LastName: "Smith",
BirthDate: "07/19/1983",
CreationDate: "1997-07-16T19:20+01:00" // need W3C DTF for Date WITH Time
};
_pplWebSvcInstance.SavePerson(person); // do the web method call
(For handling those W3C datetimes, see How to parse an ISO formatted date in Flex (AS3)?)
On the .NET web service side then, the trick was figuring out the correct Type on the web method's parameter. If you go with just Object
, then step into a call with a debugger, you'll see .NET figures it is a XmlNode[]
. Here is what I figured out to do:
[WebMethod]
public int SavePerson(PeopleDataSet p) {
// Now 'p' will be a PeopleDataSet with a Table called 'p' that has our data
// row(s) (just row, in this case) as string columns in random order.
// It WILL NOT WORK to use PeopleDataSet.PersonDataTable as the type for the
// parameter, that will always result in an empty table. That is why the
// LoadFlexDataTable utility method below is necessary.
var adapter = new PersonTableAdapter();
var tbl = new PeopleDataSet.PersonDataTable();
tbl.LoadFlexDataTable(p.Tables[0]); // see below
// the rest of this might be familiar territory for working with DataSets
PeopleDataSet.PersonRow row = tbl.FirstOrDefault();
if (row != null) {
if (row.PersonID > 0) { // doing UPDATE
row.AcceptChanges();
row.SetModified();
}
else { // doing CREATE
row.CreationDate = DateTime.UtcNow; // set defaults here
row.IsDeleted = false;
}
adapter.Update(row); // database call
return row.PersonID;
}
return -1;
}
Now, the kluge utility method you saw called above. I did it as extension method, that is optional:
// for getting the Un-Typed datatable Flex gives us into our Typed DataTable
public static void LoadFlexDataTable(this DataTable tbl, DataTable flexDataTable)
{
tbl.BeginLoadData();
tbl.Load(flexDataTable.CreateDataReader(), LoadOption.OverwriteChanges);
tbl.EndLoadData();
// Probably a bug, but all of the ampersand (&) in string columns will be
// unecessarily escaped (&) - kluge to fix it.
foreach (DataRow row in tbl.Rows)
{
row.SetAdded(); // default to "Added" state for each row
foreach (DataColumn col in tbl.Columns) // fix & to & on string columns
{
if (col.DataType == typeof(String) && !row.IsNull(col))
row[col] = (row[col] as string).Replace("&", "&");
}
}
}
精彩评论