How do I pass object (ObjectProxy) from Flex back to .NET WebService? - asp.net

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 direction - 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("&", "&");
}
}
}

Related

Is there any disadvantage to using a dynamic return type on my API endpoints?

I am currently building a web api service using MVC and I am creating the endpoints. For example, my GET endpoint will execute a stored procedure and return the data in JSON format. The model of the data returned can vary in the future and it seems like using a dynamic return type would remove the need of having to change the model and mapping every time that happens. Basically, here is some sample code, do you notice any malpractices in my implementation?
[System.Web.Mvc.HttpGet]
[Route("companies/{id}")]
public dynamic GetCompany([FromUri] int id, string userId)
{
var parameters = new Hashtable
{
{"UserID", userId},
{"CompanyID", id}
};
var result = MyDB.ExecuteSp(CompanyReadByIdSp, parameters);
return result;
}
In fact, this would enable me to transform the object and add whatever I want to it without needing to worry about the model. Is this a bad way of doing things? Thanks ahead.

Calling FluentMigrator methods inside Execute.WithConnection action

Calling FluentMigrator's builder methods while inside the action that I pass to Execute.WithConnection causes a null reference exception to be thrown.
What I am trying to do is select some data so that I may manipulate it in c#, as that is easier than manipulating it in T-SQL, and use the result of my c# operations to update the data or insert new data (to be more specific, I need to pick one query string parameter out of a stored url string and insert it somewhere else).
The only way I see to select data within a migration is to use Execute.WithConnection and retrieve the data myself (FluentMigrator provides no helpers for selecting data), but if I try to use any fluent migrator expression in the action I pass to Execute.WithConnection a null reference exception is thrown.
Here is a boiled down version of my code:
[Migration(1)]
public class MyMigration : Migration
{
public void Up()
{
Execute.WithConnection(CustomDml);
}
public void CustomDml(IDbConnection conn, IDbTransaction tran)
{
var db = new NPoco.Database(conn).SetTransaction(tran); // NPoco is a micro-ORM, a fork of PetaPoco
var records = db.Fetch<Record>("-- some sql"); // this is immediately evaluated, no reader is left open
foreach (var r in records) {
var newValue = Manipulate(r.OriginalValue);
Insert.IntoTable("NewRecords").Row(new { OriginalValueId = r.Id, NewValue = newValue }); // <-- this line causes the exception
}
}
public void Down() {}
}
The line that calls Inser.IntoTable causes a null exception to be thrown from line 36 of FluentMigrator\Builders\Insert\InsertExpressionRoot.cs - it appears that the _context variable may be null at this point but I do not understand why this is. (when testing Create.Table, e.g., it occurs on line 49 of FluentMigrator\Builders\Create\CreateExpressionRoot.cs)
Any help would be appreciated. Perhaps there is disagreement on whether DML is appropriate in a migration, and I am open to suggestions, but this scenario has come up twice this week alone. For now I am simply performing the insert using my micro-ORM within the action rather than FluentMigrator and that does work, but it seems like what I am trying to do should work.
When using the Execute.WithConnection expression all you get is the db connection and the transaction.
Using Execute.WithConnection creates an PerformDBOperationExpression expression. When processing the expression, a processor calls the Operation property (an example in the SqlServerProcessor) and the processor does not have a reference to the MigrationContext. But even if it did have access to the MigrationContext, when FluentMigrator has come to the processing stage, it is already too late. You would be trying to process expressions in a expression and at the moment FluentMigrator is not built to handle that type of nesting.
An alternative would be to make the connection string available in the migration context, see this issue: https://github.com/schambers/fluentmigrator/issues/240
Would that be a better approach?

JSON returned from ASP.NET WebService is inadequate

