value on screen doesn't match value in the entity

Category: visual studio lshtml

Question

ChristopherDeMars on Thu, 31 Jan 2013 05:44:02


Please forgive my ignorance...

Example:

I have some server side code on one of my tables (Table B, Field alpha, updated method) that does some calculations and updates a field in another related table (Table A, Field gamma).

I have a screen (editTableA) that edits all the fields of Table A, as well as a list of all related records from Table B.

I have a screen (editTableB) that edits all the fields of Table B.

There is a button on the editTableA screen that lets the user edit the selectedItem from Table B list. This buttons opens the selected Table B item in the editTableB screen with the Boundary Option of Save.

Issue (based on Example):

Action steps:

When I open the editTableA screen and select a related Table B item and click the Edit button, I rightly go to the editTableB screen. When I change the value of field alpha and click the save icon, I can see in debug mode that the updated method on the Table B entity is indeed being called as expected. The code in this updated method calculates a new value for field gamma in the related Table A and properly sets the value. After the editTableB screen closes, the HTML client navigates back to the editTableA screen as it should.

Problem:

On the editTableA screen, it shows the old value for field gamma.

I cannot find any way to "refresh" this value other then hitting the browser refresh button (which goes back to the Home screen which is several screens away from where I am in the workflow. I cannot find any screen.Table_A.refresh() method or anything like that.

So I have a situation where the value on the client is not in sync with the value on the server. How do I get the screen to update the value when server side code changes it?


-Christopher DeMars

Replies

ADefwebserver on Thu, 31 Jan 2013 13:31:32


While there may be other methods, what works for me is to have a button that calls a .ashx file handler that is easily able to get any updated values:

Full Control LightSwitch (ServerApplicationContext And Generic File Handlers And Ajax Calls)


The Visual Studio LightSwitch Marketplace

http://LightSwitchHelpWebsite.com

ChristopherDeMars on Wed, 06 Feb 2013 04:03:24


I need this to happen automatically in the background...

Michael, I know you are a proponent of using these .ashx files for several good reasons. I wonder why MS has not built this kind of thing into LS for us - I mean having VS automatically create such pages for CRUD operations on the HTML Client. It seems this would be a very useful addition to LS.

ADefwebserver on Wed, 06 Feb 2013 06:10:05


I need this to happen automatically in the background...

Michael, I know you are a proponent of using these .ashx files for several good reasons. I wonder why MS has not built this kind of thing into LS for us - I mean having VS automatically create such pages for CRUD operations on the HTML Client. It seems this would be a very useful addition to LS.


-Christopher DeMars

You can also use WebAPI, ServerApplicationContext is the key thing not the .ashx file. ServerApplicationContext was created by The LightSwitch team to handle situations such as this.

The Visual Studio LightSwitch Marketplace

http://LightSwitchHelpWebsite.com

ChristopherDeMars on Sun, 10 Feb 2013 02:18:54


Back to my example in the original post...

I see that my changes are not persisting back to the client from the Logic Tier. Since there is no refresh method or relod method, what if I call the load() method once I get back to the screen?

OR, I wonder if the changes are getting back to the client, but the UI is not being updated?


-Christopher DeMars


ADefwebserver on Sun, 10 Feb 2013 02:32:34


Back to my example in the original post...

I see that my changes are not persisting back to the client from the Logic Tier. Since there is no refresh method or relod method, what if I call the load() method once I get back to the screen?

OR, I wonder if the changes are getting back to the client, but the UI is not being updated?


-Christopher DeMars



Can you post some exampe code?

The Visual Studio LightSwitch Marketplace

http://LightSwitchHelpWebsite.com

JCMonto on Tue, 12 Feb 2013 15:43:47


Back to my example in the original post...

I see that my changes are not persisting back to the client from the Logic Tier. Since there is no refresh method or relod method, what if I call the load() method once I get back to the screen?

OR, I wonder if the changes are getting back to the client, but the UI is not being updated?


-Christopher DeMars



Can you post some exampe code?

The Visual Studio LightSwitch Marketplace

http://LightSwitchHelpWebsite.com


I am having the exact same issue. The only way to get the screen data refreshed is using the browser refresh button. But it takes me back to the home screen. Please forgive my ignorance too.

ChristopherDeMars on Thu, 21 Feb 2013 02:38:40


I went ahead and created my own "refresh" button. Here is the code:

myapp.Home.Refresh_Counties_Method_execute = function (screen) {
    // Write code here.
    screen.CountiesSet.load().done(
       function () { alert("Loaded"); }, 
       function () { alert("Error Loading"); }, 
       function () { alert("Still Loading"); }
);
};

When I click the button, I get the "Loaded" alert message, which indicates that the promise object completed successfully and the data was loaded.

BUT... when I click on a County and view the details, it still shows the old value. When I click the refresh button on the browser and it reloads the entire Ls app, then go in a view the details of the county, the correct value appears.

SO... the problem persists. Even after loading the data, the screen control insists on binding to the old value.

My conclusion from all of this is that the data on the server IS being updated correctly, and it *looks* like the data in the Logic tier is updated (I think that's what the load method does?), but FOR SURE the presentation tier is not working correctly. If the Presentation tier (View) is properly bound to the data in the middle tier (View Model), then perhaps the middle tier is not properly bound to the data tier (Model). No matter how you split the hairs, there is a failure somewhere between the data server and the UI.

ChristopherDeMars on Thu, 21 Feb 2013 17:33:24


Can someone explain to me what the load() method does? I traced the code stepping into the msls, jquery, and jquery mobile code, but never once did I see any code that makes an OData call that would reload data from the server.

Does the code have logic in it that looks to see if the data is already there, and then just use the existing data? I did notice a tone of work being done in the DOM, so does the load() method just use the existing data to reload the UI controls?

Bueller... Bueller... anyone...?

Huy Nguyen MSFT on Mon, 04 Mar 2013 22:25:38


Hi Christopher,

Due to time constraint, HTML Client Data stack only has one merge option similar to System.Data.Objects.MergeOption.AppendOnly.

I think your usage of the load() method is correct. Would you please put a breakpoint at OData.read in msls and see if your breakpoint is being hit?

I'd hesitate to suggest this work around, but this may work: Before refreshing, you can try to clear the client value of the EntityA that is modified on the server. Something like this

screen.details.dataWorkspace.ApplicationData.EntityAs._loadedEntities[changedEntityA.details._.__metadata.uri] = null

Hope this helps,

robert-ndev on Thu, 14 Mar 2013 08:09:12


Hi together,

for me this is an important question - how to refresh data in a screen either manually by a refresh button or automatically on loading/activating the screen. For me, it's a common scenario, that some data is modified server-side when saving data (e.g. like automatic updating of "last modified" date), and I would prefer an easy way to get these information back to the client, if needed.

NULLing entities may work, as Huy suggests; but a built-in functionality would be great.

Hope the LS team will address this - your work is really awesome!

Best Regards, Robert

sergey vdovin (aka evolex) on Thu, 14 Mar 2013 08:14:55


+1  

i do need the reload feature as well.

Huy Nguyen MSFT on Thu, 14 Mar 2013 17:07:57


Hi,

To confirm, reload / refresh will be supported in a future release.

Best regards,
Huy Nguyen

sergey vdovin (aka evolex) on Thu, 14 Mar 2013 18:30:57


it means "not in the update 2"?

Huy Nguyen MSFT on Thu, 14 Mar 2013 19:09:46


Hi,

Refresh will not be available in VS Update 2. Possibly a release after that.

Best regards,
Huy Nguyen

Kivito on Sat, 16 Mar 2013 20:51:20


hi!

any practical solution to this? i've tried to clear value on server as Huy suggested and it works for that moment - updated values from server are shown in details screen, but i have side effects - when i try to reopen same details screen from browse screen again, it return values to ones before edited, so again i have to do F5 on browser.. 

Kivito

Huy Nguyen MSFT on Mon, 18 Mar 2013 17:52:22


Hi Kivito,

Can you let me know more details about your application's scenario that requires a refresh?

Thanks,
Huy Nguyen

Kivito on Mon, 18 Mar 2013 19:45:02


thanks for your time Huy!

since i'm not so good in english, explaining this would probably cause bigger confusion, i've created (really) little test project and put it in my dropbox so you can see what's going on (i can send it elsewhere just say where).

in this example i have orders and customers:

- orders have two fields for salesman and revisor, and boolean "approved", if order is approved currently logged on user will be inserted into this field in "updating_orders" on server.. and i need this information to be displayed back on client (but in reality it could be anything else).. i've tried with clearing cache (to null), but after that it looses binding with main collection until i reload page in internet explorer..

to reproduce:

- go tap on one of orders, you'll get to order details, there you have command to "edit order"..

- on edit order screen just flip switch to opposite and save. dialog will close and values on screen will update, but as you will see, "customer" value will dissappear..

- go back to browse orders screen, try again to enter in same Order, you'll see that information on screen does not match (ie. when approved is false, revisor field must be empty, and when it's true, revisor name must be there)..

Kivito

Huy Nguyen MSFT on Tue, 19 Mar 2013 17:36:58


Hi Kivito,

Thanks for sharing your project and scenarios with me. To maintain the data binding with the visual collection, we need to actually clear the client cache reload the visual collection.

Again, merge option will be available in HTML Client v2, so this is a work-around that is using internal-purpose states and we cannot support it in any ways. The work-around here also have limitations - it will loads the visual collection from the beginning, so it's not very useful when you have 10,000 Orders, add 1 new Order, and has to refresh the Visual Collection and scroll to the bottom. (In this case you can build a search screen for it. Or if the Order Visual Collection is based on one Customer, hopefully that Customer does not have 10,000 Orders).

Also, please make sure you only refresh when you do not have any changes on the Client. The code will clear the client entities.

Here's how a Refresh button can be implemented on a Browse Order screen / tab, for example.

myapp.BrowseOrders.Refresh_execute = function (screen) {
    screen.details.dataWorkspace.ApplicationData.Orders._loadedEntities = {};
    screen.Orders.load();
};
Hope this helps. Best regards.
Huy Nguyen

sergey vdovin (aka evolex) on Tue, 19 Mar 2013 18:14:47


That is it - data is reloading from the source with the code.

Kivito on Tue, 19 Mar 2013 21:05:31


hi!

i guess this workaround works just in some cases..

I've created refresh button on browse order screen, and noticed that again it looses binding with customer (it is relationship with customer table), customer just disappears! also when you re-enter details screen, customer field is empty, but local properties for order are really updated..  

Kivito


Nobody expects the Spanish Inquisition! (M.P.F.C.)


Huy Nguyen MSFT on Tue, 19 Mar 2013 21:47:22


Hi Kivito,

It's very limited, hence my hesitation to even chat about this work-around.

In this case you need to clear the link set containing the links between Order and Customers

    screen.details.dataWorkspace.ApplicationData.Orders._loadedEntities = {};
    screen.details.dataWorkspace.ApplicationData.details._linkSets["Order_Customers"]._loadedLinks = {};
    screen.Orders.load();

But if I have to do this I'd rather figure out how to do server logic on the client before saving the entity.

Best regards.
Huy Nguyen

Kivito on Tue, 19 Mar 2013 22:28:27


it works!

thanks Huy, gonna try to use more logic on the client, i've already created ria service which returns current user to ls app and set it on client, but there would be a cases where update from server must be visible on screen in moment after update is made.. so this " reload/refresh/update" is important feature IMHO.. 

nevertheless great job from you and LS team.. thanks again and cheers!

Kivito

sergey vdovin (aka evolex) on Wed, 20 Mar 2013 18:30:37


Hello Huy. Thanks for the helpfull answers.

Another question about the reload:

how to modify the code above to reload the details of the partucular order in orderaddedit screen:

screen.Order

does not have the load() member

Huy Nguyen MSFT on Thu, 21 Mar 2013 00:13:18


Hi Sergey,

screen.Order is an Entity Object so it does not have a load method itself. It will has a refresh method in the next release I promise :)

