Yet another remoting thread

Posts   
 
    
jaschag
User
Posts: 79
Joined: 19-Apr-2006
# Posted on: 18-Oct-2006 21:29:58   

Sorry to go over old ground - I have read most if not all of the threads that a search for remoting returns but have not found anything that covers the whole topic end to end. Has anyone got a framework / working sample / good advice that they are willing to share that deals with using LL... in a scenario where the client talks to a business services middle tier via remoting?

Thanks,

Jascha

Rogelio
User
Posts: 221
Joined: 29-Mar-2005
# Posted on: 19-Oct-2006 01:01:56   

Hi,

This is what I have done (in the following code you will find class that use the public members directly instead of declare them as property; but the idea is that you have a sample) :



In the Interface project:

This class allows to pass the server name, database name and other fields.  The idea is to allow the BL to return data from different database (with the same structure) depending of the content of this fields.

<Serializable()> _
Public Class DataServerInfo
    Implements ICloneable

    Public SqlServer As String
    Public Database As String
    Public UserID As String
    Public CultureName As String
......

        Public Function Clone() As Object Implements System.ICloneable.Clone

        Dim info As DataServerInfo
        info = Me.MemberwiseClone

        Return info
    End Function
End Class




The following interface define the common set of functions and sub that each business service object must implements.  Normally all my tables has a indentity field as primary key and an ID field that is the key that the user use to access a specific record.  I use a array to pass the value for primary key and ID, because sometime the PK or ID are composed of more than one value, all of this will change when I migrate this to VS 2005 and generics.  The GetTypedList function is use to return all the data that are use for: dropdowns, selector screen (that allow the user create, edit and delete records), reports, etc. 

Public Interface IBasicBusiness

    Function GetByPK(ByVal pk() As Object, ByVal serverInfo As DataServerInfo) As ORMSupportClasses.EntityBase2
    Function GetByID(ByVal id() As Object, ByVal serverInfo As DataServerInfo) As ORMSupportClasses.EntityBase2
    Function Update(ByVal entity As ORMSupportClasses.EntityBase2, ByVal serverInfo As DataServerInfo) As Object
    Sub Delete(ByVal pk() As Object, ByVal serverInfo As DataServerInfo)
    Function ExistID(ByVal id() As Object, ByVal serverInfo As DataServerInfo) As Boolean
    Function ExistPK(ByVal pk() As Object, ByVal serverInfo As DataServerInfo) As Boolean
    Function GetTypedList(ByVal dispatcher As ITypedListDispatch, ByVal serverInfo As DataServerInfo) As DataTable

End Interface



The following interface is implemented by all service that will return business objects.  Actually I have 3 kind of this service :Local, Remoting and WebService, and I have implemented Local and Remoting:

Public Interface IBusinessFactory

    Function GetUserBusiness() As Interfaces.IUserBusiness
    Function GetCustomerBusiness() As Interfaces.ICustomerBusiness
......   

End Interface


Public Enum ServiceMode As Byte

    Local = 0
    Remoting = 1
    WebServices = 2

End Enum


Public Interface IBasicTypedList
    Function GetAll(ByVal serverInfo As DataServerInfo) As DataTable
    Function GetAllList(ByVal dispatcher As IGetTypedListRowByID, ByVal serverInfo As DataServerInfo) As DataTable

End Interface

------------------------------------------------------------------------------------------------------

In the BusinessFactory project:


