Is there an event in Breezejs that is fired when an entity is added or deleted? - breeze

I have looked around for an answer to this, but I have come up dry so far. What I would like to do is have an event handler in a specific view model that listens to Breeze for entities being added or deleted so the view model can take appropriate action on the array it is managing. Does such an event exist?
I have a Jobs view model for my Jobs view that contains, among other properties, a ko.observableArray of Job entities, and a NewJob view model for my NewJob view. Both view models share the same data service. I would like to simply use the DataService from the NewJob view model to insert a new Job entity in to Breeze, and then have the Jobs view model simply subscribed to an event so it would know to add the new Job into it's Jobs array.
TIA

The Breeze EntityManager has an entityChanged event that may be used like so:
var em = new EntityManager( {serviceName: "api/NorthwindIBModel" });
em.entityChanged.subscribe(function(changeArgs) {
// This code will be executed any time any entity within the entityManager is added,
// modified, deleted or detached for any reason.
var action = changeArgs.entityAction;
var entity = changeArgs.entity;
// .. do something to this entity when it is changed.
});

Related

Grid filled by a data provider: how to trigger an action after refreshment?

In Vaadin 8.2, I have a Grid bound to a bean using a data provider (AbstractBackEndDataProvider). Data is fetched from a DB, filters are applied:
Grid grid = new Grid<>();
grid.setDataProvider(dataProvider.withConfigurableFilter()); // dataProvider derives from AbstractBackEndDataProvider<T,F>
The essential flow is the following: user inputs an item id in a form and submits, a submit event listener gets the user input, creates a new filter and updates the data provider:
filterSubmitButton.addClickListener(event -> {
try {
ItemListFilter filter = new ItemListFilter(
itemFilter.getValue(), // itemFilter = new TextField();
);
filterBinder.writeBean(filter);
dataProvider.setFilter(filter);
} catch (ValidationException e) {
//...
}
});
When the data provider gets the filter updated it calls a service to fetch new items from DB with the filter applied (to the DB query). Vaadin takes care of refreshing the Grid with new data afterwards.
What I want is to have a callback at this last moment. Say an use case would be to check if a filtered fetched result set contains only one item, to select this item in the Grid (which in its turn will trigger an event showing item details in another pane). Or to select the first grid row after initial list is loaded
But the problem is that there is neither grid.addRefreshListener() nor dataProvider.addRefreshmentListener(). Any other listeners do not seem to apply in this case.
Thanks for any help in advance.
The only solution I've found is a trade-off.
The item list presenter (which handles the view with the grid) passes its com.vaadin.event.EventRouter to dataProvider (I've modified the dataProvider to hold an EventRounter as a member). And now instead of streaming DB results directly from the dataProvider I fire an event that the data is fetched (using EventRouter). The presenter can subscribe to this event and then delegate it to the presenter of the details panel. There you can read the fetched results (the event contains them), check if there's only one entry and open it by id.
public class ListItemDataProvider extends AbstractBackEndDataProvider<Item, ItemFilter> {
//...
#Override
protected Stream<Item> fetchFromBackEnd(Query<Item, ItemFilter> query) {
// ...
List<Item> fetchedResults = service.fetch(query.getOffset(), query.getLimit(), orderBy, getFilter(query));
eventRouter.fireEvent(new FilteredDataFetchedEvent(this, fetchedResults));
return fetchedResults.stream();
}
}
#Controller
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ItemListPresenter {
// ...
public void addFilteredDataFetchedListener(ItemListView.FilteredDataFetchedListener listener) {
eventRouter.addListener(FilteredDataFetchedEvent.class, listener, FilteredDataFetchedListener.FILTERED_DATA_FETCHED);
}
}
Few notes:
This is not exactly what I needed. Yes, I can do the most of my use case, if the filter is applied the list is reloaded and the details view gets the event to reload too. But I can't re-use the "selectionChanged" event listeners for this and actually can't select a row in the grid at all (just because the event from dataProdiver is thrown before the grid is updated).
This is sort of a dirty trick, because now the dataProvider throws events and deals with eventRouters of views/presenters. On the other hand Vaadins data providers anyway do allow to subscribe on events. Using that out-of-box grid-dataProvider reactive binding we just don't have an event fired after data is fetched, so we do it in a custom way.
What could also work is use the given Vaadin's subscriber dataProvider.addDataProviderListener and delegate from there an event containing the filled filter and just act independently catching that event in the details panel. But then you would need to execute sql queries twice (which can be costly) or cache them etc. This brings no benefits in comparison to the given and is still a trade-off.
When you invoke dataprovider.refreshAll(), the associated grid is automatially refreshed. Therefore, after following lines in your code:
filterBinder.writeBean(filter);
dataProvider.setFilter(filter);
add logic to get size of returned records (eg. dataprovider.size()) and if that equals one (01), invoke some other logic to select the one record and display its details in other panel.

EF: Adding new item in a collection and notifying all other collections that new item is added

Lest say that we have several pages that retrieve items from the same EntitySet. If I add a new entity on one page, I need to add it to both EntitySet collection and myCollection:
Context.EntitySet.Add(item);
myCollection.Add(item);
What is the best way to notify other pages that new item is added (or deleted)? Editing an entity is no problem, since all pages get change notification without any problem.
Instead of binding to different instances of IEnumerable<T> myCollection, the recommended approach is to bind directly to Context.EntitySet<T>. EntitySet<T> implements INotifyCollectionChanged and INotifyPropertyChanged interfaces. When you bind to the same instance of EntitySet<T>, each page may be notified of changes by responding to the EntitySet<T>.CollectionChanged event. For example:
// Page 1
_myCollection = Context.EntitySet<MyItem>();
_myCollection.CollectionChanged += MyItemsChanged;
...
// Page 2
_myCollection = Context.EntitySet<MyItem>();
_myCollection.CollectionChanged += MyItemsChanged;
When any page adds or removes from the collection, all pages are notified.
In regards to your comment, IEnumerable<T> does not implement the INotifyCollectionChanged interface and does not publish any change notifications. The best results come from using the EntitySet<T> directly.

ASP.NET MVC, finding if model has changes

How do I use the modelstate in ASP.NET MVC to see if the user has made changes or not?
For example:
A form gets loaded and the user has the ability to make changes on the form.
After making changes, users clicks apply or cancel button.
Clicking the button brings control back to my controller where I do validation, then commit to database.
What if the user clicks the apply button without making changes? I want the controller to be able to detect that nothing was altered by the user and send the appropriate message.
Is there a way to detect this using modelstate? Or is there another way to do this?
Thanks
We use automapper to take our domain entities and map them to view models. Something like this:
var viewModel = Mapper.Map<DomainEntityClass, ViewModelClass>(entity);
Then, on post back of the view/page, we can test if they are different by loading the entity again and comparing it to the mapped view model:
var entity = Repository.Get<DomainEntityClass>(id);
var mappedEntity = Mapper.Map<DomainEntityClass, ViewModelClass>(entity);
// code to compare mappedEntity to incoming viewmodel
//
// or build an equality comparer to test them
The idea is that if the mapping configuration hasn't changed, the Mapper.Map call (given the same entity) should return matching view model objects if nothing has changed.
For help building equality comparers, check here - http://msdn.microsoft.com/en-us/library/ms132151.aspx

Can you reuse a LINQ datacontext in an ASP.NET MVC application with strongly typed views

I am somewhat confused on how to properly use LINQ in combination with an ASP.NET MVC strongly typed view.
My situation is as followed.
1) I retrieve a single object through LINQ
2) I pass this LINQ object to a strongly typed view. (edit form)
3) The user submits the form and the controller receives the LINQ object.
So my questions are:
1) Is the object the controller method receives after the submit still tight to the original datacontext or is it a newly created instance?
2) What is the preferred way to store the updated values in the database using LINQ. If it is still tight to the original datacontext a simple call to SubmitChanges() would be sufficient. But how to maintain the datacontext?
I would like to be able to save these data objects without having to use really ugly linq update statements. (Like retrieving the row again and manually update its values)
Any help, insights and preferably code samples would be appreciated.
Regards,
Dennis
You should fetch the existing object again, and update it, somthing like:
public ActionResult Edit(int ID)
{
DataEntity entity = _service.GetMyEntity(ID);
UpdateModel(entity);
//Saving code goes here.
return View();
}
The entity that you are talking about retrieving is no longer attached to the data context.
Is the object the controller method
receives after the submit still tight
to the original datacontext or is it a
newly created instance?
It won't be attached to a DataContext at all on Submit.
What is the preferred way to store the
updated values in the database using
LINQ. If it is still tight to the
original datacontext a simple call to
SubmitChanges() would be sufficient.
But how to maintain the datacontext?
The preferred way is to create a new DataContext, retreive the old object from the database, update the fields based on what was submitted to your Form, and then save the updated copy from the new Context.
Use your linq objects to get the data and put it into a POCO (plain old c# object) Model specifically for your view.
Your linq2sql/ef objects can be structured to store data which do not necessarily reflect your views
Your model can be updated and validated by the MVC framework by placing it in the call to the post.
public ActionResult EditPost(EditModel model)
When you have a valid model you can then transfer the data back to the database via your linq2sql/ef objects
if(ModelState.IsValid) // save to db
Create a DataContext when you need to load and save, don't persist it
1) It is a new instance (through model binding feature) and it is not attached.
2) It depends, but the best would be probably new instance using(var context = new DBContext()) etc.
3) The most simple thing is fetch the object, pass in the updated values and SubmitChanges() (as you describe). Other option is described in this article. You create new instance of the object (or you have it from the model binding), attach it to the context and submit changes:
public ActionResult Test(MyModel model)
{
DbContext.Models.Attach(model);
DbContext.Refresh(RefreshMode.KeepCurrentValues, model);
DbContext.SubmitChanges();
}