I have constructed several web services that successfully serialize my .NET types into JSON. However, I'm running into an issue getting the JSON to be fully compatible with the YUI library and the DataTable in particular.
The YUI DataTable can be fully configured using JSON objects. I created the following struct in .NET to represent a given YUI column definition:
public struct YuiColumnDefinition{
public string key;
public string label;
public bool sortable;
public string formatter;
}
The 'formatter' property is how you instruct the YUI table to use a custom javascript function when displaying a given column. The problem is that, since formatter is defined as a String, ASP.NET wraps it's value in double quotes upon serialization, and YUI no longer recognizes the value as a JavaScript token:
JSON YUI expects
[
{key:"1", label:"Order Date", formatter:myCustomJavaScriptFunction, sortable:true},
{key:"2", label:"Customer Name", formatter:null, sortable:false}
]
JSON ASP.NET creates
[
{key:"1", label:"Order Date", formatter:"myCustomJavaScriptFunction", sortable:true},
{key:"2", label:"Customer Name", formatter:null, sortable:false}
]
Anyone have a solution outside of modifying the YUI source code?
Thanks
Change your parsing code.
Think of json as a replacement for xml, you wouldnt put a variable/function in xml. In either, you could easily identify the name or type (say from a list or enum) of the formatter to use. Then your parsing code would know that it should assign a variable/method as the "formatter" property.
Its just incorrect to return an actual variable/function in a callback like that. You could make it work but honestly its not the way to go.
I would do the following...
Change your return json to this.
[
{key:"1", label:"Order Date", formatterName:"myCustomJavaScriptFunction", sortable:true},
{key:"2", label:"Customer Name", formatterName:null, sortable:false}
]
Then in JS, lets assume that json is stored in variable returnedObj
function augmentReturnedObject(returnedObj)
{
// validate that returnObj.formatterName (as a variable) is not undefined
var isValidObj = (window[returnedObj.formatterName] !== undefined);
if (isValidObj)
{
// this will return the actual function / variable, here we are assigning it to returnedObj.formatter
returnedObj.formatter = window[returnedObj.formatterName];
}
else
{
returnedObj.formatter = null;
}
}
You could easily reduce that down to this without much thought
function augmentReturnedObject(returnedObj)
{
var specifiedMethod = window[returnedObj.formatterName];
returnedObj.formatter = (specifiedMethod === undefined) ? null : window[returnedObj.formatterName];
}
So in the end you'd take your json object, and do augmentReturnedObject(returnedObj); and at that point you can pass returnedObj to YUI
As Allen correctly pointed out:
Its just incorrect to return an actual variable/function in a callback like that.
Still, I couldn't find anywhere in the YUI documentation that spells out how to deal with javascript functions returned as strings from either JSON or XML.
Thank goodness for blogs. As pointed out in this one, the "proper" way to register custom javascript formatting functions is by using YAHOO.widget.DataTable.Formatter:
JSON Column Definitions as returned from .ASMX
[
{key:"1", label:"Order Date", formatter:"myCustomJavaScriptFunction1", sortable:true},
{key:"2", label:"Customer Name", formatter:null, sortable:false}
{key:"3", label:"State", formatter:"myCustomJavaScriptFunction2", sortable:false}
]
Javscript to wire up the YUI DataTable
YAHOO.widget.DataTable.Formatter.myCustomJavaScriptFunction1= this.myCustomJavaScriptFunction1;
YAHOO.widget.DataTable.Formatter.myCustomJavaScriptFunction2= this.myCustomJavaScriptFunction2;
function myCustomJavaScriptFunction1(elCell, oRecord, oColumn, oData) {
//do something
}
function myCustomJavaScriptFunction2(elCell, oRecord, oColumn, oData){
//do something
}

Flex: AMF and Enum Singletons – can they play well together?

I'm using Python+PyAMF to talk back and forth with Flex clients, but I've run into a problem with the psudo-Enum-Singletons I'm using:
class Type {
public static const EMPTY:Type = new Type("empty");
public static const FULL:Type = new Type("full");
...
}
When I'm using locally created instances, everything is peachy:
if (someInstance.type == Type.EMPTY) { /* do things */ }
But, if 'someInstance' has come from the Python code, it's instance of 'type' obviously won't be either Type.EMPTY or Type.FULL.
So, what's the best way to make my code work?
Is there some way I can control AMF's deserialization, so when it loads a remote Type, the correct transformation will be called? Or should I just bite the bullet and compare Types using something other than ==? Or could I somehow trick the == type cohesion into doing what I want?
Edit: Alternately, does Flex's remoting suite provide any hooks which run after an instance has been deserialized, so I could perform a conversion then?
Random thought: Maybe you could create a member function on Type that will return the canonical version that matches it?
Something like:
class Type {
public static const EMPTY:Type = new Type("empty");
public static const FULL:Type = new Type("full");
...
// I'm assuming this is where that string passed
// in to the constructor goes, and that it's unique.
private var _typeName:String;
public function get canonical():Type {
switch(this._typeName) {
case "empty": return EMPTY;
case "full": return FULL;
/*...*/
}
}
}
As long as you know which values come from python you would just convert them initially:
var fromPython:Type = /*...*/
var t:Type = fromPython.canonical;
then use t after that.
If you can't tell when things come from python and when they're from AS3 then it would get pretty messy, but if you have an isolation layer between the AS and python code you could just make sure you do the conversion there.
It's not as clean as if you could control the deserialization, but as long as you've got a good isolation layer it should work.