For now this can work

screen.details.dataWorkspace
    .ApplicationData
    .Orders_SingleOrDefault(screen.Order.Id)
    .execute();

But considering the issue that the Visual Collection will not bind to the correct entity anymore after the work-around, you may want to load the Visual Collection again.

Best regards,
Huy Nguyen

sergey vdovin (aka evolex) on Thu, 21 Mar 2013 07:58:58


Huy, thanks again - it works.

The full code (seems to be working as well):

 screen.details.dataWorkspace.ContractsSystemData.Contracts._loadedEntities = {};
    screen.details.dataWorkspace.ContractsSystemData.Contracts_SingleOrDefault(screen.Contract.id_contract).execute().then(
        function (value) {
            var entry = value.results.length ? value.results[0] : null;
            if (entry) {
                screen.Contract = entry;
            }
        }
        );

Kivito on Thu, 21 Mar 2013 08:24:49


hi Sergey!

i tried with similar code but in my scenario this creates problem after save(s).. in my setup i have browse order -> order details.. I've noticed that main browse order screen would go "out of sync" after few saves and it will constantly repeat save changes dialog until completely refresh with F5 (even with "collection.load()").. do you have similar problems?

Kivito

sergey vdovin (aka evolex) on Thu, 21 Mar 2013 09:41:06


