WCF and IsNew property

Posts   
1  /  2
 
    
methodman
User
Posts: 194
Joined: 24-Aug-2009
# Posted on: 15-Jan-2010 13:41:20   

Hello there..

I'm sending an entity thru WCF. I have setted the IsNew property to false on the client side, should be an update. When I receive the entity on the other side, the property IsNew is true. Why is that so ?

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 15-Jan-2010 19:36:56   

Mmm. And Did you construct the entity from scratch in memory? What is your LLBLGen runtime library version? (http://llblgen.com/TinyForum/Messages.aspx?ThreadID=7722) Do you have a code snippet or show us how you configured the endpoint, type of serialization used, etc? so we can try to reproduce your issue.

David Elizondo | LLBLGen Support Team
methodman
User
Posts: 194
Joined: 24-Aug-2009
# Posted on: 18-Jan-2010 13:28:18   

version 2.6 Final

The entity is created in the presentation layer from scratch. Before it is sends thru the wire, its IsNew property is set to false. When I recieve the entity on the otherside, the IsNew property is True. Strange. The other data fields are fine.

I used for serialization first the class from here http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=16234&StartAtMessage=0&#90670

but I also tried the classic [ServiceKnownType(typeof(TfProjektyEntity))].

service interface


    [ServiceContract]
    [ServiceKnownType(typeof(TfProjektyEntity))]
    //[NetDataContractFormatAttribute]
    public interface IProjektyService
    {
         [OperationContract]
        void SaveEntity(TfProjektyEntity _entity);
    }

service


        public void SaveEntity(TfProjektyEntity _entity)
        {
            var enity = _entity;
        }

client


           private ChannelFactory<IProjektyService> channelFactoryProjekty = new      ChannelFactory<IProjektyService>("TeamWork.Projekty");

           var channelProjekty = channelFactoryProjekty.CreateChannel();

            var entita = new TfProjektyEntity {IsNew = false, Id = 5};

            channelProjekty.SaveEntity(entita);

WCF definition


  <system.serviceModel>
    <services>
        <service behaviorConfiguration="BasicServiceBehavior" name="Icp.Services.TeamWork.ProjektyService">
        <endpoint address="net.pipe://localhost/ProjektyService" binding="netNamedPipeBinding" contract="Icp.Services.Contracts.TeamWork.IProjektyService"/>
      </service>
    
    </services>

    <behaviors>
      <serviceBehaviors>
        <behavior name="BasicServiceBehavior">
          <serviceTimeouts transactionTimeout="00:00:45"/>
          <serviceThrottling maxConcurrentSessions="10"/>
          <serviceMetadata httpGetEnabled="false"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

  </system.serviceModel>

I have some other methods sending LLBLGen Entities over wire but none of them is using the IsNew property.

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 18-Jan-2010 15:21:58   

version 2.6 Final

That's not the runtime library build/version number, please check the link which David have posted above.

methodman
User
Posts: 194
Joined: 24-Aug-2009
# Posted on: 18-Jan-2010 15:39:00   

Ok, I found it.

2.6.09.0807

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 18-Jan-2010 15:43:45   

That's an old one

So first... could you please, try the latest available one?

Thanks.

methodman
User
Posts: 194
Joined: 24-Aug-2009
# Posted on: 22-Jan-2010 15:12:27   

Ok, I'm running on the latest version but it didnt helped.

methodman
User
Posts: 194
Joined: 24-Aug-2009
# Posted on: 22-Jan-2010 15:37:24   

I've attached a little test projekt where you can see what is happening.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 24-Jan-2010 20:42:27   

Reproduced. It seems like the way you are setting the values is the problem. The compact serializer look at CurrentValues and DBValues to figure out the IsNew property. We will look into it.

David Elizondo | LLBLGen Support Team
methodman
User
Posts: 194
Joined: 24-Aug-2009
# Posted on: 25-Jan-2010 09:55:03   

Ok, I'll wait.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39903
Joined: 17-Aug-2003
# Posted on: 25-Jan-2010 10:21:39   

In the Compact25 format, which is used by default in WCF etc., every bit we could avoid to pack into the xml is indeed not included. So it only stores the entity.Fields.State value, and not IsNew, as IsNew is related to that state. At deserialization, the state is set to the value in the XML and IsNew is set to true if the state is 'New'.

You should set the state to EntityState.Fetched prior to sending the entity over the wire. 'IsNew' is a flag not used in this matter. This is a bit redundant, but the state can flag more states than just new / fetched, and isnew is just for flagging the container is new or not. Setting IsNew to false will mark the entity not new, but using solely the state property it would be undefined what State should become: fetched or outofsync.

Normally one doesn't run into this.

Frans Bouma | Lead developer LLBLGen Pro
methodman
User
Posts: 194
Joined: 24-Aug-2009
# Posted on: 25-Jan-2010 13:12:58   

All I needed was an update of a record thru WCF. And how can i change the entity state ? I didnt find any property like entitystate.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39903
Joined: 17-Aug-2003
# Posted on: 25-Jan-2010 13:36:38   

methodman wrote:

All I needed was an update of a record thru WCF. And how can i change the entity state ? I didnt find any property like entitystate.

set the 'State' property of the entity's Fields object to EntityState.Fetched: someEntity.Fields.State = EntityState.Fetched;

then send someEntity to the service.

Frans Bouma | Lead developer LLBLGen Pro
caffreys
User
Posts: 65
Joined: 25-Jan-2009
# Posted on: 13-May-2010 10:31:34   

Otis wrote:

set the 'State' property of the entity's Fields object to EntityState.Fetched: someEntity.Fields.State = EntityState.Fetched;

then send someEntity to the service.

Took me all day to debug this one. I'm using netTcpBinding in a WCF application and no updates were working for me. As you've stated the IsNew flag is lost over the wire so you must use the above method instead?

Are there any plans to update the docs for this method as i'm sure it will trip up many other users.

Thanks.

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 13-May-2010 10:58:31   

Thanks for the feedback, we take this into consideration while writing the docs for the new version, thank.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39903
Joined: 17-Aug-2003
# Posted on: 14-May-2010 09:51:18   

Added to docs in next build.

Frans Bouma | Lead developer LLBLGen Pro
caffreys
User
Posts: 65
Joined: 25-Jan-2009
# Posted on: 26-May-2010 09:08:26   

I'm having some rather bizarre issues with netTcpBinding in that the adapter in my WCF service attempts to create a new entity even if I check that IsNew = false.

This is what happens:

1) The client creates a new entity and passes it to the WCF service for persisting. 2) The service creates a new adapter and calls SaveEntity with refetch true and passes it back to the client. 3) The client changes something and calls the same service method for persisting. 4) In the service an exception is raised because a required field has a null value. When I check the inner exception LLBLGen is attempting to create an INSERT INTO query and also it doesn't attempt to include the required field even though i've checked the field value directly before the adapter.SaveEntity method call.

