Παρουσίαση με Ετικέτες

Easy asynchronous web notifications
11 Οκτωβρίου 18 06:05 πμ | tolisss | 0 σχόλια   
Today we will discuss another common case, how to create a progress bar to notify our web users about the state of their long running tasks. For this discussion we will work with  DevExpress XAF Business Application Framework. We will develop ONE SMALL CLASS, we can then copy paste to any XAF project.

It is a good idea before starting any work to search the Support Center for ideas and ask the DevExpress support guys. Doing so we found many tickets with ready to work solutions. Some of them are good candidates e.g How to start a long running operation in ASP.NET application using the ThreadPool.QueueUserWorkItem method and show its progress in a browser using a WebService method.

The problem with the previous sample , is that uses a WebService to periodically call back in a Controller. I wanted to create a reusable implementation inside a library and a WebService cannot live in a library.
But I got the idea on how to proceed. I just need to create a ViewItem to host a ASPxProgressBar and with periodic call-backs I will inject JavaScript code to SetPosition on the client side ASPxClientProgressbar

Step 1
First we need a sample project so let’s use the XAF New Solution Wizard to create a new Web Project. We do not need any extra modules or security, just create it as simple as possible. After the solution is created add a very simple Domain Object so XAF can generate a web View for it.

    [DefaultClassOptions]
public class MyObject : BaseObject {
public MyObject(Session session) : base(session) { }
}
Step 2
In addition we  need create a sequence of Tasks that will return the state of our work. You can use any technology you prefer e.g. webservices, TPL tasksetc. as long as it returns asynchronously it fits our case. For this discussion I will use the System.Reactive library. To create the sequence the next line will be enough.

Observable
.Interval(TimeSpan.FromMilliseconds(1000))
.Subscribe(l => Console.WriteLine($"Task {l} completed on Thread:Environment.CurrentManagedThreadId}"));

If you want to test how it behaves add it in a console app and you should see the following output.

image

Step 3
In XAF we use Controllers to communicate with our Views so let’s create a very simple Controller and add our sequence and later connect the progress bar.

    public class MyController : ViewController<DetailView> {
public MyController() {
var action = new SimpleAction(this, "StartLongOperation", PredefinedCategory.Tools);
action.Execute += action_Execute;
}

void action_Execute(object sender, SimpleActionExecuteEventArgs e) {
Observable.Interval(TimeSpan.FromMilliseconds(1000)).Subscribe();
}
}

This controller declares an StartLongOperation Action and starts our sequence, exactly as described on XAF docs.
Currently XAF already generated the web UI and actions. Here how the DetailView of MyObject looks like.
image

Step 4
Now its time to create the progress bar container. For this scenario we do not need to alter the state of MyObject  so we will follow the XAF docs on how to create a ViewItem rather than a PropertyEditor. (How to implement a ViewItem). Our starting ViewItem comes next:

    public interface IModelProgressViewItem : IModelViewItem {
}

[ViewItem(typeof(IModelProgressViewItem))]
public class ProgresViewItem : ViewItem {
public ASPxProgressBar ProgressBar{ get; private set; }

public ProgresViewItem(IModelProgressViewItem info, Type classType)
: base(classType, info.Id){
}

protected override object CreateControlCore() {
ProgressBar = new ASPxProgressBar();
return ProgressBar;
}

}

We override the CreateControlCore method and just return an ASPxProgressBar component included in DevExpress suite.
Step 5
As I mentioned before this sample (How to start a long running operation in ASP.NET application using the ThreadPool.QueueUserWorkItem method and show its progress in a browser using a WebService method) use a Javascript  Timer to periodically call a WebService which communicates with a XAF Controller.

Our scenario is very similar but we need to remove the dependency to the WebService because we need to push the implementation to our ExcelImporter module that is part of the eXpandFramework and is very hard to host a WebService in a library so it can be reusable and with friction-less installation.

So let’s introduce the Javascript timer in our ProgressViewItem and use Callbacks to notify the server instead of the WebService used in the SC sample. This is as always well document in the XAF docs (How to: Raise XAF Callbacks from Client-Side Events and Process these Callbacks on Server).

        private XafCallbackManager CallbackManager => ((ICallbackManagerHolder)WebWindow.CurrentRequestPage).CallbackManager;
public int PollingInterval{ get; set; }
public void Start(int maximum){
var script = CallbackManager.GetScript(_handlerId, $"'{ProgressBar.ClientInstanceName}'","",false);
ProgressBar.ClientSideEvents.Init =
$@"function(s,e) {{
if(window.timer) window.clearInterval(window.timer);
var controlToUpdate = s;
window.timer = window.setInterval(function(){{
var previous = startProgress;startProgress = function () {{ }}; //this line disables the Loading Panel see Q427477 in SC
{script}startProgress = previous;}},
{PollingInterval});}}"
;
}
In short the Start method use the build-in XAF CallBackManager to generate a script with one parameter the ProgressBar.ClientInstance name. We pass this parameter because we may want to use multiple progress-bars in the same view. Next the timer calls this script every PollingInterval.