form data posted using linq-to-sql not showing until I refresh the page

I have an asp.net mvc app with a form.
When you submit the form, it adds records to the sql database with linq-to-sql. After adding the records, the controller displays the form again, and should show those new values on the form. But, when it displays the form, the values are blank, until you refresh the page.
While tracing through the code, I can see the records being added to the database when they are submitted, but the view doesnt display them, unless I refresh. The view is not the problem, it just displays the view model, which is missing the new records immediately after the post.
I know this is kind of vague, but wasnt sure what parts of code to include here.
Could this have something to do with data context life cycle? Basically, there is a data context created when the form is posted, then a different data context is created in the method that displays the form.
Any suggestions on what might be causing this?
Update:
There's a whole lot of code I could post here, but I'll try to give you a simplified version:
This code maintains a schedule of volunteer assignments
The view uses a view model with a list of schedules, and displays a form of schedules and their associated assignments. (child records)
When the form is posted, with a new schedule & assignemnts, a schedule record is created, and the related assignment records are created.
// Controller
public class SchedulerController : Controller
{
ScheduleServices ScheduleSvc = new ScheduleServices(); // creates a new data context
public ActionResult Index()
{
return ShowSchedules();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(FormCollection form)
{
ScheduleSvc.ProcessRequest(form);
return Index();
}
}
public ActionResult ShowSchedules()
{
SchedulerViewModel sched_vm = new SchedulerViewModel();
sched_vm.EventsAndSchedules = ScheduleSvc.GetEventSchedulesFromDate();
return View(sched_vm);
}
ProceessScheduleRequest(ScheduleRequest req)
{
CreateSchedule(req);
AssignmentServices AssignmentSvc = new AssignmentServices(); // creates it's own data context
AssignmentSvc.Assign(req);
}
I found the answer in this post (myth #10).
Apparentlly, using the same DataContext for multiple units of work, results in stale data, because objects tracked by a DataContext instance are not refreshed simply by requerying.
Instead of using the same DataCcontext for both adding the records, and then displaying the results, I used two separate ones and that fixed it. So instead of having one ScheduleServices instance for the whole controller class, I create one for the ProcessRequest() and a separate one for ShowSchedules().
After saving to the database, you should redirect to the controller method that displays the data. This will cause the controller method to look up the data from the database again, which will now display the newly-updated record.

Resources