The following class allow you to get a businessfactory that will return a business service object.  Normally I use the GetBusinessFactory() function to get the default business factory (the default business factory is assigned depending of an enrtry in the application.config file.  The GetBusinessFactory(ByVal mode As Interfaces.ServiceMode) can be used when you know from where you want a business service object. 

Public NotInheritable Class BusinessFactory

    Private Sub New()
    End Sub

    Private Shared _factory As IBusinessFactory

    Public Shared Function GetBusinessFactory() As Interfaces.IBusinessFactory
        Return _factory
    End Function

       Public Shared Function GetBusinessFactory(ByVal mode As Interfaces.ServiceMode) As Interfaces.IBusinessFactory

        Select Case mode

            Case Interfaces.ServiceMode.Local
                Return LocalServices.Instance

            Case Interfaces.ServiceMode.Remoting
                Return RemotingServices.Instance

            Case Interfaces.ServiceMode.WebServices
                Return WebServices.Instance

        End Select
    End Function

    ' to init the default business factory 
    Friend Shared Sub FactoryConfig()
        _factory = GetBusinessFactory(ServicesConfig.ServiceMode)
    End Sub

End Class


This class allow to config the service mode and to get the default service mode that the application is using.  The ReadConfig method is call from the load event of the main form.

Public Class ServicesConfig

    Private Sub New()
    End Sub

    Private Shared _serviceMode As Interfaces.ServiceMode = Interfaces.ServiceMode.Local

    Public Shared ReadOnly Property ServiceMode() As ServiceMode
        Get
            Return _serviceMode
        End Get
    End Property

    Public Shared Sub ReadConfig()

        Dim serviceMode As String

        serviceMode = System.Configuration.ConfigurationSettings.AppSettings("BusinessServiceMode")

       If Not serviceMode Is Nothing Then
            Select Case serviceMode.ToLower
                Case "local"
                    _serviceMode = Interfaces.ServiceMode.Local
                Case "remoting"
                    _serviceMode = Interfaces.ServiceMode.Remoting
                Case "webservices"
                    _serviceMode = Interfaces.ServiceMode.WebServices
                Case Else
                    _serviceMode = Interfaces.ServiceMode.Local
            End Select
        End If

        BusinessFactory.FactoryConfig()
    End Sub



The following class allows to get local business service object.  To create business service object locally the client side need to have a copy of the business assembly.

Friend Class LocalServices
    Implements Interfaces.IBusinessFactory

    Private Sub New()
    End Sub

    Public Shared ReadOnly Instance As New LocalServices

    Public Function GetCustomerBusiness() As Interfaces.ICustomerBusiness Implements Interfaces.IBusinessFactory.GetCustomerBusiness
        Return CustomerBusiness.Instance
    End Function

    Public Function GetUserBusiness() As Interfaces.IUserBusiness Implements Interfaces.IBusinessFactory.GetUserBusiness
        Return UserBusiness.Instance
    End Function

End Class



The following class allows to get Remote business service object.  If you do not want the client (user interface) to have a copy of the business assembly then you need to remove all the options that allow create business service locally and create the instance of  Services.Remoting.RemoteServicesFactory using reflection.

Friend Class RemotingServices
    Implements Interfaces.IBusinessFactory

    Public Shared ReadOnly Instance As New RemotingServices

    Private Sub New()
        Me._remoteServicesFactory = New Services.Remoting.RemoteServicesFactory
    End Sub

    Private _remoteServicesFactory As Services.Remoting.RemoteServicesFactory

    Private ReadOnly Property RemoteServiceFactory() As Services.Remoting.RemoteServicesFactory
        Get
            Return Me._remoteServicesFactory
        End Get
    End Property

       Private iUserBusiness As Interfaces.IUserBusiness

    Public Function GetUserBusiness() As Interfaces.IUserBusiness Implements Interfaces.IBusinessFactory.GetUserBusiness

        If Me.iUserBusiness Is Nothing Then
            Me.iUserBusiness = Me.RemoteServiceFactory.GetUserBusiness
        End If

        Return Me.iUserBusiness
    End Function

.......

End Class

Note that using this class allow you to add more business services without need to worry about modifing the application.config file neither creating instances using reflection.  In the application.config file you will have something like this:

 <appSettings >
       <add key="BusinessServiceMode" value="Local"/>
       </appSettings>
  <system.runtime.remoting>
       <application name="MyApplicationClient">
          <client>
    <wellknown type="MyApplicationNameSpace...RemoteServicesFactory,MyApplication.RemotingServices" url="tcp://RemotingHost:8050/MyApplicationServer/RemoteServicesFactory" />
           </client>
   <channels>
        <channel ref="tcp client">
            <serverProviders>
    <formatter ref="binary" typeFilterLevel="Full" />
           </serverProviders>
      </channel>
   </channels>
   </application>
 </system.runtime.remoting>
......

With this framework no matter the amount of business services, you only need to create a instance of RemoteServicesFactory  after registering the above configuration or using reflection.  Remember that if you are going to use reflection then you do not need to register the configuration of the application.config file.


----------------------------------------------------------------------------------------------------------------

In the RemotingServices project:

This is the project that is referenced by the RemotingServiceHost project (this is a windows service project, let me know if you need more details of this windows service project).

This project has to have references to your Interface and Business projects.

The following is the only class in this project.

Public Class RemoteServicesFactory
    Inherits MarshalByRefObject
    Implements Interfaces.IBusinessFactory

    'the object will lives forever
    Public Overrides Function InitializeLifetimeService() As Object
        MyBase.InitializeLifetimeService()
        Return Nothing
    End Function

    Public Function GetUserBusiness() As Interfaces.IUserBusiness Implements Interfaces.IBusinessFactory.GetUserBusiness
        Return UserBusiness.Instance
    End Function
..........
End Class


You need to register this class, in the RemotingServiceHost project, to allow the clients to create instance, normally I register it using the singleton call.

-----------------------------------------------------------------------------------------------------------------

In the Business project:

The classes in this project delegate the operations of fetching, saving and deleting entities to classes in the DataAccess project, the business rules are validate by the classes in this project (for example the sale of an item need to change the quantity in stock,etc).


All the business service classes inherit from the following base class:

Public MustInherit Class BasicBusiness
    Inherits MarshalByRefObject
    Implements Interfaces.IBasicBusiness, Interfaces.IBasicTypedList

    Protected errorDelete As String
    Protected recordDB As DataAccess.BasicDB

    ' alive forever
    Public Overrides Function InitializeLifetimeService() As Object
        MyBase.InitializeLifetimeService()
        Return Nothing
    End Function

    Public Overridable Sub Delete(ByVal pk() As Object, ByVal serverInfo As Interfaces.DataServerInfo) Implements Interfaces.IBasicBusiness.Delete

        Dim entity As ORM.EntityBase2

        Try
            Me.recordDB.Delete(pk, serverInfo, Nothing)

        Catch ex As MyApplicationException
            Throw
        Catch ex As Exception
            Throw New MyApplicationException(Me.errorDelete, ex)
        End Try

    End Sub

    Public Overridable Function GetByID(ByVal id() As Object, ByVal serverInfo As Interfaces.DataServerInfo) As ORM.EntityBase2 Implements Interfaces.IBasicBusiness.GetByID

         Return Me.recordDB.GetByID(id, serverInfo, Nothing)

    End Function

    Public Overridable Function GetByPK(ByVal pk() As Object, ByVal serverInfo As Interfaces.DataServerInfo) As ORM.EntityBase2 Implements Interfaces.IBasicBusiness.GetByPK

        Return Me.recordDB.GetByPK(pk, serverInfo, Nothing)
    End Function

    Public Overridable Function Update(ByVal entity As ORM.EntityBase2, ByVal serverInfo As Interfaces.DataServerInfo) Implements Interfaces.IBasicBusiness.Update
        Return Me.recordDB.Update(entity, serverInfo, False, Nothing)
    End Function

    Public Overridable Function ExistID(ByVal id() As Object, ByVal serverInfo As Interfaces.DataServerInfo) As Boolean Implements Interfaces.IBasicBusiness.ExistID

        Dim entity As ORM.EntityBase2

        entity = Me.recordDB.GetByID(id, serverInfo, Nothing)

        Return (Not entity Is Nothing)
    End Function

    Public Overridable Function ExistPK(ByVal pk() As Object, ByVal serverInfo As Interfaces.DataServerInfo) As Boolean Implements Interfaces.IBasicBusiness.ExistPK

        Dim table As ORM.TypedListBase

        Try
           table = Me.recordDB.GetByPKList(pk, serverInfo, Nothing)

            Return (table.Rows.Count > 0)

        Finally
            If Not table Is Nothing Then
                table.Dispose()
            End If
        End Try
    End Function

    Public Overridable Function GetTypedList(ByVal dispatcher As ITypedListDispatch, ByVal serverInfo As Interfaces.DataServerInfo) As System.Data.DataTable Implements Interfaces.IBasicBusiness.GetTypedList

        Try
            Return dispatcher.Execute(Me, serverInfo)

        Catch ex As MyApplicationException
            Throw

        Catch ex As Exception
            Throw New MyApplicationException("Error Message", ex)
        End Try

    End Function

    Public Function GetAll(ByVal serverInfo As Interfaces.DataServerInfo) As System.Data.DataTable Implements Interfaces.IBasicTypedList.GetAll
        Return Me.recordDB.GetAll(serverInfo)
    End Function

    Public Function GetAllList(ByVal dispatcher As IGetTypedListRowByID, ByVal serverInfo As Interfaces.DataServerInfo) As System.Data.DataTable Implements Interfaces.IBasicTypedList.GetAllList
        Return Me.recordDB.GetAllList(dispatcher, serverInfo, Nothing)
    End Function

End Class


This is an example of a business service class:

Public Class UserBusiness

    Inherits BasicBusiness
    Implements IUserBusiness

    Public Shared Instance As New UserBusiness

    Private Sub New()
        MyBase.New()

        Me.errorDelete = MensajeBS.ErrorUserDelete
        Me.recordDB = DataAccess.UserDB.Instance
    End Sub

End Class


At this point you must be asking about LLBLGen code, almost all the LLBLGen code is done in the DataAccess classes.

Let me know if you need additional info.

jaschag
User
Posts: 79
Joined: 19-Apr-2006
# Posted on: 19-Oct-2006 15:27:53   

Rogelio,

Quite a post - thanks! A few initial questions:

So it looks like you are creating a remotable manager class for each entity class? How do you deal with retrieving an object with pre-fetched related entities to avoid the remote access getting too "chatty"? How do you fetch entity lists? How do you update saved objects with any db generated property values e.g. identity fields?

I will study this further asap.

Jascha

Rogelio
User
Posts: 221
Joined: 29-Mar-2005
# Posted on: 19-Oct-2006 20:41:57   

jaschag wrote:

So it looks like you are creating a remotable manager class for each entity class? Jascha

Only for the main entities, for example InvoiceEntity and the InvoiceDetailEntity is managed inside the InvoiceBusiness manager.

jaschag wrote:

How do you deal with retrieving an object with pre-fetched related entities to avoid the remote access getting too "chatty"? Jascha

Normally I do pre-fetch when I have an entity that has children, for example InvoiceEntity; but I do not do pre-fetch for the entities that are related with the children (for example I do not pre-fetch ProductEntity that is related with each InvoiceDetailEntity; because normally I only need two fields (code and description) from the related entities. I prefer to add two custom fields to the InvoiceDetailEntity (code and description) and loop all the details and assign value to this fields from a typedlist that I fetch based in the primary key. It is only done when I am fetching a main entity to be edited, in the UI when the user add a detail I get the two fields (code and description) from the dropdown's row that the user select.

jaschag wrote:

How do you fetch entity lists? Jascha

To fetch entity lists I use typedlist, because it allow me to fetch a subset of the fields and I think that typedlists are more light than entities.

jaschag wrote:

How do you update saved objects with any db generated property values e.g. identity fields? Jascha

I do not have to worry about identity fields, I only have to save the main entity (for example InvoiceEntity) and LLBLGen takes care of assigning the generate identity to the main entity and its children.smile

jaschag
User
Posts: 79
Joined: 19-Apr-2006
# Posted on: 20-Oct-2006 11:27:32   

Rogelio, thanks once again. Some more questions if I mayfrowning

Rogelio wrote:

Normally I do pre-fetch when I have an entity that has children, for example InvoiceEntity; but I do not do pre-fetch for the entities that are related with the children (for example I do not pre-fetch ProductEntity that is related with each InvoiceDetailEntity; because normally I only need two fields (code and description) from the related entities. I prefer to add two custom fields to the InvoiceDetailEntity (code and description) and loop all the details and assign value to this fields from a typedlist that I fetch based in the primary key. It is only done when I am fetching a main entity to be edited, in the UI when the user add a detail I get the two fields (code and description) from the dropdown's row that the user select.

Is your pre-fetch path hardcoded in the retrieval method or can you pass one in as a parameter so that each use case can retrieve exactly what it requires?

Rogelio wrote:

To fetch entity lists I use typedlist, because it allow me to fetch a subset of the fields and I think that typedlists are more light than entities.

Good point

Rogelio wrote:

I do not have to worry about identity fields, I only have to save the main entity (for example InvoiceEntity) and LLBLGen takes care of assigning the generate identity to the main entity and its children.smile

But there will be some entity properties that are assigned at the DAL / server - InvoiceNumber for example. How do you propagate these values back to the client?

Regarding entity deletion, if a user is editing an invoice and deletes an InvoiceDetailEntity by using a delete key in a detail grid, how do you perform the entity deletion?

How do you implement authentication / security, particularly for remoting connections?

Are there any threading issues to consider?

Have you used this framework over the internet or slow, high latency connections? How does it perform (are the users happy)?

Are there any pitfalls / gotcha's that I should be aware of?

Thanks again.

Rogelio
User
Posts: 221
Joined: 29-Mar-2005
# Posted on: 20-Oct-2006 20:58:44   

jaschag wrote:

Is your pre-fetch path hardcoded in the retrieval method or can you pass one in as a parameter so that each use case can retrieve exactly what it requires?

Normally I have (in the DataAccessBase class) two overloaded function to fetch an entity using the primary key: - One that get the primary key, serverInfo and adapter. Inside this function a call is done to an overridable function that return a pre-fetch path object, then this function calls the other one that received the pre-fetch path and the other parameters. If I want to retrieve additional entities (via pre-fetch path) then I overrides the function (in the final DataAccess class that inherits from DataAccessBase), create and config the pre-fetch object and return it.

jaschag wrote:

But there will be some entity properties that are assigned at the DAL / server - InvoiceNumber for example. How do you propagate these values back to the client?

In that cases I return the ID (InvoiceNumber) from the update function.

jaschag wrote:

Regarding entity deletion, if a user is editing an invoice and deletes an InvoiceDetailEntity by using a delete key in a detail grid, how do you perform the entity deletion?

The InvoiceDetail table has a column named Deleted, I add a custom field of type Boolean to the InvoiceDetailEntity named ToBeDeleted. When the user delete the row, I check is the row is new, is it is new then it is removed from the collection, it is is not new then it is marked as to be deleted. At the business layer when the InvoiceEntity is being updated, each details is checked to be deleted, it is maked then do the reversion of all entities affected and mark the row as deleted.

jaschag wrote:

How do you implement authentication / security, particularly for remoting connections?

All the implementation was inside an intranet, not need to check for external access; however is you want to avoid any other assembly calls your remote business object, then you can strong signed yours assemblies and add an attribute the the RemoteHost assembly to indicate that only can received calls from specific assembly with public key xxxxx.

jaschag wrote:

Are there any threading issues to consider?

Have you used this framework over the internet or slow, high latency connections? How does it perform (are the users happy)?

I have not used remoting over internet.

jaschag wrote:

Are there any pitfalls / gotcha's that I should be aware of?

Try to use binary formatter always.

If you want your exceptions be propagated back to the client then be sure the RemoteHost application.config file has the following entry: …… <system.runtime.remoting> <application name="NameOfYourRemoteHost"> ……….. </application> <customErrors mode="off" /> </system.runtime.remoting> ………..

Answer
User
Posts: 363
Joined: 28-Jun-2004
# Posted on: 21-Oct-2006 04:06:54   

Are there any threading issues to consider?

Have you used this framework over the internet or slow, high latency connections? How does it perform (are the users happy)?

Depends on your remoting host...if you use IIS which is what i would use and which type of remoting. If your using IIS as a host and SingleCall remoting, then threading isnt really an issue..this is the type i use.

As far usign it over the internet, again, if your using IIS it should work just fine and you get all the built in features of IIS including SSL and it should pass through firewalls just like a webservice would. wink

As far as my services go, i do things a bit different. All my "Save" functions return the entity you passed in,so clients can get an BL generated fields. And for deletes, take an invoice for example, if i modifiy an invoice, a user may wish to delete a invoice line item for example. But i want to save everything in one transaction. So i create a "request" object...

that has a invoice property and a "Invoice Line Items TO Be Deleted Collection"

public InvoiceEntity SaveInvoice(SaveInvoiceRequest request)

This has worked very well for me so far...

jaschag
User
Posts: 79
Joined: 19-Apr-2006
# Posted on: 21-Oct-2006 13:11:45   

Thanks Rogelio

jaschag
User
Posts: 79
Joined: 19-Apr-2006
# Posted on: 21-Oct-2006 13:37:15   

Answer wrote:

Depends on your remoting host...if you use IIS which is what i would use and which type of remoting. If your using IIS as a host and SingleCall remoting, then threading isnt really an issue..this is the type i use.

As far usign it over the internet, again, if your using IIS it should work just fine and you get all the built in features of IIS including SSL and it should pass through firewalls just like a webservice would. wink

Are you using this approach over the net?

Experience with multithreading has taught me to avoid if possible so singlecall sounds like the way to go - have you noticed any performance issues with it?

Answer wrote:

As far as my services go, i do things a bit different. All my "Save" functions return the entity you passed in,so clients can get an BL generated fields. And for deletes, take an invoice for example, if i modifiy an invoice, a user may wish to delete a invoice line item for example. But i want to save everything in one transaction. So i create a "request" object...

that has a invoice property and a "Invoice Line Items TO Be Deleted Collection"

public InvoiceEntity SaveInvoice(SaveInvoiceRequest request)

This has worked very well for me so far...

Good to hearsimple_smile How have you found performance over internet / slower connections? I guess I'm wondering whether the "smartclient" approach is a realistic technique for data entry type applications over the net? I realise that this depends on the nature of the application but if you are always battling with bandwidth and compromising the ui to suit then maybe asp.net becomes more attractive. What are your feelings on that?

You decided not to use unitofwork to handle update/deletes - for strong typing reasons?

Are you passing logical entities - i.e. are you passing an object graph with invoice lines and possibly other related entities as part of the invoice entity above?

Rogelio
User
Posts: 221
Joined: 29-Mar-2005
# Posted on: 21-Oct-2006 14:01:10   

[quotenick="Answer"]

As far as my services go, i do things a bit different. All my "Save" functions return the entity you passed in,so clients can get an BL generated fields. And for deletes, take an invoice for example, if i modifiy an invoice, a user may wish to delete a invoice line item for example. But i want to save everything in one transaction. So i create a "request" object...

that has a invoice property and a "Invoice Line Items TO Be Deleted Collection"

public InvoiceEntity SaveInvoice(SaveInvoiceRequest request)

This has worked very well for me so far...

I do return only the generated ID because after a transaction ( for example Invoice) is saved the user need to print it, then to print any transaction I use a typedlist that has all the fields (including the ones from related tables) that I will need to print the transaction. Why I do this instead of print the transaction directly from the entity, because: - I pass the typedlist directly to my report generator. - Normally I allow the user to reprint a transaction, then I use the same class to print a new transaction and to reprint a transaction, I only have to pass the ID of the transaction. - Normally for each transaction that is going to be printed there are custom fields (quanty by price, total discount, etc) that have to be calculated at the BL before returning the typedlist.

Edit. Writing the above lines I thought why not before saving the transaction ask the user whether he(she) wants to print the transaction and return the typedlist from the BusinessService's update function, that would save me a trip to the Server!

Answer
User
Posts: 363
Joined: 28-Jun-2004
# Posted on: 23-Oct-2006 17:42:13   

Are you using this approach over the net?

Experience with multithreading has taught me to avoid if possible so singlecall sounds like the way to go - have you noticed any performance issues with it?

I have only tested on the lan so far...but originally i was going to use webservices for my application and i switched to remoting, i did a lot of reading prior...Remoting over IIS with singlecall is basically identical to webservices, except that i can take advantage of the Binary formatter simple_smile And it works a bit nicer with llblgen... Using single also gives the benefit of being able to use load balancing fairly easily.

Good to hear How have you found performance over internet / slower connections? I guess I'm wondering whether the "smartclient" approach is a realistic technique for data entry type applications over the net? I realise that this depends on the nature of the application but if you are always battling with bandwidth and compromising the ui to suit then maybe asp.net becomes more attractive. What are your feelings on that?

So far i have been very happy but again, im only doing primitive testing. I am using those request objects etc to limit requests to the server but i havent really hammered the server yet to see what it can handle. With me jsut using it, its very fast and CPU usuage is hardly anything. I get a few spikes here and there, but most the time it like 2-4%.

I would say smart client is def faster. As the server doesnt have to render any UI. Passing a large collection of entities will be slow, you should use typed lists or typedviews for any "List Form" for instance. Other then that i am very happy with performance.

You decided not to use unitofwork to handle update/deletes - for strong typing reasons?

Strong typing was one reason. However the big deal killer was the fact that the unit of work performs inserts then udpates, then deletes. This would cause problems in my case as i needed deleted to be performed first, then inserts and updates..so i just do it manually.

Are you passing logical entities - i.e. are you passing an object graph with invoice lines and possibly other related entities as part of the invoice entity above?

Absolutely! On Some forms, im passing entities 3 levels deep... My request objecst contain the main entity graph, then a collection for each entity type that can be deleted.

On the server, i delete the collections, do any BL work on the entity graph then persist. and return the persisted entity graph back. Works like a charm simple_smile

jaschag
User
Posts: 79
Joined: 19-Apr-2006
# Posted on: 23-Oct-2006 18:10:53   

Guys,

Thanks for all your advice - your experience suggests that many of the risks of remoting are manageable. Typed lists are mentioned often by both of you - I was hoping to keep everything as objects but it sounds like the efficiency of typed lists is too attractive - for browsable lists in particular. However one risk seems to remain - solid experience of this working over slower connections. I am less concerned about server cpu - that is scaleable - it's bandwidth and latency that are the limiting factors so I am going to start a new thread specifically about remoting over the net to see if anyone has done this in anger...

Answer
User
Posts: 363
Joined: 28-Jun-2004
# Posted on: 23-Oct-2006 22:15:09   

well, even though i havent actually tested and used it over the web...YET

1, remoting uses binary formatter which = smaller payload then webservices...

2, your not passing visual markup (ie HTML) along with the data, which = smaller payload.

3, With remoting, you can add a compresssion sink in the chain, which really = smaller payload. ie, 33k entity graph serialized = 5k after compression.

Now, given that, and the fact taht your smart client can cache various things smart clients are definetly much faster IMHO. Especially if your web app isnt using AJAX.

Our internet here sucks, its got a 15k/s upload limit, so performance over the internet is a big factor for me. Plus its wireless, so i have a higher ping then a land line. So minimizing round trips and smallest payload are key wink

jaschag
User
Posts: 79
Joined: 19-Apr-2006
# Posted on: 23-Oct-2006 22:48:12   

Answer wrote:

2, your not passing visual markup (ie HTML) along with the data, which = smaller payload.

I guess that depends on whether the markup payload is > data payload... typed lists...

Answer wrote:

3, With remoting, you can add a compresssion sink in the chain, which really = smaller payload. ie, 33k entity graph serialized = 5k after compression.

Have you tried that? Is it easy to configure? Have you looked at WCF?

Answer wrote:

Our internet here sucks, its got a 15k/s upload limit, so performance over the internet is a big factor for me. Plus its wireless, so i have a higher ping then a land line. So minimizing round trips and smallest payload are key wink

Ouch

Answer
User
Posts: 363
Joined: 28-Jun-2004
# Posted on: 26-Oct-2006 19:01:07   

Have you tried that? Is it easy to configure? Have you looked at WCF?

I havent actually written it yet, but its pretty common, and there plenty of info on the net about it.

I have looked at WCF. remoting + singlecall should be a fairly easy migration. However i have no reason to really use WCF at this point so im going to stick with remoting.