Datasource of distributed app still dirty after a Save

Posts   
 
    
Markiemac
User
Posts: 132
Joined: 25-Apr-2006
# Posted on: 05-Mar-2008 16:04:34   

Hi all, (Using: VB, WinForms, Adapter V2.5, SQL Server 2005)

This code is at the start of a Save method. I save the collection only if the datasource is dirty.

If CType(BindingSource1.DataSource(), EntityCollection(Of EntityClasses.CSecDevClassEntity)).ContainsDirtyContents = True Then

If I then immediately issue another Save the datasource still indicates dirty. This method used to work as expected before I made the application into a distributed app (based on the RemotingExample).

Here's the save:

 frmMain.LookupTableManagerService.SaveAllCSecDevClass(CType(BindingSource1.DataSource(), EntityCollection(Of EntityClasses.CSecDevClassEntity)), True)

(_frmMain.LookupTableManagerService is a stored reference to the LookupTableManager service_)

I'm unsure where to look confused can anyone give me a heads-up on this?

Thanks

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 05-Mar-2008 16:25:46   

How are you saving the collection? Please post a code snippet.

Also which LLBLGen Pro runtime library version are you using? (consult: http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=7725)

Markiemac
User
Posts: 132
Joined: 25-Apr-2006
# Posted on: 05-Mar-2008 17:14:21   

The runtime library version is 2.5.7.1214

Within the form, here's the save:

            Try
                frmMain.LookupTableManagerService.SaveAllCSecDevClass(CType(BindingSource1.DataSource(), EntityCollection(Of EntityClasses.CSecDevClassEntity)), True)
            Catch ex As ORMException
                MessageBox.Show("Unexpected error - Unable to Save data", _
                AppLegend & modLegend, MessageBoxButtons.OK, MessageBoxIcon.Error)
                Return False
                Exit Function
            End Try

The Interface:

Public Interface ILookupTableManager

    Sub SaveAllCSecDevClass(ByVal allRows As EntityCollection(Of CSecDevClassEntity), ByVal rFetch As Boolean)

Bit of the concrete class:

<Serializable()> _
Public Class LookupTableManager
    Inherits MarshalByRefObject
    Implements ILookupTableManager

    Public Sub SaveAllCSecDevClass(ByVal allRows As EntityCollection(Of CSecDevClassEntity), ByVal rFetch As Boolean) Implements Interfaces.ILookupTableManager.SaveAllCSecDevClass
        Dim adapter As New DataAccessAdapter()
        Try
            adapter.SaveEntityCollection(allRows, rFetch, False)
        Finally
            adapter.Dispose()
        End Try
    End Sub

I can't help wondering if the way I get a reference to LookupTableManager might not be a problem!

Thanks

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 06-Mar-2008 10:56:56   

I think you should reset the bindingSource just after the save and re-bind it to the new collection returned from the LookupTableManagerService.

Markiemac
User
Posts: 132
Joined: 25-Apr-2006
# Posted on: 06-Mar-2008 14:34:26   

Unfortunately, Me.BindingSource1.ResetBindings(False) makes no difference

I can of course do this:

                BindingSource1.DataSource = frmMain.LookupTableManagerService.GetAllCSecDevClass

but since DataAdapter should automatically refetch the saved entity...

I wonder if the refetched collection is on another thread. Just a thought, what do you think?

Thanks

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 06-Mar-2008 14:54:42   

Exactly that's what I ment. You should set the bindingSource.DataSource to the newly fetched collection.

but since DataAdapter should automatically refetch the saved entity

This would be at the server/service side, but you need to return the newly saved/refetched collection to the client side.

Please note that these are marshalled by value not by reference, so changes to an entity or a collection on the server won't get back to the client.

Markiemac
User
Posts: 132
Joined: 25-Apr-2006
# Posted on: 06-Mar-2008 15:23:07   

... but you need to return the newly saved/refetched collection to the client side.

Sorry to display my ignorance, but how do you do this exactly?

Thank again

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 06-Mar-2008 15:52:59   

Either with an output parameter or by using the following:

BindingSource1.DataSource = frmMain.LookupTableManagerService.GetAllCSecDevClass
Markiemac
User
Posts: 132
Joined: 25-Apr-2006
# Posted on: 06-Mar-2008 18:46:16   

Walaa wrote:

...or by using the following:

BindingSource1.DataSource = frmMain.LookupTableManagerService.GetAllCSecDevClass

Does this not simply do a fetch from the database? In which case what use is the refetch parameter on the Save.

Could you clarify Output parameter please, I don't get what you mean here.

Thanks

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 07-Mar-2008 11:03:20   

Alright then, let the SaveAllCSecDevClass method return the refetched collection.

The Interface:

Public Interface ILookupTableManager

    Function SaveAllCSecDevClass(ByVal allRows As EntityCollection(Of CSecDevClassEntity), ByVal rFetch As Boolean) As EntityCollection(Of CSecDevClassEntity)

Bit of the concrete class:

<Serializable()> _
Public Class LookupTableManager
    Inherits MarshalByRefObject
    Implements ILookupTableManager

    Public Function SaveAllCSecDevClass(ByVal allRows As EntityCollection(Of CSecDevClassEntity), ByVal rFetch As Boolean) As EntityCollection(Of CSecDevClassEntity) Implements Interfaces.ILookupTableManager.SaveAllCSecDevClass
        Dim adapter As New DataAccessAdapter()
        Try
            adapter.SaveEntityCollection(allRows, rFetch, False)
            Return allRows
        Finally
            adapter.Dispose()
        End Try
    End Function