As for now - no - it seems to be working (i checked it right now). But it is all quite a mess in a code if to get all together - rather easy we can make a mistake somewhere.

As for now i'm searching for a way to prevent 

commitChanges

from navigating back - may be you know anything about that?

Kivito on Thu, 21 Mar 2013 16:22:06


hi Sergey!

well, i guess it all depends on structure of screens/project.. unfortunately i didnt have scenario where i would need to prevent commitChanges.. if i come up with something i'll post it.. cheers!

Kivito

Huy Nguyen MSFT on Thu, 21 Mar 2013 16:31:14


As for now i'm searching for a way to prevent 

commitChanges

from navigating back - may be you know anything about that?


Sergey Vdovin

Hi Sergey,

That would be applyChanges.

Best regards,
Huy Nguyen

sergey vdovin (aka evolex) on Thu, 21 Mar 2013 16:47:32


hi, Kivito

no, the task is to prevent navigating back while performing the commitChange

sergey vdovin (aka evolex) on Thu, 21 Mar 2013 16:55:00


Hi, Huy

applyChanges

does not save changes to the database - or it does?


Huy Nguyen MSFT on Thu, 21 Mar 2013 17:37:09


Hi Sergey,

It behaves the same way as commitChanges, minus the navigation.

Best regards,
Huy Nguyen

