<?xml version="1.0" encoding="UTF-8" standalone="no"?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0"><channel><title>A.Bekiaris's .Net / XAF Blog</title><description></description><managingEditor>noreply@blogger.com (Apostolis Bekiaris)</managingEditor><pubDate>Tue, 18 Feb 2025 21:36:10 +0200</pubDate><generator>Blogger http://www.blogger.com</generator><openSearch:totalResults xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">207</openSearch:totalResults><openSearch:startIndex xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">1</openSearch:startIndex><openSearch:itemsPerPage xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">3</openSearch:itemsPerPage><link>http://apobekiaris.blogspot.com/</link><language>en-us</language><itunes:explicit>no</itunes:explicit><itunes:subtitle/><itunes:owner><itunes:email>noreply@blogger.com</itunes:email></itunes:owner><item><title>Compile your solution without DevExpress assemblies installed in 4 lines</title><link>http://apobekiaris.blogspot.com/2019/01/compile-your-solution-without.html</link><author>noreply@blogger.com (Apostolis Bekiaris)</author><pubDate>Mon, 28 Jan 2019 06:57:00 +0200</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-3509234234245012858.post-3035426201417006329</guid><description>&lt;p&gt;Do you have a project, where you use &lt;em&gt;Azure&lt;/em&gt; pipelines to build it? How do you get the referenced &lt;em&gt;DevExpress&lt;/em&gt; assemblies? In this post I will discuss how we go for it in eXpandFramework. The process however is applicable in all other projects.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;p&gt;After executing the next 4 lines your solution should compile:&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;div style="background: rgb(255, 255, 255); border-width: 0.1em 0.1em 0.1em 0.8em; border-style: solid; border-color: gray; padding: 0.2em 0.6em; border-image: none; width: auto; overflow: auto;"&gt;&lt;pre style="margin: 0px; line-height: 125%;"&gt;Install-Module XpandPosh
&lt;span style="color: rgb(0, 112, 32);"&gt;Register-PackageSource&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0); background-color: rgb(255, 170, 170);"&gt;–&lt;/span&gt;ProviderName Nuget &lt;span style="color: rgb(255, 0, 0); background-color: rgb(255, 170, 170);"&gt;–&lt;/span&gt;Name DX_private &lt;span style="color: rgb(255, 0, 0); background-color: rgb(255, 170, 170);"&gt;–&lt;/span&gt;Location https&lt;span style="color: rgb(255, 0, 0); background-color: rgb(255, 170, 170);"&gt;:&lt;/span&gt;//nuget.devexpress.com/YOURTOKEN
Install-DX -binPath YOURBIN -dxSources $(&lt;span style="color: rgb(0, 112, 32);"&gt;Get-PackageSourceLocations&lt;/span&gt; -join &lt;span style="background-color: rgb(255, 240, 240);"&gt;";"&lt;/span&gt;) -sourcePath YOURSOURCESPATH -dxVersion 18.2.5 
&lt;span style="color: rgb(0, 112, 32);"&gt;Update-HintPath&lt;/span&gt; &lt;span style="color: rgb(255, 0, 0); background-color: rgb(255, 170, 170);"&gt;–&lt;/span&gt;OutputPath YOURBIN &lt;span style="color: rgb(255, 0, 0); background-color: rgb(255, 170, 170);"&gt;–&lt;/span&gt;SourcesPath YOURSOURCESPATH
&lt;/pre&gt;&lt;/div&gt;