Whats left is to implement the IXafCallbackHandler as shown.

        public long Position{ get; set; }
public void ProcessAction(string parameter){
var script = $"{parameter}.SetPosition('{Position}')";
WebWindow.CurrentRequestWindow.RegisterStartupScript(_handlerId,script,true);
}
Here we just created a script that uses the client side ASPxProgressBar API to SetPosition based on the new ProgressViewItem Position property.
Step 6
To consume the ProgressViewItem we modify the MyController defined in Step 3 like:

        void action_Execute(object sender, SimpleActionExecuteEventArgs e) {
var progresViewItem = View.GetItems<ProgresViewItem>().First();
progresViewItem.Start(maximum:100);//Start the timer
Observable
.Interval(TimeSpan.FromMilliseconds(1000))
.Subscribe(l => progresViewItem.Position=l );//Update the position for each Task
}
Step 7
Finally let's add our ProgressViewItem to the a View. We will use the Model editor to create it and drag & drop to the Layout.

Below you can see how the ProgressViewItem works in runtime in our sample solution.

I wrote this post as a proof of concept rather than a complete implementation, so I am not posting any samples. However you can download the the complete ProgressViewItem used if  you wish from this gist.
Below you can verify that it works fine in a real world complex module like the ExcelImporter.


XAF can do Mobile and Windows as well, a Mobile implementation for this scenario does not make much sense but have a look how it looks in the Windows platform.
Δημοσίευση στην κατηγορία: ,
The ExcelImporter module
19 Μαρτίου 18 02:49 πμ | tolisss | 0 σχόλια   

I am happy to announce the new ExcelIMporter module for both Windows and Web. Released with eXpandFramework v17.2.6.3.

Importing is a complex task, each scenario has difference requirements. So, first off, a short brief on the existing importing solutions found in eXpandFramework.

The ImportWizard module

This module was contributed to eXpandFramework a few years ago from a community member.
image

Pros

  1. Beautiful UI as it is build with the same DevExpress components that XAF uses.
  2. Well integrated, with localization support and skinning.
  3. Imports in batches on a background thread.
  4. Visualization of the Excel Sheet for configuring the import.

Cons

  1. It is not a native XAF implementation but a Windows Forms integration into XAF, which makes it really hard to support and harder to EasyTest.
  2. Import configuration cannot be reused.
  3. Does not support the Web.
  4. Poor notification of importing errors.
  5. Can only import *.xlsx files.

The IO module

This module was designed to help move objects graphs in the form of XML out of XAF and again in, using the same domain schema. Below you see the objects generation Graph.

image

image

Pros

  1. Its a native XAF solution.
  2. The export configuration can be reused as it is stored in the database.
  3. Supports both Win and Web.
  4. The module is EasyTested from these tests.

Cons

  1. It only works for the same BO domain.
  2. It only export in a predefined XML schema.
  3. Poor notification of import/export errors.

In addition in the IO module you can find:

The InitDataImporter

This class uses XPO to import from any XPO supported datastore, into different XPO domain model. This is accomplished with the InitialDataAttribute. You can see how to apply it in an XPO object in the XVideoRental demo which ships with DevExpress sources. For example look how it is applied to the Customer class and how it is instantiated for the whole domain. The XVideoRental demo can also be found in eXpandFramework sources so feel free to explore it.

The StorageMapper

This class requires a source and a target objectspace and it will copy a collection of objects and their graphs from one objectspace to the other.

The ExcelImporter module

The new ExcelImporter module integrates the ExcelDataReader project. It follows the same architecture with the rest of the eXpandFramework modules. There are three assemblies you have to use Xpand.ExpressApp.ExcelImporter.dll, Xpand.ExpressApp.ExcelImporter.Win.dll and Xpand.ExpressApp.ExcelImporter.Web.dll or you can use the related nugget packages.

Next are screenshots of the main view.

image

image

Pros

  1. It is a native XAF solution.
  2. It supports both Win and Web.
  3. The configuration is stored in the database and can be reused.
  4. It can import all the formats supported from the ExcelDataReader project.
  5. It is EasyTested from these tests.
  6. It notifies if errors before commit.

Cons

  1. This release does not commit in batches.

I hope this new module will be a great help for many of you. For feedback, questions etc. as always please use the online forums.  

Δημοσίευση στην κατηγορία:

Search

Go

Το Ιστολόγιο

Ιστορικό Δημοσιεύσεων

Συνδρομές