Enterprise configuration management

Almost every application requires some form of configuration information. This information can be as simple as a database connection string or as complex as multipart and hierarchical user preference information. How and where to store an application’s configuration data are questions you often face as a developer.

Any large enterprise application has many moving blocks. They all need to be configured for a proper working of the application. As the application size increases or for scalability the same configuration has to be repeated in different applications. For most applications once the configuration has been changed the application needs to be restarted.

Sample Code (create a blank console project, add json.net nuget)

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;

namespace CM
{
    // configuration management API, 
    //1. allow clients specify typesafe models for configuations
    //2. Store flat data on server (table) which is easy to edit
    //3. can be extended to have inheritance of values (overrides)
    //4. can be extended to lock / unlock certain property by admins etc.
    
    class Program
    {
        //Sample configuration model
        internal class SampleConfigModal
        {
            public SampleConfigModal()
            {
                Address = new Address();
            }
            public string Name { get; set; }
            public Address Address { get; set; }
            public int Age { get; set; }


        }
        public class Address
        {
            public string Street { get; set; }

        }

        // this is how client api will look like
        static void Main(string[] args)
        {
            // sample client code
            var data = new SampleConfigModal() { Name = "Rajnish", Age = 18, Address = new Address() { Street = "Oxley" } };

            // save Configuration
            SaveConfiguration("app", "section", data);

            //get Configuration
            var data2 = GetConfiguration("app", "section");
        }

        // Client side framework api -> call to rest end point
        private static T GetConfiguration(string appName, string SectionName) where T : new()
        {
            var defaultValue = new T();
            var samplePayload = JsonConvert.SerializeObject(defaultValue);
            var payload = GetConfiguration(appName, SectionName, samplePayload);
            return JsonConvert.DeserializeObject(payload);
        }

        // Client side framework api -> call to rest end point
        private static void SaveConfiguration(string appName, string SectionName, T data)
        {
            var payload = Newtonsoft.Json.JsonConvert.SerializeObject(data);
            SaveConfigurationa(appName, SectionName, payload);
        }


        //---------------------------------------- Server Code -------------------------- 
        //-------------- server has no knowledge of configuration structure or model

        private static Dictionary<string, string> storage;

        private static void SaveConfigurationa(string appName, string SectionName, string payload, string enumForHierarchyLevel = null)
        {
            // transformer
            var section = string.Format("{0}.{1}", appName, SectionName);
            var data = (JObject)JsonConvert.DeserializeObject(payload);
            var keyValueData = Flatten(data, section);

            // check if user has permission for level overrides
            // store with proper overides

            // store the flat list in sql or data 
            //| KEY |           |Value|            |OverrideType| - default,sysadmin,appadmin,groups,user etc
            //app.section.Name, Rajnish
            //app.section.Address.Street, Oxley
            //app.section.Age, 18

            storage = keyValueData;
        }

        private static string GetConfiguration(string appName, string SectionName, string samplePayload)
        {
            var section = string.Format("{0}.{1}", appName, SectionName);
            var data = (JObject)JsonConvert.DeserializeObject(samplePayload);
            var keyValueSample = Flatten(data, section);
            // update data from sql or data store
            // apply property override rules and get value from overrides if exists
            var keyValueData = keyValueSample.Select(x => new KeyValuePair<string, string>(x.Key, storage[x.Key]));

            //read these
            //app.section.Name, Rajnish
            //app.section.Address.Street, Oxley
            //app.section.Age, 18

            UnFlatten(data, section, keyValueData);

            var formatedData = JsonConvert.SerializeObject(data);
            /*
             * {
                  "Name": "Rajnish",
                  "Address": {
                    "Street": "Oxley"
                  },
                  "Age": "18"
                }
             * */
            return formatedData;

        }
        
        // Server side json helper