&lt;pre&gt;&lt;br&gt;&lt;/pre&gt;&lt;p&gt;For feedback drop an issue at &lt;a title="https://github.com/eXpandFramework/DevExpress.PackageContent" href="https://github.com/eXpandFramework/DevExpress.PackageContent"&gt;https://github.com/eXpandFramework/DevExpress.PackageContent&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Easy asynchronous web notifications</title><link>http://apobekiaris.blogspot.com/2018/10/easy-asynchronous-web-notifications.html</link><category>Core.Web</category><category>ExcelImporter</category><author>noreply@blogger.com (Apostolis Bekiaris)</author><pubDate>Thu, 11 Oct 2018 12:25:00 +0300</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-3509234234245012858.post-3956039727502657945</guid><description>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&amp;nbsp; DevExpress XAF Business Application Framework. We will develop &lt;strong&gt;ONE SMALL CLASS, &lt;/strong&gt;we can then copy paste&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;to any XAF project&lt;strong&gt;.&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
It is a good idea before starting any work to search the &lt;a href="https://search.devexpress.com/?q=web%20progress%20bar&amp;amp;m=SupportCenter" target="_blank"&gt;Support Center&lt;/a&gt; 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 &lt;a href="https://www.devexpress.com/Support/Center/Question/Details/Q373722/how-to-start-a-long-running-operation-in-asp-net-application-using-the-threadpool" target="_blank"&gt;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&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
The problem with the previous sample , is that uses a &lt;i&gt;WebService &lt;/i&gt;to periodically call back in a &lt;i&gt;Controller. &lt;/i&gt;I wanted to create a reusable implementation inside a library and a WebService cannot live in a library&lt;i&gt;.&lt;/i&gt;&lt;br /&gt;
But I &lt;b&gt;got the idea &lt;/b&gt;on how to proceed&lt;b&gt;.&lt;/b&gt; I just need to create a &lt;i&gt;&lt;b&gt;ViewItem &lt;/b&gt;&lt;/i&gt;to host a &lt;i&gt;&lt;b&gt;ASPxProgressBar &lt;/b&gt;&lt;/i&gt;and with &lt;b&gt;periodic&lt;/b&gt; call-backs I will inject JavaScript code to &lt;b&gt;SetPosition &lt;/b&gt;on the &lt;b&gt;client &lt;/b&gt;side &lt;a href="https://documentation.devexpress.com/AspNet/DevExpress.Web.Scripts.ASPxClientProgressBar.class" target="_blank"&gt;ASPxClientProgressbar&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;&lt;strong&gt;Step 1&lt;/strong&gt;&lt;/u&gt;&lt;br /&gt;
First we need a sample project so let’s use the &lt;a href="https://documentation.devexpress.com/eXpressAppFramework/113624/Design-Time-Features/Solution-Wizard" target="_blank"&gt;XAF New Solution Wizard&lt;/a&gt; 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.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;    [DefaultClassOptions]
    &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;class&lt;/span&gt; MyObject : BaseObject {
        &lt;span class="key"&gt;public&lt;/span&gt; MyObject(Session session) : &lt;span class="key"&gt;base&lt;/span&gt;(session) { }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;u&gt;&lt;strong&gt;
Step 2&lt;/strong&gt;&lt;/u&gt;&lt;br /&gt;
In addition we&amp;nbsp; need create a sequence of &lt;i&gt;Tasks &lt;/i&gt;that will return the state of our work. You can use any technology you prefer e.g. &lt;i&gt;webservices&lt;/i&gt;, &lt;i&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/task-parallel-library-tpl" target="_blank"&gt;TPL tasks&lt;/a&gt;
&lt;/i&gt;etc. as long as it returns asynchronously it fits our case. For this discussion I will use the &lt;a href="https://msdn.microsoft.com/en-us/library/hh242985(v=vs.103).aspx" target="_blank"&gt;System.Reactive&lt;/a&gt; library. To create the sequence the next line will be enough.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;Observable
    .Interval(TimeSpan.FromMilliseconds(1000))
    .Subscribe(l =&amp;gt;  Console.WriteLine($&lt;span class="str"&gt;"Task {l} completed on Thread:Environment.CurrentManagedThreadId}"&lt;/span&gt;));&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
If you want to test how it behaves add it in a console app and you should see the following output.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNnuVWToPv5iNVMyqL2S-zrnGrKt8t_7KivBqJS7WPOARs0juGrVqgTYewf8Iz5O0LWqbGAHP3BJOePerMmWPUN3mRaTF5AjGNGhH3magY177qLKVuCcQXshOLqOpsf9tz-PdByi_VhHHw/s1600-h/image4"&gt;&lt;img alt="image" border="0" height="206" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT-KQB1v1GLPc9qN2yb8GybTI9CrY3DnOU9B-okF01nPC0TADuy-euZEfGX87_93_43vfsbgC2zNj_mlp2uwSgVz5byQcEGw1TaaAuD0_w2qoG-pgqRLMW2vglFJkG1hJ04L6sE2eCcQ5u/?imgmax=800" style="background-image: none; border: 0px currentcolor; display: inline;" title="image" width="382" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;u&gt;&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;&lt;/u&gt;
&lt;u&gt;&lt;strong&gt;Step 3&lt;/strong&gt;&lt;/u&gt;&lt;br /&gt;
In XAF we use &lt;a href="https://documentation.devexpress.com/eXpressAppFramework/112621/Concepts/Extend-Functionality/Controllers" target="_blank"&gt;Controllers&lt;/a&gt; to communicate with our &lt;a href="https://documentation.devexpress.com/eXpressAppFramework/112611/Concepts/UI-Construction/Views" target="_blank"&gt;Views&lt;/a&gt; so let’s create a very simple &lt;i&gt;Controller &lt;/i&gt;and add our sequence and later connect the progress bar.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;    &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;class&lt;/span&gt; MyController : ViewController&amp;lt;DetailView&amp;gt; {
        &lt;span class="key"&gt;public&lt;/span&gt; MyController() {
            &lt;span class="key"&gt;var&lt;/span&gt; action = &lt;span class="key"&gt;new&lt;/span&gt; SimpleAction(&lt;span class="key"&gt;this&lt;/span&gt;, &lt;span class="str"&gt;"StartLongOperation"&lt;/span&gt;, PredefinedCategory.Tools);
            action.Execute += action_Execute;
        }

        &lt;span class="key"&gt;void&lt;/span&gt; action_Execute(&lt;span class="key"&gt;object&lt;/span&gt; sender, SimpleActionExecuteEventArgs e) {
            Observable.Interval(TimeSpan.FromMilliseconds(1000)).Subscribe();
        }
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
This controller declares an &lt;em&gt;StartLongOperation&lt;/em&gt; &lt;a href="https://documentation.devexpress.com/eXpressAppFramework/112622/Concepts/Extend-Functionality/Actions" target="_blank"&gt;Action&lt;/a&gt; and starts our sequence, exactly as described on &lt;a href="https://documentation.devexpress.com/eXpressAppFramework/112622/Concepts/Extend-Functionality/Actions" target="_blank"&gt;XAF docs&lt;/a&gt;.&lt;br /&gt;
Currently XAF already generated the web UI and actions. Here how the &lt;em&gt;DetailView&lt;/em&gt; of &lt;em&gt;MyObject&lt;/em&gt; looks like.&lt;br /&gt;
&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzoiDYxXPVGkXglYq7y8CCuvamyG7r-R0qBEyWEMAgxZsh7ugRut0Hwsy9T0cPOM_qK9ocoH4yxOwr-QnAJE0qvi1sREKjNLz2mH_h5IK7bUnvvZOabdufyI37OMyq4dvKzBsdLJF6Bjf7/s1600-h/image9"&gt;&lt;img alt="image" border="0" height="404" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhock-mW9XOVrJZZ-V0R2yM0TKtMglnZPS2q5qGFI-mdSS9J5ZTFNbPbVmkVadJ5hmMeCdDuCRH_JSzlJML6Wcdd007TvkSBU41tEGeKPATzcJ2LTXtBD_V-t33wMLL81OlfDFYep6Lo0Kc/?imgmax=800" style="background-image: none; border: 0px currentcolor; display: inline;" title="image" width="1337" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;strong&gt;&lt;em&gt;&lt;u&gt;&lt;br /&gt;&lt;/u&gt;&lt;/em&gt;&lt;/strong&gt;
&lt;strong&gt;&lt;em&gt;&lt;u&gt;Step 4&lt;/u&gt;&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;
Now its time to create the progress bar container. For this scenario we do not need to alter the state of &lt;em&gt;MyObject&lt;/em&gt;&amp;nbsp; so we will follow the XAF docs on how to create a &lt;em&gt;ViewItem&lt;/em&gt; rather than a &lt;em&gt;PropertyEditor&lt;/em&gt;. (&lt;a href="https://documentation.devexpress.com/eXpressAppFramework/112641/Task-Based-Help/Views/How-to-Implement-a-View-Item" target="_blank"&gt;How to implement a ViewItem&lt;/a&gt;). Our starting &lt;em&gt;ViewItem&lt;/em&gt; comes next:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;    &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;interface&lt;/span&gt; IModelProgressViewItem : IModelViewItem {
    }

    [ViewItem(&lt;span class="key"&gt;typeof&lt;/span&gt;(IModelProgressViewItem))]
    &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;class&lt;/span&gt; ProgresViewItem : ViewItem {
        &lt;span class="key"&gt;public&lt;/span&gt; ASPxProgressBar ProgressBar{ &lt;span class="key"&gt;get&lt;/span&gt;; &lt;span class="key"&gt;private&lt;/span&gt; &lt;span class="key"&gt;set&lt;/span&gt;; }

        &lt;span class="key"&gt;public&lt;/span&gt; ProgresViewItem(IModelProgressViewItem info, Type classType)
            : &lt;span class="key"&gt;base&lt;/span&gt;(classType, info.Id){
        }

        &lt;span class="key"&gt;protected&lt;/span&gt; &lt;span class="key"&gt;override&lt;/span&gt; &lt;span class="key"&gt;object&lt;/span&gt; CreateControlCore() {
            ProgressBar = &lt;span class="key"&gt;new&lt;/span&gt; ASPxProgressBar();
            &lt;span class="key"&gt;return&lt;/span&gt; ProgressBar;
        }

    }&lt;/code&gt;&lt;/pre&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
We override the &lt;em&gt;CreateControlCore &lt;/em&gt;method and just return an &lt;a href="https://documentation.devexpress.com/AspNet/11024/ASP-NET-WebForms-Controls/Data-Editors/ASPxProgressBar/Overview/ASPxProgressBar-Overview" target="_blank"&gt;ASPxProgressBar &lt;/a&gt;component included in DevExpress suite.&lt;br /&gt;
&lt;strong&gt;&lt;em&gt;&lt;u&gt;Step 5&lt;/u&gt;&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;
As I mentioned before this sample (&lt;a href="https://www.devexpress.com/Support/Center/Question/Details/Q373722/how-to-start-a-long-running-operation-in-asp-net-application-using-the-threadpool" target="_blank"&gt;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&lt;/a&gt;) use a &lt;em&gt;Javascript&amp;nbsp; T&lt;/em&gt;imer to periodically call a &lt;em&gt;WebService &lt;/em&gt;which communicates with a XAF &lt;em&gt;Controller. &lt;/em&gt;&lt;br /&gt;
&lt;br /&gt;
Our scenario is very similar but we need to remove the dependency to the &lt;em&gt;WebService&lt;/em&gt; because we need to push the implementation to our &lt;a href="https://github.com/eXpandFramework/eXpand#excelimporter" target="_blank"&gt;ExcelImporter&lt;/a&gt;&lt;em&gt;&lt;/em&gt; module that is part of the &lt;a href="https://github.com/eXpandFramework/" target="_blank"&gt;eXpandFramework&lt;/a&gt; and is very hard to host a &lt;em&gt;WebService&lt;/em&gt; in a library so it can be reusable and with friction-less installation.&lt;br /&gt;
&lt;br /&gt;
So let’s introduce the &lt;em&gt;Javascript&lt;/em&gt; timer in our &lt;em&gt;ProgressViewItem &lt;/em&gt;and use &lt;em&gt;Callbacks&lt;/em&gt; to notify the server instead of the &lt;em&gt;WebService&lt;/em&gt; used in the SC sample. This is as always well document in the XAF docs (&lt;a href="https://docs.devexpress.com/eXpressAppFramework/119706/task-based-help/miscellaneous-ui-customizations/how-to-raise-xaf-callbacks-from-client-side-events-and-process-these-callbacks-on-server" target="_blank"&gt;How to: Raise XAF Callbacks from Client-Side Events and Process these Callbacks on Server&lt;/a&gt;).&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;        &lt;span class="key"&gt;private&lt;/span&gt; XafCallbackManager CallbackManager =&amp;gt; ((ICallbackManagerHolder)WebWindow.CurrentRequestPage).CallbackManager;
        &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;int&lt;/span&gt; PollingInterval{ &lt;span class="key"&gt;get&lt;/span&gt;; &lt;span class="key"&gt;set&lt;/span&gt;; }
        &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;void&lt;/span&gt; Start(&lt;span class="key"&gt;int&lt;/span&gt; maximum){
            &lt;span class="key"&gt;var&lt;/span&gt; script = CallbackManager.GetScript(_handlerId, $&lt;span class="str"&gt;"'{ProgressBar.ClientInstanceName}'"&lt;/span&gt;,&lt;span class="str"&gt;""&lt;/span&gt;,&lt;span class="key"&gt;false&lt;/span&gt;);
            ProgressBar.ClientSideEvents.Init =
                $@&lt;span class="str"&gt;"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});}}"&lt;/span&gt;;
        }
&lt;/code&gt;&lt;/pre&gt;
In short the &lt;em&gt;Start &lt;/em&gt;method use the build-in XAF &lt;em&gt;CallBackManager &lt;/em&gt;to generate a script with one parameter the &lt;em&gt;ProgressBar.ClientInstance &lt;/em&gt;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 &lt;em&gt;PollingInterval.&lt;/em&gt;&lt;br /&gt;
&lt;em&gt;&lt;br /&gt;&lt;/em&gt;
Whats left is to implement the &lt;a href="https://docs.devexpress.com/eXpressAppFramework/119706/task-based-help/miscellaneous-ui-customizations/how-to-raise-xaf-callbacks-from-client-side-events-and-process-these-callbacks-on-server" target="_blank"&gt;IXafCallbackHandler&lt;/a&gt;&lt;em&gt; &lt;/em&gt;as shown.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;        &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;long&lt;/span&gt; Position{ &lt;span class="key"&gt;get&lt;/span&gt;; &lt;span class="key"&gt;set&lt;/span&gt;; }
        &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;void&lt;/span&gt; ProcessAction(&lt;span class="key"&gt;string&lt;/span&gt; parameter){
            &lt;span class="key"&gt;var&lt;/span&gt; script = $&lt;span class="str"&gt;"{parameter}.SetPosition('{Position}')"&lt;/span&gt;;
            WebWindow.CurrentRequestWindow.RegisterStartupScript(_handlerId,script,&lt;span class="key"&gt;true&lt;/span&gt;);
        }
&lt;/code&gt;&lt;/pre&gt;
Here we just created a script that uses the client side &lt;a href="https://documentation.devexpress.com/AspNet/11024/ASP-NET-WebForms-Controls/Data-Editors/ASPxProgressBar/Overview/ASPxProgressBar-Overview" target="_blank"&gt;ASPxProgressBar&lt;/a&gt; API to &lt;em&gt;SetPosition &lt;/em&gt;based on the new &lt;em&gt;ProgressViewItem&lt;/em&gt; &lt;em&gt;Position &lt;/em&gt;property.&lt;br /&gt;
&lt;strong&gt;&lt;u&gt;Step 6&lt;/u&gt;&lt;/strong&gt;&lt;br /&gt;
To consume the &lt;em&gt;ProgressViewItem &lt;/em&gt;we modify the &lt;em&gt;MyController &lt;/em&gt;defined in Step 3 like:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;        &lt;span class="key"&gt;void&lt;/span&gt; action_Execute(&lt;span class="key"&gt;object&lt;/span&gt; sender, SimpleActionExecuteEventArgs e) {
            &lt;span class="key"&gt;var&lt;/span&gt; progresViewItem = View.GetItems&amp;lt;ProgresViewItem&amp;gt;().First();
            progresViewItem.Start(maximum:100);&lt;span class="com"&gt;//Start the timer&lt;/span&gt;
            Observable
                .Interval(TimeSpan.FromMilliseconds(1000))
                .Subscribe(l =&amp;gt; progresViewItem.Position=l );&lt;span class="com"&gt;//Update the position for each Task&lt;/span&gt;
        }
&lt;/code&gt;&lt;/pre&gt;
&lt;b&gt;&lt;u&gt;Step 7&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;
Finally let's add our &lt;i&gt;ProgressViewItem &lt;/i&gt;to the a View. We will use the &lt;a href="https://docs.devexpress.com/eXpressAppFramework/112582/concepts/application-model/model-editor" target="_blank"&gt;Model editor &lt;/a&gt;to create it and drag &amp;amp; drop to the Layout.&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOpzsNvLjWcU70Bfq79bW9SZIA8c0PbF0YkZxrzx_mzwX_Y9G5_1uHxi-5kx7ow2fTw2wqT9coN3e4xVd4mlYtJpW6VeszLJkyvYCcnIfvS4FX8RDHMgWSbSeKshosPapa2MFAcnphOVY/s1600/CreateViewItem.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="674" data-original-width="1212" height="177" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOpzsNvLjWcU70Bfq79bW9SZIA8c0PbF0YkZxrzx_mzwX_Y9G5_1uHxi-5kx7ow2fTw2wqT9coN3e4xVd4mlYtJpW6VeszLJkyvYCcnIfvS4FX8RDHMgWSbSeKshosPapa2MFAcnphOVY/s320/CreateViewItem.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRclS9qdeDsGj3IsLPDbooPoJnio-_iw9GFN_O7ZBdohVKKxboia6w38KL8W13pV_mlxxpvoEOQtKjyVyKRYoK-VdGQTgPGp3lyo2nD3tO2-zxcKtbVoWDhZfuIHXXTBjYdFsc4i0HyVE/s1600/CreateViewItem.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="674" data-original-width="1212" height="177" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRclS9qdeDsGj3IsLPDbooPoJnio-_iw9GFN_O7ZBdohVKKxboia6w38KL8W13pV_mlxxpvoEOQtKjyVyKRYoK-VdGQTgPGp3lyo2nD3tO2-zxcKtbVoWDhZfuIHXXTBjYdFsc4i0HyVE/s320/CreateViewItem.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Below you can see how the ProgressViewItem works in runtime in our sample solution.&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' src='https://www.blogger.com/video.g?token=AD6v5dxWsaY_CGrlJkdo1XFQf9rGtu6CLSJQcgdcbUc9suZJD52wCjKMAJU1zFBYw2JRtX6qYJZjWoCBeURrE4OnQQ' class='b-hbp-video b-uploaded' frameborder='0'&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;br /&gt;
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&amp;nbsp; you wish from this &lt;a href="https://gist.github.com/apobekiaris/c43264a86d216cdeaf496634012b3570" target="_blank"&gt;gist&lt;/a&gt;.&lt;br /&gt;
Below you can verify that it works fine in a real world complex module like the &lt;a href="https://github.com/eXpandFramework/eXpand#excelimporter" target="_blank"&gt;ExcelImporter&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;
&lt;iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' src='https://www.blogger.com/video.g?token=AD6v5dzV6OZJPdK38e4eQem59BocfNn7j5ea6Tj4Cp136HyLlmB6cCV41ZmOQxaJwNRFL4J_00spcojuXCbEgYndJg' class='b-hbp-video b-uploaded' frameborder='0'&gt;&lt;/iframe&gt;&lt;/div&gt;
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.&lt;br /&gt;
&lt;br /&gt;
&lt;iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dxa9XWYFOl9XH8g0cCIxLB6qwQf7cvUPz-UodDEZqLVLgRTBc_H3ZT4OO_YSVxHkH-19-4x4qMGm-YiVvqZiQ' class='b-hbp-video b-uploaded' frameborder='0'&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;div style="text-align: center;"&gt;
&lt;br /&gt;
&lt;a href="https://gist.github.com/apobekiaris/c43264a86d216cdeaf496634012b3570" target="_blank"&gt;gist&lt;/a&gt;&lt;/div&gt;
</description><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT-KQB1v1GLPc9qN2yb8GybTI9CrY3DnOU9B-okF01nPC0TADuy-euZEfGX87_93_43vfsbgC2zNj_mlp2uwSgVz5byQcEGw1TaaAuD0_w2qoG-pgqRLMW2vglFJkG1hJ04L6sE2eCcQ5u/s72-c?imgmax=800" width="72"/></item><item><title>Scheduled long running tasks with XAF</title><link>http://apobekiaris.blogspot.com/2018/08/scheduled-long-running-tasks-with-xaf.html</link><category>Core.Web</category><category>Core.Win</category><author>noreply@blogger.com (Apostolis Bekiaris)</author><pubDate>Wed, 15 Aug 2018 16:31:00 +0300</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-3509234234245012858.post-3390693729814786009</guid><description>Today I will detail about implementing Scheduled Long running tasks in an abstract and reusable way. &lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;The Requirement&lt;/u&gt;&lt;br /&gt;
&lt;u&gt;&lt;br /&gt;&lt;/u&gt;
Usually, in my solution I have business objects that store time depended data e.g. Exceptions, Logs etc. There is a need to periodically clean up those tables. &lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;The parameters&lt;/u&gt;&lt;br /&gt;
&lt;u&gt;&lt;br /&gt;&lt;/u&gt;
First we need to identify the problem parameters so &lt;a href="https://documentation.devexpress.com/eXpressAppFramework/113169/Concepts/Application-Model/Extend-and-Customize-the-Application-Model-in-Code" target="_blank"&gt;we can create a model interface&lt;/a&gt;. Parameterizing the problem in a model interface is very useful, because the end user can switch it off if something goes wrong.&lt;br /&gt;
Parameters will be: the business &lt;strong&gt;object type &lt;/strong&gt;which we want to clear its records, the &lt;strong&gt;criterion &lt;/strong&gt;to filter the objects and the &lt;strong&gt;time&lt;/strong&gt; to execute this action. The interface along with its registration for this is bellow.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;&lt;span class="key"&gt;using&lt;/span&gt; System;
&lt;span class="key"&gt;using&lt;/span&gt; System.ComponentModel;
&lt;span class="key"&gt;using&lt;/span&gt; DevExpress.ExpressApp;
&lt;span class="key"&gt;using&lt;/span&gt; DevExpress.ExpressApp.Model;
&lt;span class="key"&gt;using&lt;/span&gt; DevExpress.ExpressApp.Model.Core;

&lt;span class="key"&gt;namespace&lt;/span&gt; PurgingRule.Module.Controllers{
    &lt;span class="com"&gt;//rules container&lt;/span&gt;
    &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;interface&lt;/span&gt; IModelClassPurgingRules:IModelClass{
        IModelPurgingRules PurgingRules{ &lt;span class="key"&gt;get&lt;/span&gt;; }
    }

    [ModelNodesGenerator(&lt;span class="key"&gt;typeof&lt;/span&gt; (PurgingRulesModelNodesGenerator))] 
    &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;interface&lt;/span&gt; IModelPurgingRules : IModelNode, IModelList&amp;lt;IModelPurgingRule&amp;gt;{
    }

    &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;interface&lt;/span&gt; IModelPurgingRule:IModelNode{
        &lt;span class="key"&gt;string&lt;/span&gt; Criteria{ &lt;span class="key"&gt;get&lt;/span&gt;; &lt;span class="key"&gt;set&lt;/span&gt;; }
        &lt;span class="key"&gt;bool&lt;/span&gt; ExecuteOnApplicationStart{ &lt;span class="key"&gt;get&lt;/span&gt;; &lt;span class="key"&gt;set&lt;/span&gt;; }
        TimeSpan TimeSpan{ &lt;span class="key"&gt;get&lt;/span&gt;; &lt;span class="key"&gt;set&lt;/span&gt;; }
        [DefaultValue(500)]
        &lt;span class="key"&gt;int&lt;/span&gt; ChunkSize{ &lt;span class="key"&gt;get&lt;/span&gt;; &lt;span class="key"&gt;set&lt;/span&gt;; }
    }

    &lt;span class="com"&gt;//will help us generated more rules if needed&lt;/span&gt;
    &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;class&lt;/span&gt; PurgingRulesModelNodesGenerator:ModelNodesGeneratorBase{
        &lt;span class="key"&gt;protected&lt;/span&gt; &lt;span class="key"&gt;override&lt;/span&gt; &lt;span class="key"&gt;void&lt;/span&gt; GenerateNodesCore(ModelNode node){
            
        }
    }

    &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;class&lt;/span&gt; PurgingController:Controller,IModelExtender{

        &lt;span class="com"&gt;//model interface registration&lt;/span&gt;
        &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;void&lt;/span&gt; ExtendModelInterfaces(ModelInterfaceExtenders extenders){
            extenders.Add&amp;lt;IModelClass,IModelClassPurgingRules&amp;gt;();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
Now, it is possible to use the Model Editor to configure in which object we want to apply our rules.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2bGtFjuBa7C36qYpCU7-QL7cD2_DLj1dmJyhaLfc0mQdE2zYf3Qiryntp2cPnyZZsJxqFGiS6m1mD1HRRynIW2kASu3KYixemvdM_hV15yMo5B5neFarB5mdSahbpKcKDn0ze32B1qFVh/s1600-h/image%255B8%255D"&gt;&lt;img alt="image" border="0" height="280" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPHtOaWeaO06N98i_T5PidS9XdC_FqnWQMrVgMfx6ckDnh07P8x7R0WHiwbvQZRmVpVmZZbNdxYQy4xBTonOnBre9zkBSDEuVxBJso_fKCGw-oZ_5FdMJ8Y9E55-p_JX_AGCKnDGS9k2gr/?imgmax=800" style="background-image: none; border: 0px currentcolor; display: inline;" title="image" width="640" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
That's nice! &lt;br /&gt;
&lt;br /&gt;
But I like to use an Editor to write the &lt;em&gt;Criteria&lt;/em&gt; and need to add a few more bits for this to happen. For this we need to use the &lt;a href="https://docs.devexpress.com/eXpressAppFramework/113567/concepts/business-model-design/data-types-supported-by-built-in-editors/criteria-properties/criteria-properties-in-domain-components" target="_blank"&gt;CriteriaOptions&lt;/a&gt; and Editor attributes as shown:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;    &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;interface&lt;/span&gt; IModelPurgingRule:IModelNode{
        [Editor(&lt;span class="str"&gt;"DevExpress.ExpressApp.Win.Core.ModelEditor.CriteriaModelEditorControl, DevExpress.ExpressApp.Win"&lt;/span&gt; + XafAssemblyInfo.VersionSuffix + XafAssemblyInfo.AssemblyNamePostfix, &lt;span class="key"&gt;typeof&lt;/span&gt;(UITypeEditor))]
        [CriteriaOptions(&lt;span class="str"&gt;"TypeInfo"&lt;/span&gt;)]
        &lt;span class="key"&gt;string&lt;/span&gt; Criteria{ &lt;span class="key"&gt;get&lt;/span&gt;; &lt;span class="key"&gt;set&lt;/span&gt;; }
        [Browsable(&lt;span class="key"&gt;false&lt;/span&gt;)]
        [ModelValueCalculator(&lt;span class="str"&gt;"((IModelClass) Parent.Parent).TypeInfo"&lt;/span&gt;)]
        ITypeInfo TypeInfo { &lt;span class="key"&gt;get&lt;/span&gt;; }&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
Now it should be straight forward to construct any criterion with the help of this build-in editor.&lt;br /&gt;
&lt;br /&gt;
&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyALC_gGykUs-bVDInji9Bd86wBKy9ojkQ1oTE-ceZWSuTZHUaoyu_-NF4iFlBYyBRLddv609qOwltmooJaFPcxpZRlGFGVinWdEacUTyyF-OVAGuPoSaN-pFDLWn0DWyZsJUI4rSUnetR/s1600-h/image%255B13%255D"&gt;&lt;img alt="image" border="0" height="336" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_bJqC1ri0Ju6AqlbUFcDPwnniqnJ3fxQ6iWv4eTBB6H0AeNLmGgLv6uibcpwDhEgSk8sicquKdJGSY6z9AitDQHnSIq0NQRhqdeYbrbw0_72w7UE48hxKXHAPYYRAeWV5QXPrmrODn2UJ/?imgmax=800" style="background-image: none; border: 0px currentcolor; display: inline;" title="image" width="640" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;u&gt;&lt;br /&gt;&lt;/u&gt;
&lt;u&gt;Making Thread Safe Database calls&lt;/u&gt;&lt;br /&gt;
&lt;u&gt;&lt;br /&gt;&lt;/u&gt;
Working in a multi threaded environment (long running tasks) is build-in in XAF's XPO ORM so we go for it. Just make sure you enable the switch as bellow.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;        &lt;span class="key"&gt;protected&lt;/span&gt; &lt;span class="key"&gt;override&lt;/span&gt; &lt;span class="key"&gt;void&lt;/span&gt; CreateDefaultObjectSpaceProvider(CreateCustomObjectSpaceProviderEventArgs args) {
            &lt;span class="key"&gt;var&lt;/span&gt; threadSafe = true &lt;span class="com"&gt;//enable threadsafe;&lt;/span&gt;
            args.ObjectSpaceProviders.Add(&lt;span class="key"&gt;new&lt;/span&gt; XPObjectSpaceProvider(XPObjectSpaceProvider.GetDataStoreProvider(args.ConnectionString, args.Connection, &lt;span class="key"&gt;true&lt;/span&gt;), threadSafe));
            args.ObjectSpaceProviders.Add(&lt;span class="key"&gt;new&lt;/span&gt; NonPersistentObjectSpaceProvider(TypesInfo, &lt;span class="key"&gt;null&lt;/span&gt;));
        }
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;u&gt;Scheduling the long running tasks&lt;/u&gt;&lt;br /&gt;
&lt;u&gt;&lt;br /&gt;&lt;/u&gt;
We will use the &lt;a href="https://github.com/expand/eXpand/blob/c40e4f571fbfd29af4321c60d7fa7cfc599a5474/Xpand/Xpand.Utils/Threading/TaskExtensions.cs#L38-L60" target="_blank"&gt;StartNewPeriodic&lt;/a&gt; extension method and will follow the next steps in our code.&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Execute once at application startup .&lt;/li&gt;
&lt;li&gt;Collect the model rules to execute.&lt;/li&gt;
&lt;li&gt;Calculate the next execution time. For this we need to store the last execution time so I used the &lt;em&gt;RuleScheduleStorage&lt;/em&gt; BO class found at the end of the next snippet&lt;/li&gt;
&lt;li&gt;Periodically schedule parallel calls to the PurgeObjects method for each rule.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol&gt;&lt;/ol&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;    &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;class&lt;/span&gt; PurgingController:Controller,IModelExtender{
        &lt;span class="key"&gt;protected&lt;/span&gt; &lt;span class="key"&gt;override&lt;/span&gt; &lt;span class="key"&gt;void&lt;/span&gt; OnFrameAssigned(){
            &lt;span class="key"&gt;base&lt;/span&gt;.OnFrameAssigned();
            &lt;span class="com"&gt;//1.execute once at application startup .&lt;/span&gt;
            &lt;span class="key"&gt;if&lt;/span&gt; (Frame.Context == TemplateContext.ApplicationWindow){
                &lt;span class="com"&gt;//2.collect the model rules to execute.&lt;/span&gt;
                &lt;span class="key"&gt;var&lt;/span&gt; purgingRules = Application.Model.BOModel.Cast&amp;lt;IModelClassPurgingRules&amp;gt;().SelectMany(rules =&amp;gt; rules.PurgingRules ).ToArray();
                IEnumerable&amp;lt;(IModelPurgingRule rule, DateTime executed)&amp;gt; ruleExecutionTimes;
                &lt;span class="com"&gt;//create an objectspace to query the last execution time from the database&lt;/span&gt;
                &lt;span class="key"&gt;using&lt;/span&gt; (&lt;span class="key"&gt;var&lt;/span&gt; objectSpace = Application.CreateObjectSpace(typeof(RuleScheduleStorage))){

                    DeleteObsoleteRules(objectSpace, purgingRules);
                    &lt;span class="key"&gt;var&lt;/span&gt; rulesToSchedule = purgingRules.Where(rule =&amp;gt;rule.Interval!=TimeSpan.MinValue);
                    &lt;span class="com"&gt;//get an enumerable of (IModelPurgingRule rule, DateTime executed)&lt;/span&gt;
                    ruleExecutionTimes = CalculateExecutionTimes(rulesToSchedule, objectSpace);
                }

                &lt;span class="key"&gt;foreach&lt;/span&gt; (&lt;span class="key"&gt;var&lt;/span&gt; ruleExecutionTime &lt;span class="key"&gt;in&lt;/span&gt; ruleExecutionTimes){
                    &lt;span class="key"&gt;var&lt;/span&gt; timeSinceLastExecution = DateTime.Now.Subtract(ruleExecutionTime.executed);
                    &lt;span class="com"&gt;//calculate if periodic task should start with a delay based time passed since last execution&lt;/span&gt;
                    &lt;span class="key"&gt;int&lt;/span&gt; delay=timeSinceLastExecution&amp;lt;ruleExecutionTime.rule.Interval?(&lt;span class="key"&gt;int&lt;/span&gt;) ruleExecutionTime.rule.Interval.Subtract(timeSinceLastExecution).TotalMilliseconds:0;
                    &lt;span class="com"&gt;//starts the task periodically&lt;/span&gt;
                    Task.Factory.StartNewPeriodic(() =&amp;gt; PurgeObjects(ruleExecutionTime.rule),
                        interval: (&lt;span class="key"&gt;int&lt;/span&gt;) ruleExecutionTime.rule.Interval.TotalMilliseconds, delay: delay);
                }
            }
        }

        &lt;span class="key"&gt;private&lt;/span&gt; &lt;span class="key"&gt;static&lt;/span&gt; IEnumerable&amp;lt;(IModelPurgingRule rule, DateTime executed)&amp;gt; CalculateExecutionTimes(IEnumerable&amp;lt;IModelPurgingRule&amp;gt; rulesToSchedule, IObjectSpace objectSpace){
            &lt;span class="key"&gt;return&lt;/span&gt; rulesToSchedule.Select(rule =&amp;gt; {
                &lt;span class="key"&gt;var&lt;/span&gt; ruleScheduleStorage = objectSpace.GetObjectsQuery&amp;lt;RuleScheduleStorage&amp;gt;()
                                              .FirstOrDefault(storage =&amp;gt;storage.RuleScheduleType == RuleScheduleType.Purging &amp;amp;&amp;amp; storage.RuleId ==((ModelNode) rule).Id);
                &lt;span class="key"&gt;return&lt;/span&gt; (rule:rule,executed:ruleScheduleStorage?.Executed ?? DateTime.MinValue);
            });
        }

        &lt;span class="key"&gt;private&lt;/span&gt; &lt;span class="key"&gt;void&lt;/span&gt; DeleteObsoleteRules(IObjectSpace objectSpace, IModelPurgingRule[] purgingRules){
            &lt;span class="key"&gt;var&lt;/span&gt; ids = purgingRules.Cast&amp;lt;ModelNode&amp;gt;().Select(node =&amp;gt; node.Id).ToArray();
            &lt;span class="key"&gt;var&lt;/span&gt; rulesToDelete = objectSpace.GetObjectsQuery&amp;lt;RuleScheduleStorage&amp;gt;().Where(storage =&amp;gt;
                storage.RuleScheduleType == RuleScheduleType.Purging &amp;amp;&amp;amp; !ids.Contains(storage.RuleId)).ToArray();
            objectSpace.Delete(rulesToDelete);
            objectSpace.CommitChanges();
        }&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;
You might wonder why I inherit from a controller and used the OnFrameAssigned method as start signal and not simply write my code at &lt;a href="https://documentation.devexpress.com/eXpressAppFramework/DevExpress.ExpressApp.XafApplication.SetupComplete.event" target="_blank"&gt;Application.SetupComplete.Event&lt;/a&gt;. The reason for this is that I like to keep my implementation in separate files and not pollute the Module.cs.&lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;The Long Running PurgeObjects method:&lt;/u&gt;&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt; Stores last execution time in the database.&lt;/li&gt;
&lt;li&gt;Deletes filtered objects in chunks.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;        &lt;span class="key"&gt;private&lt;/span&gt; &lt;span class="key"&gt;void&lt;/span&gt; PurgeObjects(IModelPurgingRule purgingRule){
            &lt;span class="key"&gt;try&lt;/span&gt;{
                Tracing.Tracer.LogVerboseText($&lt;span class="str"&gt;"Purging {purgingRule}"&lt;/span&gt;);
                &lt;span class="key"&gt;var&lt;/span&gt; objectsCount = 0;
                &lt;span class="key"&gt;using&lt;/span&gt; (&lt;span class="key"&gt;var&lt;/span&gt; objectSpace = Application.CreateObjectSpace(purgingRule.TypeInfo.Type)){
                    StoreExecutionTime(purgingRule, objectSpace);
                    &lt;span class="key"&gt;var&lt;/span&gt; criteriaOperator = objectSpace.ParseCriteria(purgingRule.Criteria);
                    &lt;span class="key"&gt;var&lt;/span&gt; objects = objectSpace.GetObjects(purgingRule.TypeInfo.Type, criteriaOperator);
                    objectSpace.SetTopReturnedObjectsCount(objects, purgingRule.ChunkSize);
                    &lt;span class="key"&gt;while&lt;/span&gt; (objects.Count &amp;gt; 0){
                        objectsCount += objects.Count;
                        objectSpace.Delete(objects);
                        objectSpace.CommitChanges();
                        objectSpace.ReloadCollection(objects);
                    }                    
                }
                Tracing.Tracer.LogVerboseText($&lt;span class="str"&gt;"Purged {purgingRule}-{objectsCount}"&lt;/span&gt;);
            }
            &lt;span class="key"&gt;catch&lt;/span&gt; (Exception e){
                Tracing.Tracer.LogError(e);
            }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;        &lt;span class="key"&gt;private&lt;/span&gt; &lt;span class="key"&gt;static&lt;/span&gt; &lt;span class="key"&gt;void&lt;/span&gt; StoreExecutionTime(IModelPurgingRule purgingRule, IObjectSpace objectSpace){
            &lt;span class="key"&gt;var&lt;/span&gt; ruleId = ((ModelNode) purgingRule).Id;
            &lt;span class="key"&gt;var&lt;/span&gt; ruleScheduleStorage =objectSpace.GetObjectsQuery&amp;lt;RuleScheduleStorage&amp;gt;().FirstOrDefault(storage =&amp;gt;
                    storage.RuleScheduleType == RuleScheduleType.Purging &amp;amp;&amp;amp; storage.RuleId == ruleId) ?? objectSpace.CreateObject&amp;lt;RuleScheduleStorage&amp;gt;();
            ruleScheduleStorage.RuleScheduleType = RuleScheduleType.Purging;
            ruleScheduleStorage.RuleId = ((ModelNode) purgingRule).Id;
            ruleScheduleStorage.Executed = DateTime.Now;
            objectSpace.CommitChanges();
        }&lt;/code&gt;&lt;/pre&gt;
&lt;u&gt;&lt;br /&gt;&lt;/u&gt;
&lt;u&gt;Last minute feature&lt;/u&gt;&lt;br /&gt;
&lt;u&gt;&lt;br /&gt;&lt;/u&gt;
I am happy with the &lt;em&gt;PurgingRules&lt;/em&gt;, but I would be more happy if I could invalidate them based on a &lt;em&gt;CSharp&lt;/em&gt; expression. For example I want to have some rules on my dev machine only.&lt;br /&gt;
The &lt;em&gt;PurgingRules&lt;/em&gt; already have a criterion so we could &lt;a href="https://documentation.devexpress.com/eXpressAppFramework/113480/Concepts/Filtering/Custom-Function-Criteria-Operators" target="_blank"&gt;create a Custom Function&lt;/a&gt; to evaluate &lt;em&gt;CSharp&lt;/em&gt; expressions.&lt;br /&gt;
I want to use this code in eXpandFramework which there is a &amp;lt;=.NET4 dependency so for evaluating CSharp expressions I chose compile on the fly + caching the from&lt;em&gt; &lt;a href="https://github.com/expand/eXpand/blob/626117d23a03e078369901c6482ac3e0ce71c955/Xpand/Xpand.Persistent/Xpand.Persistent.Base/General/CustomFunctions/EvaluateExpressionOperator.cs#L32-L41" target="_blank"&gt;ExpressionEvaluator.Eval&lt;/a&gt; &lt;/em&gt;method.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;    &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;class&lt;/span&gt; EvaluateExpressionOperator:ICustomFunctionOperator{
        &lt;span class="key"&gt;public&lt;/span&gt; List&amp;lt;&lt;span class="key"&gt;string&lt;/span&gt;&amp;gt; Usings=&lt;span class="key"&gt;new&lt;/span&gt; List&amp;lt;&lt;span class="key"&gt;string&lt;/span&gt;&amp;gt;();

        &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;const&lt;/span&gt; &lt;span class="key"&gt;string&lt;/span&gt; OperatorName = &lt;span class="str"&gt;"EvaluateExpression"&lt;/span&gt;;

        &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;static&lt;/span&gt; EvaluateExpressionOperator Instance{ &lt;span class="key"&gt;get&lt;/span&gt;; } = &lt;span class="key"&gt;new&lt;/span&gt; EvaluateExpressionOperator();

        &lt;span class="key"&gt;public&lt;/span&gt; Type ResultType(&lt;span class="key"&gt;params&lt;/span&gt; Type[] operands){
            &lt;span class="key"&gt;return&lt;/span&gt; &lt;span class="key"&gt;typeof&lt;/span&gt;(&lt;span class="key"&gt;object&lt;/span&gt;);
        }

        &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;object&lt;/span&gt; Evaluate(&lt;span class="key"&gt;params&lt;/span&gt; &lt;span class="key"&gt;object&lt;/span&gt;[] operands){
            &lt;span class="key"&gt;var&lt;/span&gt; csCode = &lt;span class="key"&gt;string&lt;/span&gt;.Join(&lt;span class="str"&gt;""&lt;/span&gt;,operands);
            &lt;span class="key"&gt;var&lt;/span&gt; usings = &lt;span class="key"&gt;string&lt;/span&gt;.Join(Environment.NewLine,Usings);
            &lt;span class="key"&gt;var&lt;/span&gt; eval = ExpressionEvaluator.Eval(csCode, usings);
            &lt;span class="key"&gt;return&lt;/span&gt; eval;
        }

        &lt;span class="key"&gt;public&lt;/span&gt; &lt;span class="key"&gt;string&lt;/span&gt; Name =&amp;gt; OperatorName;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;pre class="cscode"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEho9X3deD2uJS52gKt-yO2uIKHW0ynoMkGyi5flX8xvQ3sE6MXIOUxCh18di3jkRgq010aA87CCdwrLZzC8sh2otx6_02D0iieS62EKMq61ckBQ7p_0s4Xm9K1w4P6JHOoBSDLL_0FeKP6I/s1600-h/image%255B18%255D"&gt;&lt;img alt="image" border="0" height="219" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjufFI7biJE17RrMrf_5eP4bp9DkDHlBKSi9604UuLcAPfJpQUTY-ZlVqRlsqlGFwAqxpdWh6Z2wsstKJzWVQ0v_KyBqLw-porzl3zK_SgvRoSO10M0m7SydZ-wXsI_j-VBnz9-vcVpJGmB/?imgmax=800" style="background-image: none; border: 0px currentcolor; display: inline;" title="image" width="640" /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Working with such rich libraries like &lt;a href="https://www.devexpress.com/products/net/application_framework/" target="_blank"&gt;DevExpress XAF suite&lt;/a&gt; you make serious stuff in no time!</description><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPHtOaWeaO06N98i_T5PidS9XdC_FqnWQMrVgMfx6ckDnh07P8x7R0WHiwbvQZRmVpVmZZbNdxYQy4xBTonOnBre9zkBSDEuVxBJso_fKCGw-oZ_5FdMJ8Y9E55-p_JX_AGCKnDGS9k2gr/s72-c?imgmax=800" width="72"/></item></channel></rss>