.Net Web service Encrypt Decrypt Dataset

Does anyone know of examples which show how to encrypt a dataset at the client side and send it over to a web service and have it decrypted there?
Another question:
What i need to do is a send hundreds of rows of data from a client to a web service and have the web service update the database with these records. I can't think of any other way to do this without using a dataset. Is there a better method?
Thanks in advance!
As far as the encryption is concerned, why try to reinvent the wheel? Just connect to the webservice over SSL - it'll most likely be much safer than a homegrown alternative.
I would probably create a custom struct/object and send an array of those to the webservice rather than a DataSet. It will mean (slightly) less network traffic; it will make the webservice's WSDL more descriptive; and it will make it easier for any non-Microsoft apps to talk to the webservice, if that becomes necessary in the future.
EDIT: An example...
At the server-side you can declare a custom type (eg, ExampleUser), and then setup your method to accept an array of that type instead of a DataSet:
[WebService(Namespace="http://example.yourdomain.com/ExampleWebService/")]
public class ExampleWebService : System.Web.Services.WebService
{
// this is your custom type
public class ExampleUser
{
public int UserID { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
}
// this is your method
// accepts an array of ExampleUser rather than a DataSet
[WebMethod]
public void UploadUsers(ExampleUser[] usersArray)
{
// do something...
}
}
In the client application you would add a reference to the webservice. This will enable you to use the ExampleUser type declared in the server-side code above.
You could then just convert your DataSet to an array of ExampleUser objects before sending it to the webservice:
// get the number of rows in the DataTable
int rowCount = yourDataSet.Tables[0].Rows.Count;
// create an array of ExampleUser with the correct capacity
ExampleWebService.ExampleUser[] usersArray =
new ExampleWebService.ExampleUser[rowCount];
// iterate through each row in the table
for (int i = 0; i < rowCount; i++)
{
DataRow dr = yourDataSet.Tables[0].Rows[i];
// create an ExampleUser object and populate it from the DataRow columns
ExampleWebService.ExampleUser eu = new ExampleWebService.ExampleUser();
eu.UserID = (int)dr["User_ID"];
eu.Name = (string)dr["Name"];
eu.DateOfBirth = (DateTime)dr["Date_Of_Birth"];
// add the ExampleUser object to the array
usersArray[i] = eu;
}
// the array is populated so let's call the webservice
ExampleWebService.UploadUsers(usersArray);
EDIT: Another example...
If you're using .NET 3.5 then you can get the client-side down to just a few lines of code by using LINQ and object initialisers to create your array:
// create and populate the array
ExampleWebService.ExampleUser[] usersArray =
yourDataSet.Tables[0].AsEnumerable().Select
(
s => new ExampleWebService.ExampleUser()
{
UserID = (int)s["User_ID"],
Name = (string)s["Name"],
DateOfBirth = (DateTime)s["Date_Of_Birth"]
}
).ToArray();
// the array is populated so let's call the webservice
ExampleWebService.UploadUsers(usersArray);
Well, there are a lot of approaches to this. Since you are sending this over the wire, you could: 1) Write the data to an XML stream (this is very much what the DataSet is meant to do) then 2) you could compress the XML (the compression ratio would be best at this stage) then 3) Encrypt using one of the .NET cryptographic schemes and finally 4) decrypt, unzip, and deserialize your XML into a DataSet object or whatever you want to do with it.
Note, you might need to Base64 the result of the encryption. Another alternative is to not encrypt and use the Web Service over SSL and just use that native encryption. Depending on the type of data a DataSet may not be the best choice in terms of performance. You could send a CSV or JSON style data block; this would potentially be smaller, especially if there is only one "DataTable" object in your DataSet. A lot of this is situation dependent as far as what the best method do use is.

Resources