        private static void UnFlatten(JObject jsonObject, string prefix, IEnumerable<KeyValuePair<string, string>> data)
        {
            foreach (var item in data)
            {
                var keyName = item.Key.Substring(prefix.Length + 1);
                var storageValue = item.Value;
                if (keyName.Contains("."))
                {
                    var keys = keyName.Split('.');
                    var jtoken = (JToken)jsonObject;
                    foreach (var k in keys)
                    {
                        jtoken = jtoken.SelectToken(k);
                    }
                    ((JValue)jtoken).Value = storageValue;
                }
                else
                {
                    jsonObject[keyName] = storageValue;
                }
            }
        }

        private static Dictionary<string, string> Flatten(JObject jsonObject, string prefix)
        {

            IEnumerable jTokens = jsonObject.Descendants().Where(p => p.Count() == 0);
            Dictionary<string, string> results = jTokens.Aggregate(new Dictionary<string, string>(), (properties, jToken) =>
            {
                properties.Add(string.Format("{0}.{1}", prefix, jToken.Path), jToken.ToString());
                return properties;
            });
            return results;
        }
    }
}

Windows 8: TopMost window

TopMost

I am working on my next ambitious project “MouseTouch” which is multi touch simulator application for windows 8 platform and intended to increase the productivity if you are running windows 8 on non-touch device.

This will bring the touch feature of windows 8 to life even if you are using mouse pad.

The first challenge is how to render something on top of metro start menu items?

So if you want to create a true topmost window which can float even on top of windows 8 metro apps here are the simple steps..

  1. Create WPF visual studio application (or any other window app )
  2. Set TopMost=True for MainWindow
  3. Right-click on your project in the Solution Explorer
  4. Select “Add New Item” from the context menu.
  5. Choose “Application Manifest File” from the list of options in the dialog box that appears.
  6. Right-click on your project in the Solution Explorer and click “Properties” (or double-click on the “Properties” item under your project).
  7. Under the first tab (“Application”),select your app.manifest file from the drop-down box labeled “Manifest”.
  8. Set the, <requestedExecutionLevel level=”asInvoker” uiAccess=”true” />

The next part is to create a certificate and install it in trusted root authorities.

  1. Create certificate 
    1. makecert -r -pe -ss PrivateCertStore-n “CN=TopMost.com” topmost.cer
  2. Import certificate to (Local Machine) trusted root certification authorities using mmc.exe.

Now sign your executable using the certificate, either by using the command, or using visual studio (check the delay sign flag).

  1. Signtool sign /v /s PrivateCertStore /n TopMost.com /t http://timestamp.verisign.com/scripts/timestamp.dll TopMost.exe

Now copy the TopMost.exe to trusted location like c:Windows or program files and execute the TopMost.exe..

Still struggling ..

Ok if , executable is not signed or certificate is not installed properly, you see following exception

referralError

 

 

 

 

 

 

 

To avoid this exception, open mmc.exe, add certificates snap-in>Select Computer account>Local Computer>

Go to trusted root certification authorities>certificates>right click > tasks and import the certificate ..

CertInstall

Determine an Assembly’s Fully Qualified Name

While extending the Visual Studio development you might be working on an extension that would be using the fully qualified (4-part) name of an assembly.
Below are the simple steps to extend the visual studio
Open Visual Studio
Go to Tools –> External Tools
Click on “Add” to add a tool, and put in the following values:
Title: Get Qualified Assembly Name
Command: Powershell.exe
Arguments: -command “[System.Reflection.AssemblyName]::GetAssemblyName(“$(TargetPath)”).FullName”

When this menu item is selected (Tools->Get Qualified Assembly Name), the assembly name is given in the output window.
It should look like

MyApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c2534056547c8962

 

@@servername

If @@servername returns null or return wrong host name, execute below mentioned sql Query.


DECLARE @Current SysName
Select @Current = Convert(varchar(128), SERVERPROPERTY(‘ServerName’) )
IF(@Current <> @@SERVERNAME)
BEGIN
EXEC sp_dropserver @@SERVERNAME
EXEC sp_addserver @server=@Current,@local = ‘local’
EXEC master..xp_cmdshell ‘ECHO NET STOP MSSQLSERVER > restartSQL.bat’, no_output
EXEC master..xp_cmdshell ‘ECHO NET START MSSQLSERVER >> restartSQL.bat’, no_output
EXEC master..xp_cmdshell ‘restartSQL.bat’, no_output
–Connection with sql break here
END