Then I changed zero code other than to use wsHttpBinding in the various app.configs, re-tried the test and everything works fine. I then switched back to netTcpBinding and the errors returned.

So I see two issues:

1) A previously fetched entity, when re-persisting loses its fetched state. 2) Certain fields are not included in the query even though they have values. ....but only using netTcpBinding.

I've tried setting entity.Fields.State = Fetched in various places, and also explicitly setting IsNew = false with no luck.

Any ideas??

caffreys
User
Posts: 65
Joined: 25-Jan-2009
# Posted on: 26-May-2010 10:43:06   

...my mistake, all of the above applies to wsHttpBinding as well. I was confident that the app was working fine so I deployed the services and re-run the test using wsHttpBinding and I received the same errors as before.

I am definitely trying re-save an existing entity which has all fields validated - I have log files which log this data immediately before the adapter.SaveEntity - but the save method incorrectly tries to do an INSERT INTO.

Here is the exception:

An exception was caught during the execution of an action query: The field 'tblProducts.LastModifiedBy' cannot contain a Null value because the Required property for this field is set to True. Enter a value in this field.. Check InnerException, QueryExecuted and Parameters of this exception to examine the cause of this exception.

Query: INSERT INTO [tblProducts] ([ProductNumber], [AccountNumber], [AccountProductNumber], [AccountDescription], [ExaltDescription], [GrossWeight], [Length], [Width], [Height], [IsActive], [ProductTypeID]) VALUES (@ProductNumber, @AccountNumber, @AccountProductNumber, @AccountDescription, @ExaltDescription, @GrossWeight, @Length, @Width, @Height, @IsActive, @ProductTypeId) Parameter: @ProductNumber : String. Length: 20. Precision: 0. Scale: 0. Direction: Input. Value: "5039036029704". Parameter: @AccountNumber : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 67. Parameter: @AccountProductNumber : String. Length: 25. Precision: 0. Scale: 0. Direction: Input. Value: "5039036029704". Parameter: @AccountDescription : String. Length: 75. Precision: 0. Scale: 0. Direction: Input. Value: "Young Riders Season 1 [DVD] [1989]". Parameter: @ExaltDescription : String. Length: 75. Precision: 0. Scale: 0. Direction: Input. Value: "Young Riders Season 1 [DVD] [1989]". Parameter: @GrossWeight : Double. Length: 0. Precision: 15. Scale: 0. Direction: Input. Value: 0.082. Parameter: @Length : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 181. Parameter: @Width : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 138. Parameter: @Height : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 15. Parameter: @IsActive : Boolean. Length: 2. Precision: 0. Scale: 0. Direction: Input. Value: True. Parameter: @ProductTypeId : Int32. Length: 0. Precision: 10. Scale: 0. Direction: Input. Value: 2.

