iSeller Commerce
iSeller POS Retail
iSeller POS F&B
iSeller POS Express
Crosslight
WebUI
ClientUI
What's New
Download Trial
Web Solution
Mobile Solution
Enterprise Solution
Custom Development
Blog
Community
Latest Development Blogs
ForumPostTopic
Browse By Tag
Hi,
We've probably asked this before, but didn't quite get the answer we're looking for. But, what we need to know is how to update a field on the CLIENT side without raising property changed; thus making the record state as clean. Say for example we have table called Table1 that has columns (num1, num2, and total). And all of which are presented to the user using the Form Builder, which accepts inputs in fields num1 and num2 while the total field is a readonly value that is being calculated from the server side. That being said, once the num1 and num2 fields have been populated and sent to the server, the total field on the CLIENT side should be updated with whatever value has been generated from the server side. This way, both values of the TOTAL field on CLIENT and SERVER side are synchronized.
How can we properly implement this with the current version of Enterprise Framework? Does the framework automatically fetch updated data from the server on ExecuteSave? If not, how do we execute an existing command to fetch the updated data from the server and refresh the data within the form?
Please advise. Thanks!
Hi Jimmy,
Insert, Update, and Delete operation on SumListViewModel will be reflected on the Editor A too. To ensure the data, it is recommended that all data operation to Sum Model was done through SumRepository.
Best Regards,Leo
The default ViewModel ExecuteSave and the server side (ex: WebAPI) default save behaviour (db.SaveChanges(saveBundle)) that has been provided by crosslight has implement this kind of behaviour. By default, server side will include the updated value in it's return value and then client side (crosslight) will make an update accordingly based on it.
In your scenario, you may want to prevent the editor to be closed when save has been done succesfully. This could be achieve easily by setting CloseEditorOnSaveSucceed (in ViewModel) to false.
Awesome! If that's the case, our next question is... does that include updated data for RelatedEntities? Do you have a sample on git that demonstrates these behaviors?
Thanks!
For your information, you could include the updated data from RelatedEntities. You could set the logic for the related entity changes in EntityContextProvider's BeforeSaveChangesDelegate and include related entity in changedEntityList. Currently we did not have a sample that demonstrate these behaviour yet, as a reference, the code below could be used to achieve it:
db.BeforeSaveChangesDelegate = (context, changedEntityList) =>{ List<object> temp = new List<object>(); RelatedEntity relatedEntity=null; var items = changedEntityList.OfType<Item>(); var relatedEntities = changedEntityList.OfType<RelatedEntity>(); if (items.Count() > 0) { foreach (Item item in items) { if (relatedEntities.Count() > 0) { relatedEntity = relatedEntities.SingleOrDefault(o => o.ItemID == item.ItemID); } if (relatedEntity == null) { relatedEntity = db.Context.RelatedEntities.SingleOrDefault(o => o.ItemID == item.ItemID); temp.Add(relatedEntity); } // changes the related entity. relatedEntity.Count++; } if (temp.Count > 0) { entityList = changedEntityList.Concat(temp).ToList(); } } };
Hi Leo,
Thanks for your response. Does it make sense that if DataEditorViewModelBase<>.TrackRelatedEntityChanges is set to true for the client side, the code snippet above should have already been incorporated with the EntityContextProvider?
TrackRelatedEntityChanges was not mean for the behaviour where you want to include other related entities on server response. TrackRelatedEntityChanges was mean to indicate whether changes to related entities should be tracked in the editing process. Example: When you save Item model with changes only to it's RealtedEntity and TrackRelatedEntityChanges was set to false, save action won't be executed because the main model (Item) was not modified (ViewModel.IsDirty is false). But with TrackRelatedEntityChanges set to true, changes to RelatedEntity will make ViewModel.IsDirty set to true which mean that save action will be executed.
Thanks for your response.
We understand that when setting the TrackRelatedEntityChanges to true, the service will also track changes on related entities. And that's a great feature, especially on the client side. But that's not our concern. Our concern is... while the behavior of updated data (either computed value or processed by a trigger) are being returned with the main entity, how come updated data for related entities are not included? Don't you think this should have been considered as a built-in feature with EntityContextProvider, instead of coding our way around it? While the code snippet you provided might work, it would be great to have this feature already built-in. Especially when tracking of changes to related entities is on or set to true on the client side.
As always, thanks!
About the updated data for related entities, Did you want to achieve the following scenario?
Modified Main Entity in client side and save the changes -> Server Make calculation and Modified related Entity -> Server Return Main Entity and Related Entity that has been changed -> Client side update All changes.
If not, please elaborate the scenario that you would like to achieve.
Yes, I believe that's what we want to achieve. Is that supported, or only changes to main entity are being returned to the client? While the form editor can expose fields from related entities by using BindingAttribute, the changes we are expecting from the server should also reflect on the client side to synchronize the changes regardless if it's on the main entity or related entities.
After further investigation I have found that if you modify related entity in BeforeSaveChangesDelegate, The changes to related entity will also included in the return value. So the changes in server side that is done in this request will be reflected on client side.
We can't seem to replicate this behavior, kindly provide a sample that demostrates such behavior. In connection to this, it doesn't seem to be working if we manually invoke "base.ExecuteSave(null);" method in a form editor. Below is a sample on how we are calling the Save method, and expecting return values that were generated or computed from the server.
private void ExecuteMeter(object parameter) { var p_Parameter = new NavigationParameter() { ModalPresentationStyle = ModalPresentationStyle.FormSheet, NavigationMode = NavigationMode.Modal, EnsureNavigationContext = true }; this.NavigationService.Navigate<MeterListViewModel>(p_Parameter, this.OnExecuteMeterDone); } private void OnExecuteMeterDone(NavigationResult result) { if (result.Action == NavigationResultAction.Done) { this.Item.ModifiedOn = DateTime.Now; this.CloseEditorOnSaveSucceed = false; this.ExecuteSave(null); } }
Please provide the sample as requested, and see how we can improve the code snippet above to simulate the behavior when manually invoked. Many thanks to your support! Much appreciated.
Any updates on this? Thanks!
Sorry for the wait. I have create a simple sample that demonstrate this behaviour. Please find the sample here.
Regarding invoke ExecuteSave on navigation result Callback, I have report this issue under CROS-805. I will keep you informed with the progress of CROS-805.
Thank you for the sample. Do you have a flowchart or a document that illustrates the flow of events or delegates used by the EntityContextProvider? We need to understand how and where to put processes within these events as necessary. The sample you have given us illustrates calculations done on WebApi layer, not on the database itself. It does make sense to process calculations that way if calculations are handled on WebApi controller. However, we need to fetch calculated values after the changes have been saved to the database, not before. The reason being... there are calculations being processed ON AFTER UPDATE triggers that rely on incoming data from client side.
Okay, let's try to make things simpler. Let's forget about related entities for the time being. Take a look at the table below, with a trigger. This sample indicates that Gross and CommissionAmount are calculated via trigger. In this design, the mentioned fields can be overriden in case the actual cash is not the same with the calculated value. Be that as it may, whatever values generated for these fields should also be fetched by the EntityContextProvider on its return to the client.
USE [IntersoftSample] GO CREATE TABLE [dbo].[Collection]( [CollectionId] [bigint] IDENTITY(1,1) NOT NULL PRIMARY KEY, [Amount] [money] NOT NULL, [Refund] [money] NULL, [Gross] [money] NULL, [CommissionRate] [smallint] NOT NULL, [CommissionAmount] [money] NULL ) GO CREATE TRIGGER [dbo].[PostCommission] ON [dbo].[Collection] AFTER UPDATE AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; -- Insert statements for trigger here IF (NOT UPDATE(Gross) AND NOT UPDATE(CommissionAmount)) BEGIN UPDATE [dbo].[Collection] SET Gross = INSERTED.Amount - ISNULL(INSERTED.Refund, 0), CommissionAmount = (INSERTED.CommissionRate / 100.00) * (INSERTED.Amount - ISNULL(INSERTED.Refund, 0)) FROM [dbo].[Collection] CO INNER JOIN INSERTED ON CO.CollectionId = INSERTED.CollectionId END END GO
Based on the current behavior of EntityContextProvider, is it capable of fetching the calculated values for Gross and CommissionAmount in this scenario if an update is made from client side for Amount, Refund, and/or CommissionRate? Please advise.
db.AfterSaveChangesDelegate = (context, affectedRows) =>{ var dbEntityEntry = context.ChangeTracker.Entries(); List<DbEntityEntry> collectionEntries = dbEntityEntry.Where(o => o.Entity.GetType() == typeof(Collection)).ToList(); foreach (DbEntityEntry collectionEntry in collectionEntries) { collectionEntry.Reload(); } };
db.AfterSaveChangesDelegate = (context, affectedRows) => { var dbEntityEntry = context.ChangeTracker.Entries(); List<DbEntityEntry> collectionEntries = dbEntityEntry.Where(o => o.Entity.GetType() == typeof(Collection)).ToList(); bool relatedAttached = dbEntityEntry.Count(o => o.Entity.GetType() == typeof(ReleatedEntity)) >= 1; foreach (DbEntityEntry collectionEntry in collectionEntries) { Collection collection = collectionEntry.Entity as Collection; if(relatedAttached) context.Entry(collection.ReleatedEntity).Reload(); else context.Entry(collection).Reference(c => c.ReleatedEntity).Load(); } };
Awesome! Yes, we've though about the first option you mentioned... but that's not going to work on our design, because the fields can be overridden on the client side. Looks like the second option is the way to go. And yes, we also need changes to related entities as you anticipated. We'll give it a try and see how it's going to work.
Many thanks!
The sample code you provided works because the data being modified on SumEditorViewModel is the same instance with that of the Item.Sum entity being passed by the ResultEditorViewModel. But, how do you pass data from a DataEditorViewModelBase to another DataEditorViewModelBase with a DataListViewModelBase in between, on the same manner that a view model sends data to a DataEditorViewModelBase? Say an Editor opens a new list and sends an instance of this.Item.SomeRelatedList property (is this even possible? sending list data from an editor?). Then, each item from SomeRelatedList intance are modified through each editor... and that any change to each item should be reflected to the main editor view. Picture this... > EDITOR A > LIST > EDITOR B. The calculated results for changes on EDITOR B should be reflected on EDITOR A as well.
Based on your description, it seem you want to do the following scenario:1. On Editor A Pass RelatedList to a List View2. On ListView, you could go to Editor B to edit item on the list.3. Changes done in Editor B to one of the related item should be reflected on Editor A.I have modified my sample to fit the above scenario. You should be able to get the sample from here. Please note that the sample used crosslight 4. For your information, The data on Editor A and Editor B is the same instance, it just that the changes on Data not reflected on Editor A. So in the sample, I manually trigger property changes on Editor A to update Editor A view. I will discuss it with crosslight development team whether this is the right way to do it. Will give you an update later.
or
Choose this if you're already a member of Intersoft Community Forum. You can link your OpenID account to your existing Intersoft Social ID.
Choose this if you don't have an Intersoft account yet. Your authenticated OpenID will be automatically linked to your new Intersoft account.
Enter your Wordpress Blogname