And on the client:

BindingSource1.DataSource = frmMain.LookupTableManagerService.SaveAllCSecDevClass(CType(BindingSource1.DataSource(), EntityCollection(Of EntityClasses.CSecDevClassEntity)), True)
Markiemac
User
Posts: 132
Joined: 25-Apr-2006
# Posted on: 08-Mar-2008 12:48:58   

I'm sorry, but this is where I came in disappointed

In my first posting you can see that I've been passing in True to have the SaveAllCSecDevClass method return the refetched collection, but this has not been happening, presumably because the returned collection is on another thread.

So the problem as I see it is how do I get hold of the refetched collection which is on another thread? I could of course use my Get method to explicitly do a refetch from the db but then this negates LLBLGen's ability to automatically refetch the collection.

I've gone back to the manual but can't find anything to help me. What am I missing? I can't believe this LLBLGen option doesn't work in a distributed environment confused

Thanks for any guidance

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 10-Mar-2008 11:11:49   

In my first posting you can see that I've been passing in True to have the SaveAllCSecDevClass method return the refetched collection, but this has not been happening, presumably because the returned collection is on another thread.

I see that you have been doing it that way. But I don't understand how the returned collection is on another thread.

So the problem as I see it is how do I get hold of the refetched collection which is on another thread?

I don't follow, where the other thread came from, if you are returning the collection in the same function call.

I could of course use my Get method to explicitly do a refetch from the db but then this negates LLBLGen's ability to automatically refetch the collection.

Well that can be an option, and it doesn't violate LLBLGen concepts. Anyway all the refetch did is retriving data again from the database, which what the Get method should do.

I've gone back to the manual but can't find anything to help me. What am I missing? I can't believe this LLBLGen option doesn't work in a distributed environment

I don't think that's an LLBLGen issue on the first place. LLBLGen Pro is a DAL framework & solution.

Markiemac
User
Posts: 132
Joined: 25-Apr-2006
# Posted on: 10-Mar-2008 13:00:51   

First, thanks for responding on this.

But I don't understand how the returned collection is on another thread.

I've clearly misunderstood your response above at http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=12705&StartAtMessage=0&#70595. I mistakenly thought you were agreeing that it was a thread issue.

I could of course use my Get method to explicitly do a refetch from the db but then this negates LLBLGen's ability to automatically refetch the collection.

Well that can be an option, and it doesn't violate LLBLGen concepts. Anyway all the refetch did is retriving data again from the database, which what the Get method should do.

I wanted to use the LLBLGen refetch feature to return the collection because as I understod it, this would automatically return the saved collection from the server, and avoid the need to issue a Get and then make another trip to the server to get the saved collection. This would also be consistent with how LLBLGen operates in a non-distributed environment.

I've gone back to the manual but can't find anything to help me. What am I missing? I can't believe this LLBLGen option doesn't work in a distributed environment

I don't think that's an LLBLGen issue on the first place. LLBLGen Pro is a DAL framework & solution.

I don't understand what is meant here.

I can see that the refetch feature works in a non-distributed environment and my collection is returned automatically after a Save. I can also see that after making the app distributed, the collection is no longer returned automatically after the Save. So it's reasonable to assume that either something else is needed to make the refetch work automatically or it doesn't work in a distributed environment and I must always issue a Get after a Save disappointed

What I'm getting from this is that LLBLGen's refetch doesn't work in a distributed environment (hard to believe). If I'm wrong where else could I be looking for a solution?

Thanks again

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 11-Mar-2008 10:51:45   

Public Function SaveAllCSecDevClass(ByVal allRows As EntityCollection(Of CSecDevClassEntity), ByVal rFetch As Boolean) As EntityCollection(Of CSecDevClassEntity) Implements Interfaces.ILookupTableManager.SaveAllCSecDevClass Dim adapter As New DataAccessAdapter() Try adapter.SaveEntityCollection(allRows, rFetch, False) Return allRows Finally adapter.Dispose() End Try End Function

The above method runs on the server, right? If so you may debug it, place a breakpoint after the SaveEntityCollection call and check if the allRows collection was refetched from the database? (allRows[0].Fields.State == EntityState.Fetched)

I think the issue is whether the fetched collection is passed back to the client or not, that's why I was saying this is not an LLBLGen Pro issue.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39903
Joined: 17-Aug-2003
# Posted on: 11-Mar-2008 13:38:21   

I think the problem is indeed that entities are sent to the service, persisted there, and then it's assumed the entities on the client are updated. This isn't the case. Distributed systems SEEM to distribute objects over the wire, but they distribute data, not objects. So if a graph of entities is send over the wire, it actually means copies of them are send, as the entities are passed by value, and these objects are persisted on the service, but these objects are different objects (as they live on different machines!) than the ones on the client.

So to make the objects on the client become reset, you either have to refetch them or traverse them and mark them not dirty anymore. If this is a graph, you can use the ObjectGraphUtils class in the ORMSupportClasses to get a list of all entities in a graph to make it easier to traverse them.

Frans Bouma | Lead developer LLBLGen Pro