sergey vdovin (aka evolex) on Thu, 21 Mar 2013 18:00:00


Huy, after your word it seems to be working again ))) (i'v made some tests previously and there were problems)

will keep you in touch )) thanks again )

Huy Nguyen MSFT on Fri, 22 Mar 2013 02:04:35


Hi everyone,

I just think of a less destructive work-around to refresh one single entity from the server. It has the following benefits:

  • It does not require deleting the LightSwitch entity object (like this screen.details.dataWorkspace.ApplicationData.EntityAs._loadedEntities[changedEntityA.details._.__metadata.uri] = null).
  • It does not require deleting every LightSwitch entity objects on an Entity Set (like this  screen.details.dataWorkspace.ContractsSystemData.Contracts._loadedEntities = {}). So the Visual Collection remains correct, navigating between screen to screen works.

Here's the catch:

  • It only works for one single entity. So no mass refresh unless you do it for each of the entities you care about.
  • It still relies on internal implementation state. (But less destructive).
  • It does not work well for navigation properties. For example if a change to an Order's Customer happens on the server, the client will not show it.

Let's say I have a simple app that Browse Northwind's Order and edit them.

Here's the sample code that will Refresh one Northwind's Order, implemented as a Refresh button on an Add Edit Order screen.

myapp.AddEditOrder.Refresh_execute = function (screen) { var order = screen.Order; // Can only safely do this if the order is not modified. if (order.details.entityState !== msls.EntityState.unchanged) { return msls.showMessageBox( "Cannot refresh the order because it was changed.", { title: "Cannot refresh" }); } // Get the current Order ID var orderID = screen.Order.OrderID; // Construct a new data workspace that is isolated from the
// application's data workspace and load a separate instance
// of this Order from server. var dataWorkspace = new myapp.DataWorkspace(); return dataWorkspace.NorthwindData
.Orders_SingleOrDefault(orderID) .execute().then(function (result) { // Query succeeded. If the Order still exists on
// server, merge it. var serverOrder = result.results[0]; if (serverOrder) { // Replace the raw JSON object representing the
// Order. order.details._ = serverOrder.details._; // Raise changes notification for all storage
// properties, so the UI can update. order.details.properties.all().forEach(
function (prop) { if (prop instanceof
msls.Entity.Details.StorageProperty) { // The property's value. prop.dispatchChange("value"); // The entity's property value. order.dispatchChange(prop.name); } }); } }, function (error) { msls.showMessageBox(error, { title: "Refresh failed" }); }); };

You may choose to refresh at different point in time, through different mechanism (maybe automatically after a save when you know the server is going to change the entity and cause conflict), but the principal remains the same as above.

Hope this helps. Best regards.
Huy Nguyen


sergey vdovin (aka evolex) on Fri, 22 Mar 2013 13:47:06


i have got the similar "out of sync" after the deployment - at the development computer i have IE10 - it works there but in IE9 and in IE10 (IE9 mode) there is "something":

I have several places to select the values from the references like this (not sure if this is the right way but it works):

myapp.AddEditContract.SelectDepartment_execute = function (screen) {
    // Write code here.
    myapp.sela = screen.Contract.MSPLT_Department_UserView1;
    myapp.showBrowseMSPLT_Department_UserViews(
        options = {
            afterClosed: function (screen2, action) {
                screen.Contract.MSPLT_Department_UserView1 = myapp.sela;
            }
        });
};

myapp.sela contains selected in the BrowseMSPLT_Department_UserViews value.

as i told it works but it asks to save the changes all the time - a little bit annoying. I added applyChanges:

myapp.AddEditContract.SelectDepartment_execute = function (screen) { // Write code here. myapp.sela = screen.Contract.MSPLT_Department_UserView1; myapp.showBrowseMSPLT_Department_UserViews( options = { afterClosed: function (screen2, action) { screen.Contract.MSPLT_Department_UserView1 = myapp.sela;

myapp.applyChanges(); } }); };

the save changes dialog disappeared then but when i go to the main screen and then back to the same AddEditContract, the value of screen.Contract.MSPLT_Department_UserView1 is empty.

even the latest Huy refresh does not help here (of cource i could make a mistake somewhere, but is seems to be ok) - only after the page reloading the value is at the screen.

Can you check if the problem exists in IE10?

Kivito on Fri, 22 Mar 2013 16:03:26


hi (all)!

just tried in simple demo app and it seems to work ok.. tested in IE10 and latest chrome (only on development pc)..

great job Huy! would this be implemented "out of the box" in next update?

Kivito


Nobody expects the Spanish Inquisition! (M.P.F.C.)



Huy Nguyen MSFT on Fri, 22 Mar 2013 16:29:02


Hi Sergey,

I don't have the source code of the CTP4 around, but I remembered from another thread that afterClosed is not in CTP4. Would you please set a breakpoint and see if the afterClosed is invoked. Just to make sure.

screen.Contract.MSPLT_Department_UserView1 is a reference property, right? Also, in your BrowseMSPLT_Department_UserViews are you also setting myapp.sela before closing?

Thanks,
Huy

Huy Nguyen MSFT on Fri, 22 Mar 2013 16:29:43


Hi Kivito,

Yes, it will be implemented by LIGHTSWITCH JavaScript Runtime in the next release (possible VS 12 timeframe).

Best regards,
Huy


sergey vdovin (aka evolex) on Fri, 22 Mar 2013 16:35:35


Hi, Huy.

yes, afterClosed is invoked.

yes, indeed.

Huy Nguyen MSFT on Fri, 22 Mar 2013 17:28:41


Hi Sergey,

The issue you're seeing does not seem to relate to data refresh.

screen.Contract.MSPLT_Department_UserView1 = myapp.sela should persist the change across the application. Are you seeing the issue only on IE 9, and IE 10 works fine?

If you can share your project, or reproduce it in a simplified project, I can take a look.

Best regards,
Huy Nguyen

sergey vdovin (aka evolex) on Fri, 22 Mar 2013 17:34:01


E10 - working, IE9, IE10 (IE9 mode) - not workig

will try to share

David Ching on Fri, 22 Mar 2013 17:37:28


Wow!  Thanks Huy!  :-)  I will try this.  But, can you suggest an event to hook on the HTML client side to execute this code?  Is myapp.onSaveChanges() a good place?  If so, any sample code?  Then this solution would would be completely broilerplate that is a pain to include, but it would be copy & paste.

Thanks,
David

Huy Nguyen MSFT on Fri, 22 Mar 2013 19:48:02


Thanks for your feedbacks David.

There's no global onSaveChanges event for the application though. And at that low level, it will be pretty hard for the framework to figure out which saved entity to refresh.

  • Even though by default the UI drives you towards saving changes to one entity at a time, the framework does support editing multiple entities and save them all in one batch request.
  • Also, refreshing an entity like in this scenario is not the common case. The majority of data sources out there will accept the changes and does not make any subsequent changes to the entity. And in those scenario it will be a waste of bandwidth to refresh after every changes.

We strive to make the common scenarios as effective as we can, and provide good API to handle the custom cases. We'll take the feedbacks into consideration to come up with better APIs to support this.

Best regards,
Huy Nguyen

sergey vdovin (aka evolex) on Fri, 22 Mar 2013 19:57:44


here is the test project with the DB script - it is actually not working in any tested IE version (9,10).

about afterClosed - it is working (and it is actually a logical thing) when you use myapp.navigateBack and is not working when you open the previous screen with myapp.show... - just have figured it out - it was also in my code but it does not affect the problem in the test project above



Sergey Vdovin


David Ching on Fri, 22 Mar 2013 21:03:13


Huy> Also, refreshing an entity like in this scenario is not the common case. The majority of data sources out there will accept the changes and does not make any subsequent changes to the entity.
 
I’m sorry, but is it not extremely common to have your SQL Server table set with
 
    (Is Identity) = true?
 
 
That is, the primary key (Id) will be set by SQL Server, on insertion of the record?
 
I’m a relative newbie to all this, so please correct me if I’m wrong.  All the books I’ve read encourage setting SQL server up this way to ensure referential integrity.
 
Thanks,
David

David Ching on Fri, 22 Mar 2013 21:06:32


> There's no global onSaveChanges event for the application though
 
Has something changed since Stephen Provine’s blog:  http://blogs.msdn.com/b/lightswitch/archive/2012/11/13/new-lightswitch-html-client-apis.aspx which shows myapp.onsavechanges()?
 
Is there a way to get notified when the Save button is clicked (or more importantly, when the Save has completed), to run your code to refresh the single entity that was saved?
 
Thanks,
David

Huy Nguyen MSFT on Fri, 22 Mar 2013 21:41:59


Hi David,

Is it not extremely common to have your SQL Server table set with
    (Is Identity) = true?
That is, the primary key (Id) will be set by SQL Server, on insertion of the record?

Yes, it's extremely common. In fact if you create the Entities yourself using LightSwitch (not attaching), that's how it works; and it works without the need of refresh. You can try that quickly. But in that case, here's what happened:

  1. Client creates new entity and send to the server.
  2. Server save the entity. SQL database updates Primary Key within one transaction.
  3. Server returns the new entity to the Client, including the value of the primary key. Everything is correct.

Here's when you need refresh:

  1. Client creates new entity and send to the server.
  2. Server save the entity. SQL database saves the entity.
  3. Server returns the new entity to the Client.
  4. Immediately another transaction happens on the data source that modifies the entity. Now the client record is out of date.

onSaveChanges event:

Sorry, my memory corrupted. There is, you can use it. But I think the framework is pretty much hands off when you handle that event. You will have to manually invokes the appropriate data service's saveChanges method, then figure out which entity you want to refresh, etc.

Best regards,
Huy Nguyen

Huy Nguyen MSFT on Fri, 22 Mar 2013 21:52:47


Hi Sergey,

There's no LightSwitch project in the link you provided above though. Would you please check?

Thanks,
Huy

sergey vdovin (aka evolex) on Fri, 22 Mar 2013 21:59:10


Hi, sorry the project is here:

https://dl.dropbox.com/u/48588709/toshare.zip

Huy Nguyen MSFT on Fri, 22 Mar 2013 22:09:03


Hi Sergey,

I'm having problems extracting this file. I can open it, but trying to extract it results in Error 0x800004005: Unspecified error.

sergey vdovin (aka evolex) on Fri, 22 Mar 2013 22:15:14


Let's try this one (the previous was made with 7z), the following is native zip folder:

https://dl.dropbox.com/u/48588709/tosharen.zip

sergey vdovin (aka evolex) on Fri, 22 Mar 2013 23:33:33


Huy.

ok, it was about links - missed it somehow - it works

screen.details.dataWorkspace.ApplicationData.details._linkSets["Order_Customers"]._loadedLinks = {};

Thanks

Huy Nguyen MSFT on Sun, 24 Mar 2013 01:49:56


Hi Sergey,

I looked at your application, and it looks like you're trying to build a custom screen to replace the Modal Picker. The problem with your approach is that you are writing a lot of code to customize the navigation and saving of the application. When I remove the code that are doing the refreshing work your sample app works fine. So I think along the way things get too complicated and bugs were introduced. I would suggest doing this:

1. Let say you have Customer - Order and you want to build a custom screen to pick an Order Customer.
2. On the Edit Order screen, you add a button to launch a new screen that show a Custom List of Orders. But I would make this new screen another Edit Order screen, so that it is bound to the order being edited. Then I would add a separate Customers query to the screen. Then all the code I have to write is

myapp.SelectOrderCustomer.created = function (screen) {
    // When Customers selectedItem changed, set the Order Customer
    screen.Customers.addChangeListener("selectedItem", function () {
        screen.Order.Customer = screen.Customers.selectedItem;
    });
};

And I don't have to worry about navigating / saving the workspace / things getting out of sync. Everything works as expected.

I have a sample project here that you can try. The database is built in. Just F5 and add or edit an Order. The Select Customer screen can be filtered by USA, UK or Australia.

Hope this helps. Best regards.
Huy Nguyen

David Ching on Wed, 27 Mar 2013 17:35:05


Hi Huy,
 
I did verify that creating a Table in LS does allow 1) adding an item, 2) editing the same item, and it works fine (without manual refresh).
 