As you can see the query in addition to it being an INSERT instead of UPDATE, does not include the field [LastModifiedBy] even though I can read and log the entity value just prior to doing SaveEntity.

So now i'm stuck. I don't know how to reliably perform update queries over WCF. Sometimes it works fine, and I have another application using netTcpBinding which has been working for over 6 months without issue.

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 26-May-2010 10:49:42   

At the service side, you should have 2 methods, one to Insert an entity, and the other to Update and entity.

In the Update method, set the entity's IsNew property to false.

caffreys
User
Posts: 65
Joined: 25-Jan-2009
# Posted on: 26-May-2010 10:53:04   

I'm a bit puzzled. The query above is only including fields which have been updated so clearly there's some optimisation going on. LastModifiedBy is a user name (text) field used for transaction logging. Its default value is NULL so whoever posts an entry must supply their name.

I have a validation method which checks all required fields. If no user is supplied - possibly because this is a system-derived transaction - then the method will set LastModifiedBy to a constant value of 'System'.

So if I take a new Product entity then pass it through the validate method it will check and ensure this field is set to a value - in fact it actually sets four fields in the Product entity.

When I first persist this entity it will correctly create the insert query containing all fields which are set to a value, including LastModifiedBy.

When the entity is modified, LastModifiedBy field is not necessarily changed, and if it isn't changed then the optimised UPDATE query should not attempt to write this value. And why should it? The value has not changed.

So really the above problem is only a problem because LLBLGen is trying to perform an INSERT INTO query instead of an UPDATE query, and so I suspect if I tried setting the required flag of LastModifiedBy to false - to allow nulls, I would instead see a different error: probably a key violation attempting to insert a record where the PK already exists.

Perhaps this helps??? disappointed

caffreys
User
Posts: 65
Joined: 25-Jan-2009
# Posted on: 26-May-2010 10:56:29   

Walaa wrote:

At the service side, you should have 2 methods, one to Insert an entity, and the other to Update and entity.

In the Update method, set the entity's IsNew property to false.

OK that's a new one?? Now the client has to declare its intentions, i.e. UPDATE or CREATE? I thought that's the point of using an ORM to take care of the complexities. Surely the entity contains - or should contain - enough information for the correct operation to be carried out? This has worked perfectly fine in other applications. I have only one save method and I expect the adapter SaveEntity to take care of this. The only thing I handle myself in the business logic is deletions.

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 26-May-2010 11:08:49   

We've tried to keep data passed through the wire to minimum. You may pass the IsNew as a parameter and reset it at server side, so you can use only one Save method.

caffreys
User
Posts: 65
Joined: 25-Jan-2009
# Posted on: 26-May-2010 11:25:54   

Thanks Walaa,

I'm going back to basics and doing some testing with a console app and extensive logging in the service. I'm particularly interested in what is kept and what is lost when (re)persisting.

What makes it more tricky is that the client passes the entity to the business logic - hosted as a WCF service - and the BL validates the entity and then passes to the DL for persistence.

So testing and debugging when the entity passes through 3 physical machines is quite tricky. But I can now visualise log files at every step.

Firstly I notice that IsNew is lost when re-persisting so I need to try again using entity state flag, or using a parameter.

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 26-May-2010 11:29:17   

Good luck, and we will be here in case you needed any clarifications.

caffreys
User
Posts: 65
Joined: 25-Jan-2009
# Posted on: 26-May-2010 12:15:01   

OK my console app works pretty much as you would expect. It seems at least over wsHttpBinding the flags are all being carried across. If I create an entity and pass it to the service, here is the current state of the flags (in the service) prior to saving:

IsNew: True IsDirty: True State: New

Back in the client I receive the refetched entity. I then change something and call the same service method. Now the state flags are as follows:

IsNew: False IsDirty: True State: Fetched

And the service indeed creates an update query and all is well.

So back to my main app, i've had a look through the code and what is happening is that I initially create a new entity and persist it. Then I drop the entity as I have no longer any use for it. But later when I want to change the entity I actually call the service's Load method to fetch a copy of the now existing entity. I make a change to this entity and then call the service's save method.

Now, even though the flags in the service are as per the second example, i.e. fetched, not new, an insert query is created rather than an update query.

So if I create, modify, update using the same entity all is well. If I create, fetch, modify then update, the latter fails.

No amount of persuasion setting the flags can I get the entity to update after doing a fetch.

1  /  2