— Check if every thing is correct
SELECT @@Servername
SELECT SERVERPROPERTY(‘ServerName’)

Export data from corrupted database

Below is the sql script to import data from source database into target database, It is assumed that you have both the databases on single server.The source database is current database & few tables are corrupted whereas the traget database is created from old backup for target database.since their is corruption ,it is not possible to take backup of current database. This script imports back data (only) from source to target database. You need to only replace 3 lines of the script (12th line from bottom).
Download script from here

 

------------- Create helper functions -----------------------------

IF EXISTS (select * from dbo.sysobjects where id = object_id(N'[dbo].[Mig_ImportTable]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
DROP PROCEDURE [dbo].[Mig_ImportTable]
GO

CREATE PROC dbo.Mig_ImportTable @Database SYSNAME, @Table SYSNAME AS
	SET NOCOUNT ON

	DECLARE @Column SYSNAME
	DECLARE @SQL NVARCHAR(4000)
	DECLARE @colSQL NVARCHAR(4000)
	DECLARE @IsIdentity BIT
	DECLARE @IsTableIdentity BIT

	SET @colSQL = ''
	SET @SQL=''
	SET @IsTableIdentity = 0 --false
	SET @IsIdentity = 0 --false

	PRINT 'Table Migration Started for :' + @Table + ' in ' + @Database

	DECLARE curMoveDown CURSOR
	LOCAL FORWARD_ONLY
	OPTIMISTIC FOR
	SELECT column_name,COLUMNPROPERTY(object_id(TABLE_NAME), COLUMN_NAME, 'IsIdentity')as IsIdentity FROM information_schema.columns
	WHERE UPPER(table_name ) = UPPER(@Table)

	OPEN curMoveDown FETCH NEXT FROM curMoveDown INTO @Column,@IsIdentity
	WHILE @@FETCH_STATUS = 0
	BEGIN
		SET @colSQL = @colSQL + '[' + @Column + '],'
		SET @IsTableIdentity = @IsTableIdentity | @IsIdentity
		IF (@IsTableIdentity = 1) BREAK
		FETCH NEXT FROM curMoveDown INTO @Column,@IsIdentity
	END
	CLOSE curMoveDown
	DEALLOCATE curMoveDown 

	IF(LEN(@colSQL)>1)
	BEGIN
		SET @colSQL = LEFT(@colSQL,LEN(@colSQL) - 1)

		IF(@IsTableIdentity = 1) SET @SQL = @SQL + 'SET IDENTITY_INSERT ' + @Table + ' ON '
		SET @SQL = @SQL + '	ALTER TABLE ' + @Table + ' DISABLE TRIGGER ALL '
		SET @SQL = @SQL + '	ALTER TABLE ' + @Table + ' NOCHECK CONSTRAINT ALL '
		SET @SQL = @SQL + '	TRUNCATE TABLE ' + @Table + '  '
		IF(@IsTableIdentity = 1)
			SET @SQL = @SQL + '	INSERT INTO ' + @Table + ' (' +  @colSQL + ') SELECT '+  @colSQL + ' FROM ' + '[' + @Database + '].[dbo].[' + @Table + '] '
		ELSE
			SET @SQL = @SQL + '	INSERT INTO ' + @Table + ' SELECT * FROM ' + '[' + @Database + '].[dbo].[' + @Table + '] '
		IF(@IsTableIdentity = 1) SET @SQL = @SQL + '	SET IDENTITY_INSERT ' + @Table + ' OFF '
		SET @SQL = @SQL + '	ALTER TABLE ' + @Table + ' ENABLE TRIGGER ALL '
		SET @SQL = @SQL + '	ALTER TABLE ' + @Table + ' CHECK CONSTRAINT ALL '

		EXEC sp_executesql @SQL
		PRINT 'Table migrated :' + @Table
	END
	ELSE
		PRINT 'No Column found :' + @Table + ' (SQL = ' + @colSQL + ')'

	PRINT 'Table Migration finished for :' + @Table
GO

IF EXISTS (select * from dbo.sysobjects where id = object_id(N'[dbo].[Mig_ImportDatabase]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
DROP PROCEDURE [dbo].[Mig_ImportDatabase]
GO

CREATE PROC dbo.Mig_ImportDatabase @Source SYSNAME AS	

	DECLARE @SERVER SYSNAME
	DECLARE @INSTANCE SYSNAME
	DECLARE @FULLNAME SYSNAME
	DECLARE @DBNAME SYSNAME
	DECLARE @cmd varchar(1000)
	DECLARE @SQL Nvarchar(1000)
	DECLARE @Table Nvarchar(100)
	DECLARE @Result INT

	SET NOCOUNT ON

	CREATE TABLE [#Mig_FailedTables] ( [name] SYSNAME NOT NULL ) ON [PRIMARY]

	SELECT @SERVER = CONVERT(SYSNAME, SERVERPROPERTY('servername'))
	SELECT @INSTANCE = IsNull('',CONVERT(SYSNAME, SERVERPROPERTY('InstanceName')))
	SELECT @DBNAME = DB_NAME()

	IF(Len(@INSTANCE)>0) SET @FULLNAME = @SERVER + '' + @INSTANCE
	ELSE SET @FULLNAME = @SERVER

	Print 'Migration started from database [' + @SOURCE + '] to database [' + @DBNAME + '] on server  ' + @FULLNAME

	--Cursor to loop throw all tables of Target database (current database)
	DECLARE CurTables CURSOR LOCAL FORWARD_ONLY
	OPTIMISTIC FOR
	SELECT NAME from dbo.sysobjects where OBJECTPROPERTY(id, N'IsUserTable') = 1 order by Name

	OPEN CurTables FETCH NEXT FROM CurTables INTO @Table
	WHILE @@FETCH_STATUS = 0
	BEGIN
		Print 'Migrating table ' + @Table
		SET @cmd = 'ECHO Exec [Mig_ImportTable] ''' + @Source + ''',''' + @Table + '''  > DbMig.sql'
		EXEC @Result = master..xp_cmdshell @cmd, no_output

		SET @cmd = 'ECHO GO  >> DbMig.sql'
		EXEC @Result =  master..xp_cmdshell @cmd, no_output

		SET @cmd = 'ECHO @ECHO OFF  > DbMig.cmd'
		EXEC @Result =  master..xp_cmdshell @cmd, no_output

		SET @cmd = 'ECHO osql -E -b -S "' + @FULLNAME +'" -d "' + @DBNAME +'" -i "DbMig.sql" >> DbMig.cmd'
		EXEC @Result =  master..xp_cmdshell @cmd, no_output

		SET @cmd = 'ECHO EXIT ERRORLEVEL >> DbMig.cmd'
		EXEC @Result =  master..xp_cmdshell @cmd, no_output

		SET @cmd = 'CMD /c "DbMig.cmd>>DbMig.Log"'
		EXEC @Result = master..xp_cmdshell @cmd, no_output
		IF (@Result <> 0)
			Insert into #Mig_FailedTables (Name) values (@Table)
		FETCH NEXT FROM CurTables INTO @Table

	END
	CLOSE CurTables
	DEALLOCATE CurTables
	Print 'Migration Finished..'
	SELECT Name as [Failed Tables] FROM #Mig_FailedTables
	DROP TABLE #Mig_FailedTables

GO

------------- Migration of data starts from here ------------------

----------------------------------------------------------------------------------------------------------------------------------

USE TargetDatabase						-- Target Database  	** Change This
Exec Mig_ImportDatabase 'SourceDatabase'			-- Source Database  	** Change This - Import Complete database
EXEC Mig_ImportTable 'SourceDatabase','SpecificTableName'	-- SpecificTableName  	** Change This - Import Single Table
----------------------------------------------------------------------------------------------------------------------------------