But it does not work on my existing database table, even with a new LS project accessing it.  (Same table used in the project I e-mailed you and you helped me with about jobs).
 
I opened the LS generated table in Server Explorer and saw it set the Id field to Primary Key and (Is Identity) to be true, same as mine.  So I don’t know why my database does not work and the LS one does.  Your #4 seems to be occurring:
 
  #4 Immediately another transaction happens on the data source that modifies the entity. Now the client record is out of date.
 
 
Any idea what the ‘another transaction happens’ is?
 
Thanks,
David
 

Huy Nguyen MSFT on Thu, 28 Mar 2013 04:26:56


Hi David,

I'd check for any triggers that can be invoked after the new entity is saved into the database. Do you know which property on the new entity is modified after it is saved to the database? You can quickly check this by:

  1. Open 2 instances of the browser and load the same application.
  2. Instance 1: Add a new entity and save.
  3. Immediately open its View or Add/Edit screen again.
  4. Instance 2: Load the same list.
  5. Open the same entity View or Add/Edit screen.
  6. Are there any visible differences between them?

If there are no visible differences, you can use Fiddler or IE Network capture to get the JSON response after step 4 and step 5 and it should show the difference.

Best regards,
Huy

David Ching on Thu, 04 Apr 2013 17:19:18


Hi Huy,

