Pixytech

Lead Architect  •  Full Stack Engineer

Category: C#

  • N-tier Sync Framework – OCA (Occasional connected Application)

    Sync Framework is a comprehensive synchronization platform that enables collaboration and offline access for applications, services, and devices. Sync Framework features technologies and tools that enable roaming, data sharing, and taking data offline. By using Sync Framework, developers can build synchronization ecosystems that integrate any application with data from any store, by using any protocol over any network.

    This article shows how to synchronize efficiently with a remote server by using a proxy provider on the local computer over secure WCF channel. The proxy provider uses the Remote Change Application pattern and Windows Communication Foundation (WCF) to send serialized metadata and data to the remote replica so synchronization processing can be performed on the remote computer (server) with fewer round trips between the client and server computers. Microsoft Sync Framework synchronizes data between data stores. Typically, these data stores are on different computers or devices that are connected over a network. In our case we will be using synchronization between local SQL Server 2008 and central (remote) SQL server 2008 (express edition). Following are the different (Visual Studio 2010 – .Net 4.0) projects involved in the solution

    1. Sync.WebServer : Web server (Asp.Net) project to host WCF Sync service, Authentication service and web portal to manage Sync clients.
    2. Sync.Library : Sync library (Class Library) used by client/server which provides server proxy to client and also provides base class for RelationalSyncService, ISqlSyncContract for WCF Sync service.
    3. Sync.Client : Windows based client which will perform database sync between SyncLocal and SyncServerCert via WCF service.

    For synchronizing two databases, Sync Framework supports two-tier and N-tier architectures that use any server database for which an ADO.NET provider is available. For synchronizing between a client database and other types of data sources, Sync Framework supports a service-based architecture. This architecture requires more application code than two-tier and N-tier architectures; however, it does not require a developer to take a different approach to synchronization.

    The following illustrations show the components that are involved in N-tier, and service-based architectures. Each illustration shows a single client, but there are frequently multiple clients that synchronize with a single server. Sync Framework uses a hub-and-spoke model for client and server database synchronization. Synchronization is always initiated by the client. All changes from each client are synchronized with the server before the changes are sent from the server to other clients. (These are clients that do not exchange changes directly with one another.)

    N-tier architecture requires a proxy, a service, and a transport mechanism to communicate between the client database and the server database. This architecture is more common than a two-tier architecture, because an N-tier architecture does not require a direct connection between the client and server databases.

    N-Tier Architecture

    For demo purpose the server side database is very simple, with just two tables in used in synchronization process. Create blank database SyncCenter and execute script SyncCenter_Script.sql (See database script attached with source code)

    Class ServerProvisioning.cs (in project Sync.WebServer) is used to create sync filter template and then create filtered scope for each client based on this template.

    We need add our tables to function CreateTemplate (filtered template)

    //Add tables which will participate in Sync sequence matters
    scopeDesc.Tables.Add(GetDescriptionForTable("Clients", ConnectionString));
    scopeDesc.Tables.Add(GetDescriptionForTable("Products", ConnectionString));
    
    //With each table we have to add @Id and filter records based on Client id
    
    SqlSyncTableProvisioning Clients = serverTemplate.Provisioning.Tables[GetTableFullName("Clients")];
    Clients.AddFilterColumn("Id");
    Clients.FilterClause = "[side].[Id] = @Id";
    Clients.FilterParameters.Add(new SqlParameter("@Id", SqlDbType.UniqueIdentifier));
    
    SqlSyncTableProvisioning Products = serverTemplate.Provisioning.Tables[GetTableFullName("Products")];
    Products.AddFilterColumn("ClientId");
    Products.FilterClause = "[side].[ClientId] = @Id ";
    Products.FilterParameters.Add(new SqlParameter("@Id", SqlDbType.UniqueIdentifier));

    Please note that you for each table which is used in synchronization process we have to add filter clause to filter records based on client id. You may use complex SQL queries with joins etc. to specify which records should be synchronized between multiple clients. The central server (single) holds data for multiple clients (multi – tenancy).See article Sync framework – choose your primary keys type carefully: http://www.codeproject.com/Articles/63275/Sync-framework-choose-your-primary-keys-type-caref

    The above mentioned code id called when you click on create template button from Central MS Sync web site (Project: Sync.WebServer).

    Before running our web server project (Sync.WebServer), you need to change connection string SyncCenterConnectionString in web.config file.The web server project Sync.WebServer is used to host WCF Sync service and also provides you admin panel from where you can setup and create Sync template and sync scopes for each client. The sync template will create filter based template and specify tables used in sync process and also define filter clause (Sql Queries) where as the sync scope will create scope of each client based on these templates where clientId is fixed. So that whenever you setup new sync client you need to create scope for this client before it can take participate in sync process.

    OK now, web server project is almost ready to run however we need to install membership provider and create few certificates which will be used in later stage. The Asp.Net membership provider is used to access this web portal.

    Follow below mentioned steps to install membership provider on your SyncCenter database.

    Run aspnet_regsql.exe utility from C:windowsMicrosoft.NETFrameworkv2.0.50727 folder on your machine.

    Choose your database and click next, next … finish

    And click next, next…. Finish.

    Now we need two X.509 certificates “SyncServerCert” and “SyncClientCert“

    Certificate SyncServerCert will be used by web server where as SyncClientCert will be distributed to its clients.

    To create certificate follow tasks mentioned below

    Execute Make Cert.bat available under certificates folders (download).This batch file is having following commands.

    Makecert.exe -r -pe -n "CN= SyncServerCert " -b 01/01/2000 -e 01/01/2050 -eku 1.3.6.1.5.5.7.3.1   -ss my -sr localMachine -sky exchange -sp   "Microsoft RSA SChannel Cryptographic Provider" -sy 12
    
    Winhttpcertcfg.exe -g -c  LOCAL_MACHINEMy -s "SyncServerCert" -a ASPNET
    
    Winhttpcertcfg.exe -g -c  LOCAL_MACHINEMy -s "SyncServerCert" -a "NETWORK SERVICE"
    
    Winhttpcertcfg.exe -g -c  LOCAL_MACHINEMy -s "SyncServerCert" -a "LOCAL SERVICE"
    
    Makecert.exe -r -pe -n "CN= SyncClientCert " -b 01/01/2000 -e 01/01/2050 -eku 1.3.6.1.5.5.7.3.1   -ss my -sr localMachine -sky exchange -sp   "Microsoft RSA SChannel Cryptographic Provider" -sy 12

    Makecert is available with visual studio installation and you can download from Winhttpcertcfg.exe from http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=19801

    Launch mmc and add Certificates (Local computer) to mmc. You will found certificates SyncServerCert & SyncClientCert under Personal Certificates.

    Copy (right click copy and paste) these certificates under Trusted People /certificates and under Trusted Root Certificate Authorities/certificates.

    Now export SyncClientCert certificate (alone with Private key) as pfx file. This can be deployed to sync clients.

    Server project is ready to run now, Launch web server project,create login (register) and navigate to sync Clients tab.

    Click on create template and then click on setup sync for each client

    Setup sync will execute following code

    //For filter parameter name see template below
    serverProv.Provisioning.PopulateFromTemplate(SyncConfigurations.ClientScopeName(ClientId), ServerProvisioning.TemplateName);
    serverProv.Provisioning.Tables[GetTableFullName("Clients")].FilterParameters["@Id"].Value = ClientId;
    serverProv.Provisioning.Tables[GetTableFullName("Products")].FilterParameters["@Id"].Value = ClientId;

    which will create sync scope of each client.

    At this stage, our server is ready to sync with 3 clients. Please note that Sync service uses wsHTTP binding with certificate authentication SyncServerCert.

    Sync.Client

    For sync client setup, create blank database “SyncLocal” on local machine or where your client application will run,Edit the connection string app.config and also specify the clientId in config file.Not, in production system you may need to provide service from where user will request authentication and once authenticated from server the server will provide clientId based on logon details. In that case you don’t need to hard code client id however for simplicity I have just used the fixed value (read from app.config).

    Run this application and click “Sync With Client”, Change Client Id in text box and again click sync With Client.

    Please note that sync framework will only create table (and primary keys) however relationships and constraints were not in scope o Sync. For more information check Microsoft documentation.

    Source Code

  • Async Entity Framework for WPF

    – Sample application , implemented ObjectContext, – base class for your entity data modal classes in WPF project, where data is used via ADO.net instead of WCF. The extension adds Async capability to your existing entity data model ..This is based on .net 4.0 Task..The below mentioned approach is one way implementing Async entity call , wrapped inside thread call and finally exposed event when results are available.Another prefred approach to achive this is via Reactive Extension library.

    The Reactive Extensions (Rx) is a library for composing asynchronous and event-based programs using observable sequences and LINQ-style query operators. Using Rx, developers represent asynchronous data streams with Observables, query asynchronous data streams using LINQ operators, and parameterize the concurrency in the asynchronous data streams using Schedulers. Simply put, Rx = Observables + LINQ + Schedulers.

     My next article is about Reactive Extensions for streaming data similar to SQL Server Management Studio.This way the grid would show first 100 available rows in the beginning and then when the next 100 rows were loaded they would be added to the grid and so on until the query completes.

    Create class ObjectContext

    public class ObjectContext :EntityContext, IDisposable
        {
            public LoadOperation LoadQuery(Action query)
            {
                return new LoadOperation(query);
            }
    
            public LoadOperation LoadQuery(Func<object> query)
            {
                return new LoadOperation(query);
            }
    
            public void Dispose()
            {
                //throw new NotImplementedException();
            }
        }
    
    public sealed class QueryResultEventArgs : EventArgs
        {
            internal QueryResultEventArgs(Exception exception)
            {
                this.Error = exception;
                this.HasError = true;
            }
    
            internal QueryResultEventArgs(Exception exception, bool isCancelled, bool isCompleted, bool hasError)
                : this(exception)
            {
                this.IsCanceled = isCancelled;
                this.IsComplete = isCompleted;
                this.HasError = hasError;
                /// if (hasError == false) this.Error = null;
    
            }
    
            internal QueryResultEventArgs(object result)
            {
                this.HasError = false;
                this.Result = result;
                this.IsComplete = true;
                this.IsCanceled = false;
            }
    
            public object Result { get; private set; }
            public Exception Error { get; private set; }
            public bool HasError { get; private set; }
            public bool IsCanceled { get; private set; }
            public bool IsComplete { get; private set; }
        }
    
        public sealed class LoadOperation : DispatcherObject, IDisposable
        {
            public event EventHandler<QueryResultEventArgs> Completed;
            private readonly CancellationTokenSource _taskCancelProvider;
            private readonly Task _task;
            private LoadOperation()
            {
                this._taskCancelProvider = new CancellationTokenSource();
            }
    
            internal LoadOperation(Func<object> query)
                : this()
            {
    
                Task<object> task = new Task<object>(query, this._taskCancelProvider.Token);
                _task = task;
                this.SetTaskInternal(task);
            }
    
            internal LoadOperation(Action query)
                : this()
            {
                Task task = new Task(query, this._taskCancelProvider.Token);
                _task = task;
                this.SetTaskInternal(task);
            }
    
            private void SetTaskInternal(Task<object> task)
            {
                task.ContinueWith(delegate
                {
                    try
                    {
                        RaiseCompletetionEvent(new QueryResultEventArgs(task.Result));
                    }
                    catch (Exception ex)
                    {
                        RaiseCompletetionEvent(new QueryResultEventArgs(ex.InnerException, task.IsCanceled, task.IsCompleted, task.IsFaulted));
    
                    }
                });
            }
    
            private void SetTaskInternal(Task task)
            {
                task.ContinueWith(delegate
                {
                    try
                    {
    
                        RaiseCompletetionEvent(new QueryResultEventArgs(null));
                    }
                    catch (Exception ex)
                    {
                        RaiseCompletetionEvent(new QueryResultEventArgs(ex));
    
                    }
                });
            }
    
            public bool CanCancel { get { return this._taskCancelProvider.Token.CanBeCanceled; } }
    
            public void Cancel()
            {
                this._taskCancelProvider.Cancel();
            }
    
            public void Cancel(bool throwOnFirstException)
            {
                this._taskCancelProvider.Cancel(throwOnFirstException);
            }
    
            private void RaiseCompletetionEvent(QueryResultEventArgs e)
            {
                //Wrap in DispatchThread
                this.Dispatcher.Invoke(
              System.Windows.Threading.DispatcherPriority.Normal,
              new Action(
                delegate()
                {
                    if (Completed != null)
                        Completed(this, e);
                }
            ));
    
            }
    
            public void Execute(bool Async = true)
            {
                if (Async)
                {
                    _task.Start();
                }
                else
                {
                    _task.RunSynchronously();
                }
            }
    
            public void Dispose()
            {
                _task.Dispose();
            }
        }
    
    // EntityContext - Base for all your entity classes (LocalEntities - generated by entity framework)
    
    public class EntityContext
        {
            public LocalEntities ObjectContext
            {
                get;
                private set;
            }
    
            public  EntityContext()
            {
                ObjectContext = new LocalEntities();
            }
        }
    
    // How to Executing Async load of data from entity (serviceUsersRepository is ObjectContext driven entity)
    
                serviceUsersRepository = new ServiceUsersRepository();
                LoadOperation result = serviceUsersRepository.LoadQuery(() => serviceUsersRepository.GetServiceUsers(objSearchCriteria.SearchText));
    
                result.Completed += ((object sensexSx, QueryResultEventArgs ex) =>
                {
                    this.AllServiceUsers = ex.Result as List<ServiceUser>;
                });
                result.Execute();
    
    // example of serviceUsersRepository is described below (where ServiceUsers is entity generated by entity framework)
    
    class ServiceUsersRepository : ObjectContext�
        {
            public ServiceUsersRepository()
            {
    
            }
    
            /// <summary>
            /// Returns a shallow-copied list of all Service Users in the repository.
            /// </summary>
            public List<ServiceUser> GetServiceUsers(string sText)
            {
                    var result = from serviceuser in ObjectContext.ServiceUsers
                                 select serviceuser;
                    return result.ToList<ServiceUser>();
            }
        }
  • Page flip with deep zoom

    Book control is another excellent page flip control which allows you to add any silverlight element as book page.It supports page down and page up as bidiretional navigation keys.Demo application & source code is attached below.
    [silverlight: http://www.pixytech.com/rajnish/uploads/code/BookDemo.xap, 850, 650]

    The objective of this article is to create photo album application which allows user to add pictures just by drag and drop on album.The picture will be splitted into parts and uploaded on server where it is re-constructed and again processed into deep zoom meta file (on fly) and link to this picture will be then added into album.Photo album will have deep zoom enabled pages.

    I couldn’t get much time to finish this project & hence i would call this as beta source code which includes the full source code of Book control (page flip)

    Beta Source code for book control is available here

  • Xap Loader Splash Screen

    [silverlight:http://www.pixytech.com/rajnish/uploads/code/Splash.xap,550, 550]

    The objective of this article is to create custom xap loader screen without any background image and code behind (pure XAML).In the final application version, download progress of root xap file will be shown in the sample screen above..

    Will write more on this article in next few days …

  • Silverlight TextBox AutoComit Behaviour

    Standard Silverlight TextBox control is very useful but has one strange behavior: if you use TwoWay data binding and bind some property to controls Text property, when users type text into the control, this change is not propagated to the bound property until the control loses its focus.  

    This can be very annoying if you have MVVM application and you have some kind of real-time filter that needs to update some data as-you-type.  

    In TwoWay bindings, changes to the target automatically update the source, except when binding to the Text property of a TextBox. In this case, the update occurs when the TextBox loses focus (in case of element to element binding the text box behaviour is normal) .  

    You can disable automatic source updates and update the source at times of your choosing. For example, you can do this to validate user input from multiple controls before you update the bound data sources.  

    You must update the source for each binding individually, however. To update a binding, first call the FrameworkElement.GetBindingExpression method of a target element, passing in the target DependencyProperty. You can then use the return value to call the BindingExpression.UpdateSource method. The following example code demonstrates this process  

     Problem is that TextBox control does not call BindingExpression.UpdateSource when its Text property is changed so we have to do that manually in order to fix this issue.The AutoComit Behavior for textbox control is now part of NanoVMSupport Library (Lib for MVVM)  

    public class AutoCommit : Behavior<TextBox>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.TextChanged += AssociatedObjectOnTextChanged;
        }
    
        private void AssociatedObjectOnTextChanged(object sender, TextChangedEventArgs args)
        {
            var bindingExpr = AssociatedObject.GetBindingExpression(TextBox.TextProperty);
            if(bindingExpr != null) bindingExpr.UpdateSource();
        }
    
        protected override void OnDetaching()
        {
            AssociatedObject.TextChanged -= AssociatedObjectOnTextChanged;
            base.OnDetaching();
        }
    }

    And the xaml is  

    <TextBox Text=”{Binding SearchText,Mode=TwoWay}”> 
     <i:Interaction.Behaviors> 
    <NanoVM:AutoCommit/> 
    </i:Interaction.Behaviors> 
    </TextBox> 

    Another approach to solve this issue is to create TextBoxEx class drived from TextBox.With this approach the xaml size will be highly reduced as for each text box in your application it would save around 90 chars in xaml.

    public class TextBoxEx : TextBox
    {
        public TextBoxEx()
        {
            this.Loaded += new RoutedEventHandler(TextBoxEx_Loaded);
        }
    
        void TextBoxEx_Loaded(object sender, RoutedEventArgs e)
        {
            this.TextChanged += new TextChangedEventHandler(TextBoxEx_TextChanged);
        }
    
        void TextBoxEx_TextChanged(object sender, TextChangedEventArgs e)
        {
            var source = sender as TextBox;
            if (source != null)
            {
                var bindingExpression = source.GetBindingExpression(TextBox.TextProperty);
                if (bindingExpression != null)
                {
                    bindingExpression.UpdateSource();
                }
            }
        }
    
    }

    and xaml in this case is 

    <ctrls:TextBoxEx Text=”{Binding SearchText,Mode=TwoWay}”/>

    However the above behaviour give you more control of how text box behaves.