Thanks!  Still no explanation why the successful LightSwitch generated database works and importing mine doesn't.  I cannot see any difference between them!

I used the IE Network Capture to see the server response when a new entity is added.  The entity’s ID (primary key, which is an IDENTITY field) has changed, in both the successful and failed cases, since the database generated the ID upon insertion.

 
The difference (at least one of them) is that in the failed case, I had to write any custom code to set a default primary key Job Id:  e.g.
 
    myapp.JobsAll.created = function (entity) {
          ...
        entity.JobId = -1;
    };
 
to prevent the message box:
 
  “JobId:  This field is required”
 
from being shown and the Save aborting.  But this was not required in the successful case.
 
This code causes this ID value of –1 to be posted to the server as –1, whereas in the successful case, it was not specified.  I’m guessing LightSwitch detects the difference because it was specified and flags it as modified (thus preventing future editing/deleting of the record until the app is refreshed with F5).
 
Another difference is in the Designer, if I click on the successful ID field, the right pane shows only
 
 
 

but in the failed case it shows:

I compared the service.lsml files; I had to add the <Hidden /> attribute to the failed one:

<EntityType.Properties>
      <KeyProperty
        Name="JobId"
        PropertyType=":Int32">
        <KeyProperty.Attributes>
          <Required />
          <NotSearchable />
          <Hidden />
        </KeyProperty.Attributes>

But this did not make the Designer show the limited display of the Successful one.  I thought there might be a bug in how LightSwitch imported primary keys of an existing table, but have not found the setting that affects that.

The failed table has this definition for the ID field:
 
CREATE TABLE [dbo].[JobsAll] (
    [JobId]               INT            IDENTITY (1, 1) NOT NULL,
    [CustomerCompany]     NVARCHAR (MAX) NOT NULL,
    ...
    [RowVersion]          ROWVERSION     NOT NULL,
    CONSTRAINT [PK_JobsAll] PRIMARY KEY CLUSTERED ([JobId] ASC)
);
 
 
The successful (LightSwitch generated) table has this one:
 
    CREATE TABLE [dbo].[LSJobs] (
        [Id]              INT            IDENTITY (1, 1) NOT NULL,
        [RowVersion]      ROWVERSION     NOT NULL,
        [CustomerCompany] NVARCHAR (255) CONSTRAINT [DF_LSJobs_CustomerCompany] DEFAULT ('') NOT NULL,
        ...
        CONSTRAINT [PK_LSJobs] PRIMARY KEY CLUSTERED ([Id] ASC)
    );
 
 

These are essentially identical except for naming changes.

I'm at a loss here and short of a quick find am tempted to just go with the LightSwitch generated table, but that might require me to re-work my app from scratch.  :-(

Thanks,
David