Reliable bring external process window to foreground with C#

If you want to reliable bring the main window of an external process to the foreground in C#, use the “simulate alt key” technique found at: http://stackoverflow.com/questions/10740346/setforegroundwindow-only-working-while-visual-studio-is-open

Just start Notepad, then run the following test in Visual Studio.

It should bring Notepad as a maximized window in the foreground.

namespace Research.EndToEndTests
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;

    [TestClass]
    public class ResearchTester
    {
        
        [TestMethod]
        public void Test()
        {
            Reliable_set_window_to_forground();
            Assert.IsTrue(true);
        }

        public void Reliable_set_window_to_forground()
        {
            Process[] processes = Process.GetProcesses();
            foreach (Process proc in processes)
            {
                if (ProcessIsNotepad(proc))
                {
                    ActivateWindow(proc.MainWindowHandle);
                }
            }
        }

        public bool ProcessIsNotepad(Process proc) 
        {
            return proc.MainWindowTitle.EndsWith("Notepad", StringComparison.InvariantCultureIgnoreCase);
        }
        
        private const int ALT = 0xA4;
        private const int EXTENDEDKEY = 0x1;
        private const int KEYUP = 0x2;
        private const int SHOW_MAXIMIZED = 3;

        [DllImport("user32.dll")]
        private static extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll")]
        private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
        [DllImport("user32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);
        [DllImport("user32.dll")]
        private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
        
        public static void ActivateWindow(IntPtr mainWindowHandle)
        {
            // Guard: check if window already has focus.
            if (mainWindowHandle == GetForegroundWindow()) return;

            // Show window maximized.
            ShowWindow(mainWindowHandle, SHOW_MAXIMIZED);
            
            // Simulate an "ALT" key press.
            keybd_event((byte)ALT, 0x45, EXTENDEDKEY | 0, 0);
                        
            // Simulate an "ALT" key release.
            keybd_event((byte)ALT, 0x45, EXTENDEDKEY | KEYUP, 0);

            // Show window in forground.
            SetForegroundWindow(mainWindowHandle);
        }
    }
}

Changing C# on the fly (without stopping the debugger) in an ASP .NET vNext web application with Visual Studio 14 CTP3

The roslyn compiler found in Visual Studio 14 enables us, to adjust C# code during runtime, by compiling the new C# code in-memory. Now we can edit the UI part (HTML, CSS, JavaScript) of a single page app, including the backend C# ASP .NET Web Api service dynamically in runtime.

 

On of the things, that disrupts my workflow, when creating a single page app with AngularJS, backed by a web api C# service in Visual Studio 2013, is the fact that, when I find a bug in my C# code while testing the UI, I have to stop the debugger, adjust the web api C# code, re-compile, re-run and then recreating state in the web app before I can resume testing the UI.

This pain will go away, when using Visual Studio 14

 

Download and install Visual Studio 14 CTP 3 from http://msdn.microsoft.com/en-US/subscriptions/downloads/

Open Visual Studio, choose File > New > Project …

 

image

 

image

 

Start the web application by pressing CTRL + F5 (so don’t start the debugger).

 

image

 

Click on the Contact page in the browser:

You will see the text “Your contact page”.

image

 

Now go to the Controllers\HomeController.cs

image

 

Adjust the the C# code: ViewBag.Message = "Your contact page.";

to

ViewBag.Message = "My contact page.";

 

image

 

Save the file and in the browser press F5, you might see a short delay, because the roslyn compiler is now dynamically compiling the code.

 

The C# code is changed without disrupting my workflow.

 

image

AngularJS and Breeze – A simple crud app – Part 2 – Adding create, delete, reset and is dirty checking.

 

In my previous post I created a simple [AngularJS – Breeze] edit view. In this post I will add the create, delete, reset and “is dirty” (entity change state tracking) to the simple view.

 

Before create

image

 

Create

The user clicked on the create button, so an “is dirty” icon is shown and a new row is added to the grid.

image

 

Save

When the user fills the fields of the new row and clicks save, the “is dirty” icon will disappear and the id field will automatically be filled by the id generated on the server in the database.

 

image

 

Reset

When the user create, updated and deleted some records and pressed save, the original state of the database can be restored by pressing the reset button.

image

 

 

index.html

<!DOCTYPE html>
<html data-ng-app="app">
<head>
    <title data-ng-bind="title">Angular and Breeze</title>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no" />

    <!-- Libraries -->
    <link rel="stylesheet" type="text/css" href="../../../Libraries/FontAwesome/css/font-awesome.min.css" />
    <link rel="stylesheet" type="text/css" href="../../../Libraries/Toastr/toastr.min.css" />
    
    <!-- App -->
    <link rel="stylesheet" type="text/css" href="app.css" />
</head>
<body>
    <div class="spa-page" data-ng-controller="admin as vm">
        <div class="spa-grid-toolbar">
            <a class="spa-action-link" ng-click="vm.save()">save</a> |
            <a class="spa-action-link" ng-click="vm.reset()">reset</a> |
            <a class="spa-action-link" ng-click="vm.create()">create</a>
            <i class="fa fa-exclamation-circle" 
               title="Some data has change. Press save to save the changes to the server!" 
               ng-show="vm.isDirty"></i>
        </div>
        <table class="spa-grid">
            <thead>
                <tr>
                    <th> </th>
                    <th ng-repeat='(key, prop) in vm.entityFields'>{{ prop.name }}</th>
                </tr>
            </thead>
            <tbody>
                <tr ng-repeat="entity in vm.entities">
                    <td><a class="spa-action-link" ng-click="vm.delete(entity)">delete</a></td>
                    <td ng-repeat='(key, prop) in vm.entityFields'>
                        <input ng-disabled="{{vm.isReadOnlyField(prop.name)}}" 
                               type='text' 
                               ng-model='entity[prop.name]'>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>

    <!-- Libraries -->
    <script type="text/javascript" src="../../../Libraries/Angular/angular.js"></script>
    <script type="text/javascript" src="../../../Libraries/Breeze/breeze.debug.js"></script>
    <script type="text/javascript" src="../../../Libraries/Breeze/breeze.angular.js"></script>

    <!-- Add toastr which needs jQuery (Breeze does not need jQuery) -->
    <script type="text/javascript" src="../../../Libraries/jQuery/jquery-2.1.1.js"></script>
    <script type="text/javascript" src="../../../Libraries/Toastr/toastr.js"></script>
    
<!-- Add breeze.savequeuing which needs Q (Breeze does not need Q)--> <script type="text/javascript" src="../../../Libraries/Q/q.min.js"></script> <script type="text/javascript" src="../../../Libraries/Breeze/breeze.savequeuing.js"></script> <!-- App --> <script type="text/javascript" src="app.js"></script> </body> </html>

 

 

app.js

// Use namespaces to prevent pollution of the global namespace.
var spa = spa || {};
spa.controllers = spa.controllers || {};

// Angular module [app].
spa.app = (function () {
    'use strict';

    var app = angular.module('app', [
        'breeze.angular' // The breeze service module.
    ]);
})();

// Angular controller [admin].
spa.controllers.admin = (function () {
    'use strict';
    var controllerId = 'admin';
    angular.module('app').controller(controllerId, ['$http', 'breeze', admin]);

    function admin($http, breeze) {
        var entityChangedToken = null;
        var entityTypeName = "Employee";
        var manager = null;

        var vm = this;
        vm.create = function () {
            // Create entity by breeze.
            var entity = manager.createEntity(entityTypeName);
            // Show entity to user.
            vm.entities.push(entity);
        };
        vm.delete = function (entity) {
            // Delete from UI
            vm.entities.pop(entity);

            // Mark for deletion.
            entity.entityAspect.setDeleted();
        };
        vm.entities = [];
        vm.entityFields = null;
        vm.isDirty = false;
        vm.isReadOnlyField = function (name) {
            // Make 'id' fields read-only.
            return (name === 'id');
        };
        vm.reset = function () {
            // Re-seed database and refetch data.
            $http.get('/breeze/breeze/ReSeed').then(getData).then(handleResetResult).catch(showError);
        };
        vm.save = function () {
            manager.saveChanges().then(handleSaveResult).catch(showError);
        };

        function handleStateChange(args) {
            vm.isDirty = true;
        }

        function getData() {
            // Get entities from the server.
            var query = new breeze.EntityQuery().from(entityTypeName);
            manager.executeQuery(query).then(handleGetDataResult).catch(showError);
        }

        function initialize() {
            // Use camel case for entity properties.
            breeze.NamingConvention.camelCase.setAsDefault();

            // Configure and create EntityManager (double breeze is needed, because of .
            manager = new breeze.EntityManager('/breeze/breeze');
            manager.enableSaveQueuing(true);
            registerForStateChange();

            getData();
        }

        function handleGetDataResult(data) {
            // Get entity fields from metadata.
            var entityMetaData = manager.metadataStore.getEntityType(entityTypeName);
            vm.entityFields = entityMetaData.dataProperties;

            // Show the enties from the server.
            vm.entities = data.results;
            vm.isDirty = false;
        }

        function handleResetResult() {
            vm.isDirty = false;
            toastr.info("Database re-seeded.");
        }

        function handleSaveResult() {
            vm.isDirty = false;
            toastr.info("Changes saved to the server.");
        }

        function registerForStateChange() {
            // Make sure to only subscribe once.
            if (entityChangedToken) { return; }

            // Register for state change.
            entityChangedToken = manager.entityChanged.subscribe(handleStateChange);
        }

        function showError(e) {
            // Show xhr error.
            toastr.error(e);
        }

        initialize();
    }
})();

 

app.css

/* Resets */
html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre,abbr, address, cite, code,
del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset,
form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, 
figcaption, figure, footer, header, hgroup, menu, nav, section, summary, time, mark, audio, video {
    border: 0;              /* Prevent unnecessary white space. */
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    box-sizing: border-box; /*  Border boxing is used, so the padding, margin and borders are within 
                                the width and height of the element. */
    margin: 0;              /* Prevent unnecessary white space. */
    outline: 0;             /* Prevent unnecessary white space. */
    padding: 0;             /* Prevent unnecessary white space. */
    font-size: 100%;
    vertical-align: baseline;
}

html, body {
    height: 100%;       /* Full screen single page app. */
    max-height: 100%;   /* Full screen single page app. */
}

body {
    padding: 20px;
}

a {
    cursor: pointer;
}

a.spa-action-link {
    color: #428bca;
    text-decoration: none;
}

a.spa-action-link:hover, a.spa-action-link:focus {
    color: #2a6496;
    text-decoration: underline;
}

div.spa-page {
    border: 1px solid rgb(212, 212, 212);
    height: 100%;       /* Full screen single page app. */
    max-height: 100%;   /* Full screen single page app. */
    padding: 20px;
    position: relative;
}

div.spa-page > a {
    margin-bottom: 10px;
}

div.spa-grid-toolbar i.fa-exclamation-circle {
    margin-left: 10px;
}

.spa-grid input[type="text"] {
    padding-left: 2px;
}

 

BreezeController.cs

namespace Research.UI.Web.Server.Controllers
{
    using Breeze.ContextProvider;
    using Breeze.ContextProvider.EF6;
    using Breeze.WebApi2;
    using Newtonsoft.Json.Linq;
    using Research.UI.Web.Server.Model;
    using System.Data.Entity.Migrations;
    using System.Linq;
    using System.Web.Http;

    [BreezeController]
    public class BreezeController : ApiController
    {
        private readonly EFContextProvider<ResearchDbContext> _contextProvider;

        public BreezeController(): this(null)
        {
        }

        public BreezeController(EFContextProvider<ResearchDbContext> contextProvider)
        {
            _contextProvider = contextProvider ??  new EFContextProvider<ResearchDbContext>();
        }

        [HttpGet]
        public IQueryable<Employee> Employee()
        {
            return _contextProvider.Context.Employees;
        }

        [HttpGet]
        public string Metadata()
        {
            string result = _contextProvider.Metadata();
            return result;
        }
                        
        [HttpGet]
        public void ReSeed()
        {
            // Remove all records from the "Employees" table.
            _contextProvider.Context.Database.ExecuteSqlCommand("truncate table Employees");

            // Run an "Update-Database" EF migrations command, this will update the database schema 
// to the latest state and run the Seed() method.
var configuration = new Research.UI.Web.Migrations.Configuration(); configuration.ContextType = typeof(ResearchDbContext); var migrator = new DbMigrator(configuration); migrator.Update(); } [HttpPost] public SaveResult SaveChanges(JObject saveBundle) { return _contextProvider.SaveChanges(saveBundle); } } }

 

 

For the complete code, see:

https://github.com/roelvanlisdonk/Research/tree/master/Research/Research.UI.Web/Client/Features/AngularJS_and_Breeze/Part2

AngularJS and Breeze – A simple crud app – Part 1 –Automatic field data binding.

 

I wanted a really simple view in AngularJS, that would allow me to edit the data in a database table.

The view should show a grid containing a row for each record in the database.

Each record should show textboxes (html inputs) for all columns in the database, except the “Id” column.

In this way I could edit all data in a database table.

 

Here’s the end result:

 

image

 

When the user clicks on the “save” button, the data is persisted to the database.

 

image

 

 

Breeze.js

I decided to use Breeze.js for the data interaction.

The main trick I used to automatically data bind the Breeze.js entity data properties (fields) to the UI in AngularJS, is getting the names of the entity data properties (database table column names) from the metadata and then using a ng-repeat to loop over the table records and within this ng-repeat an other ng-repeat to loop over de Breeze entity data properties.

 

Getting table column names with Breeze (part of the client side code):

var entityMetaData = manager.metadataStore.getEntityType(“Employee”);

vm.entityFields = entityMetaData.dataProperties;

Automatically data bind, Breeze entity data properties (part of the client side code):

<tbody> <tr ng-repeat="entity in vm.entities"> <td ng-repeat='(key, prop) in vm.entityFields'>
<input ng-disabled="{{vm.isReadOnlyField(prop.name)}}" type='text' ng-model='entity[prop.name]'>
</
td> </tr> </tbody>

Client side code

<!DOCTYPE html>
<html data-ng-app="app">
<head>
<title data-ng-bind="title">Research page</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, 
user-scalable=no" />
<!-- Libraries --> <link rel="stylesheet" type="text/css" href="../../Libraries/Toastr/toastr.min.css" /> <!-- App --> <style> /* Resets */ html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead,
tr, th, td, article, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav,
section, summary, time, mark, audio, video { border: 0; /* Prevent unnecessary white space. */ -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; /* Padding, margin and borders are within the width and height of the element. */ margin: 0; /* Prevent unnecessary white space. */ outline: 0; /* Prevent unnecessary white space. */ padding: 0; /* Prevent unnecessary white space. */ font-size: 100%; vertical-align: baseline; } html, body { height: 100%; /* Full screen single page app. */ max-height: 100%; /* Full screen single page app. */ } body { padding: 20px; } a { cursor: pointer; } a.spa-action-link { color: #428bca; text-decoration: none; } a.spa-action-link:hover, a.spa-action-link:focus { color: #2a6496; text-decoration: underline; } div.spa-page { border: 1px solid rgb(212, 212, 212); height: 100%; /* Full screen single page app. */ max-height: 100%; /* Full screen single page app. */ padding: 20px; position: relative; } div.spa-page > a { margin-bottom: 10px; } </style> </head> <body> <div class="spa-page" data-ng-controller="admin as vm"> <a class="spa-action-link" ng-click="vm.save()">save</a> <table> <thead> <tr> <th ng-repeat='(key, prop) in vm.entityFields'>{{ prop.name }}</th> </tr> </thead> <tbody> <tr ng-repeat="entity in vm.entities"> <td ng-repeat='(key, prop) in vm.entityFields'>
<
input ng-disabled="{{vm.isReadOnlyField(prop.name)}}"
type='text'
ng-model='entity[prop.name]'>
</
td> </tr> </tbody> </table> </div> <!-- Libraries --> <script type="text/javascript" src="../../Libraries/Angular/angular.js"></script> <script type="text/javascript" src="../../Libraries/Breeze/breeze.debug.js"></script> <script type="text/javascript" src="../../Libraries/Breeze/breeze.angular.js"></script> <!-- Add toastr which needs jQuery (Breeze does not need jQuery) --> <script type="text/javascript" src="../../Libraries/jQuery/jquery-2.1.1.js"></script> <script type="text/javascript" src="../../Libraries/Toastr/toastr.js"></script>
<!-- Add breeze.savequeuing which needs Q (Breeze does not need Q)-->
<script type="text/javascript" src="../../Libraries/Q/q.min.js"></script> <script type="text/javascript" src="../../Libraries/Breeze/breeze.savequeuing.js"></script> <!-- App --> <script type="text/javascript"> // Use namespaces to prevent pollution of the global namespace. var spa = spa || {}; spa.controllers = spa.controllers || {}; // Angular module [app]. spa.app = (function () { 'use strict'; var app = angular.module('app', [ 'breeze.angular' // The breeze service module. ]); })(); // Angular controller [admin]. spa.controllers.admin = (function () { 'use strict'; var controllerId = 'admin'; angular.module('app').controller(controllerId, ['breeze', admin]); function admin(breeze) { var manager = null; var entityTypeName = "Employee"; var vm = this; vm.entities = []; vm.entityFields = null; vm.isReadOnlyField = function (name) { // Make 'id' fields read-only. return (name === 'id'); }; vm.save = function () { manager.saveChanges().then(showInfo).catch(showError); }; function initialize() { // Use camel case for entity properties. breeze.NamingConvention.camelCase.setAsDefault(); // Configure and create EntityManager.
// The first breeze is part of the routing, second breeze is the name of the Web Api
// controller).
manager = new breeze.EntityManager('/breeze/breeze'); manager.enableSaveQueuing(true); // Get entities from the server. var query = new breeze.EntityQuery().from(entityTypeName); manager.executeQuery(query).then(handleRefresh).catch(showError); } function handleRefresh(data) { // Get entity fields from metadata. var entityMetaData = manager.metadataStore.getEntityType(entityTypeName); vm.entityFields = entityMetaData.dataProperties; // Show the enties from the server. vm.entities = data.results; } function showError(e) { // Show xhr error. toastr.error(e); } function showInfo() { // Show info message. toastr.info(entityTypeName + " saved!"); } initialize(); } })(); </script> </body> </html>

 

Sever side code

 

Employee.cs

namespace Research.UI.Web.Server.Model
{
public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
   public string LastName { get; set; }
  public string PhoneNumber { get; set; }
}
}

ResearchDbContext.cs

namespace Research.UI.Web.Server.Model
{
using System.Collections.Generic;
using System.Data.Entity;
public class ResearchDbContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
}    
}

BreezeWebApiConfig.cs

using System.Web.Http;
[assembly: WebActivator.PreApplicationStartMethod(
typeof(Research.UI.Web.App_Start.BreezeWebApiConfig), "RegisterBreezePreStart")]
namespace Research.UI.Web.App_Start {
///<summary>
/// Inserts the Breeze Web API controller route at the front of all Web API routes
///</summary>
///<remarks>
/// This class is discovered and run during startup; see
/// http://blogs.msdn.com/b/davidebb/archive/2010/10/11/light-up-your-nupacks-with-startup-code-and-webactivator.aspx
///</remarks>
public static class BreezeWebApiConfig {
public static void RegisterBreezePreStart() {
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze{controller}/{action}"
);
}
}
}

 

BreezeController.cs

namespace Research.UI.Web.Server.Controllers
{
using Breeze.ContextProvider;
using Breeze.ContextProvider.EF6;
using Breeze.WebApi2;
using Newtonsoft.Json.Linq;
using Research.UI.Web.Server.Components;
using Research.UI.Web.Server.Model;
using System.Linq;
using System.Web.Http;
[BreezeController]
public class BreezeController : ApiController
{
private readonly EFContextProvider<ResearchDbContext> _contextProvider;
public BreezeController(): this(null)
{
}
public BreezeController(EFContextProvider<ResearchDbContext> contextProvider)
{
_contextProvider = contextProvider ??  new EFContextProvider<ResearchDbContext>();
}
[HttpGet]
public string Metadata()
{
string result = _contextProvider.Metadata();
return result;
}
[HttpGet]
public IQueryable<Employee> Employee()
{
return _contextProvider.Context.Employees;
}

[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
}
}

C# snippet: Add CommonAssemblyInfo.cs as link to all C# projects.

We use a CommonAssemblyInfo.cs file, to contain all common AssemblyInfo information for all projects.

Like:

[assembly: AssemblyVersion("0.0.0.0")]
[assembly: AssemblyFileVersion("0.0.0.0")]
[assembly: AssemblyCompanyAttribute("ADA ICT")]
[assembly: AssemblyCopyrightAttribute("Copyright © ADA ICT 2014")]

Our continuous integration system, alters the CommonAssemblyInfo.cs file add build time, to sync the TFS build version with the assemblies found in the solution.

 

When you add this CommonAssemblyInfo.cs file as a link to your project in Microsoft Visual Studio:

image

 

Microsoft Visual Studio, will merge the information in the AssemblyInfo.cs with the information in the CommonAssemblyInfo.cs.

 

If you don’t want to add this link manual, you can use the following C# code:

 

VsSolution.cs

namespace Research.Core.CodeGeneration
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
public class VsSolution
{
/// <summary>
/// Add CommonAssemblyInfo as link to a project.
/// </summary>
public string AddCommonAssemblyInfo(string content)
{
string result = string.Empty;
if (!string.IsNullOrWhiteSpace(content))
{
result = content;
var findRegEx = new Regex(@"<ItemGroup>.*
<Compile Include=""..\\SharedSource\\CommonAssemblyInfo.cs"">
<Link>CommonAssemblyInfo.cs</Link>
</Compile>.*
</ItemGroup>", RegexOptions.Singleline);
if (!findRegEx.IsMatch(content))
{
var replaceRegEx = new Regex(@"<ItemGroup>");
result = replaceRegEx.Replace(result, @"<ItemGroup>
<Compile Include=""..\\SharedSource\\CommonAssemblyInfo.cs"">
<Link>CommonAssemblyInfo.cs</Link>
</Compile>", 1);
}
}
return result;
}
}
}

This code can be used with a small helper class to change all *.csproj files on disk:

 

FileSystem.cs

namespace Research.Core.Components
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
public class FileSystem
{
public async Task FindAndReplace(string path, string searchPattern, SearchOption searchOption, 
Func<string, string> updateContent)
{
string[] files = await GetFilesAsync(path, searchPattern, searchOption);
foreach (string file in files)
{
string content = await ReadAllTextAsync(file);
content = updateContent(content);
await WriteAllTextAsync(file, content);
}
}
public async Task<string[]> GetFilesAsync(string path, string searchPattern, SearchOption searchOption)
{
return await Task.Factory.StartNew(() =>
{
return Directory.GetFiles(path, searchPattern, searchOption);
});
}
public async Task<string> ReadAllTextAsync(string path)
{
string result = await Task.Factory.StartNew(() => { return File.ReadAllText(path); });
return result;
}
public async Task WriteAllTextAsync(string path, string content)
{
await Task.Factory.StartNew(() => { File.WriteAllText(path, content); });
}
}
}

To execute the code use:

[TestMethod] public void FindAndReplace_should_replace_common_assemblyinfo() { var solution = new VsSolution(); var fs = new FileSystem(); Task task = fs.FindAndReplace(@"C:\Projects\TNT\RouteMaker\Main-v1.0\Source",

"AssemblyInfo.cs", System.IO.SearchOption.AllDirectories, solution.RemoveCommonAssemblyInfo); task.Wait(); }

Code can be found at:

https://github.com/roelvanlisdonk/Research

C# snippet: remove common assembly information from AssemblyInfo.cs

A C# method that I used to remove common assembly information from a AssemblyInfo.cs.

This method was used with a small FileSystem.cs class.

 

/// <summary>
/// Remove common assembly info from the given content.
/// Like:
/// - AssemblyCompany
/// - AssemblyCopyright
/// - AssemblyVersion
/// - AssemblyFileVersion
/// </summary>
public string RemoveCommonAssemblyInfo(string content)
{
string result = string.Empty;
if (!string.IsNullOrWhiteSpace(content))
{
result = content;
var regexes = new List<Regex>();
regexes.Add(new Regex(@"\r\n\[assembly: AssemblyCompany\(""{1}.*""{1}\)]"));
regexes.Add(new Regex(@"\r\n\[assembly: AssemblyCopyright\(""{1}.*""{1}\)]"));
regexes.Add(new Regex(@"\r\n\[assembly: AssemblyVersion\(""{1}.*""{1}\)]"));
regexes.Add(new Regex(@"\r\n\[assembly: AssemblyFileVersion\(""{1}.*""{1}\)]"));
regexes.Add(new Regex(@"\r\n\r\n// Version information.*""{1}\)]", RegexOptions.Singleline));
regexes.ForEach(x =>
{
result = x.Replace(result, string.Empty);
});
}
return result;
}

namespace Research.Core.Components { using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; public class FileSystem { public async Task FindAndReplace(string path, string searchPattern, SearchOption searchOption,

Func<string, string> updateContent) { string[] files = await GetFilesAsync(path, searchPattern, searchOption); foreach (string file in files) { string content = await ReadAllTextAsync(file); content = updateContent(content); await WriteAllTextAsync(file, content); } } public async Task<string[]> GetFilesAsync(string path, string searchPattern, SearchOption searchOption) { return await Task.Factory.StartNew(() => { return Directory.GetFiles(path, searchPattern, searchOption); }); } public async Task<string> ReadAllTextAsync(string path) { string result = await Task.Factory.StartNew(() => { return File.ReadAllText(path); }); return result; } public async Task WriteAllTextAsync(string path, string content) { await Task.Factory.StartNew(() => { File.WriteAllText(path, content); }); } } }

 

To replace all common assembly info found in all AssemblyInfo.cs files in a folder, use the code:

 

var solution = new VsSolution(); var fs = new FileSystem(); Task task = fs.FindAndReplace(@"C:\Projects\Github\roelvanlisdonk\Research\Main",

"AssemblyInfo.cs", System.IO.SearchOption.AllDirectories, solution.RemoveCommonAssemblyInfo); task.Wait();

 

Code can be found at: https://github.com/roelvanlisdonk/Research

Dynamically call C# instance method by "string" name, without performance overhead.

 

There is no noticeable difference in calling a C# instance method directly or dynamically by "string" name, when you use a cached compiled LINQ expression.

 

In my test case I call a method 1.000.000 times directly and dynamically and both take 1,6s:

namespace Research.UnitTests
{
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Reflection;
[TestClass]
public class Research
{
[TestMethod]
public void TestWithTiming()
{
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
var calculator = new Calculator();           
// Get "Calculate" method.
MethodInfo methodInfo = typeof(Calculator).GetMethod("Calculate", new Type[] { typeof(int) });
// Create parameter "i" for Calculate method.
ParameterExpression param = Expression.Parameter(typeof(int), "i");           
// Create "thisParameter" needed to call instance methods.
var thisParameter = Expression.Constant(calculator);
// Create an expression for the method call "Calculate" and specify its parameter(s).
// If the method was a static method, the "thisParameter" must be removed.
MethodCallExpression methodCall = Expression.Call(thisParameter, methodInfo, param);
// Create lambda expression from MethodCallExpression.
Expression<Func<int, string>> lambda = Expression.Lambda<Func<int, string>>(
methodCall,
new ParameterExpression[] { param }
);
// Compile lambda expression to a Func<>.
Func<int, string> func = lambda.Compile();
// Dynamically call instance method by "name".
// Duration: 1620 ms (1,6s).
for (int i = 0; i < 1000000; i++)
{
string result = func(i);
}
// Direct call
// Duration: 1605ms (1,6s)
for (int i = 0; i < 1000000; i++)
{
string result = calculator.Calculate(i);
}
watch.Stop();
System.Console.WriteLine(watch.Elapsed.TotalMilliseconds);
}
}
public class Calculator
{
public string Calculate(int i)
{
string result = string.Empty;
// Execute some code.
DateTime now = DateTime.Now;
DateTime nextDay = now.AddDays(i);
result = nextDay.ToString();
return result;
}
}
}

Casting when dealing with nullable fields in a SqlDataReader

When dealing with nullable fields in a SqlDataReader, use:

int? field_a = reader["field_a"] as int?;
string field_b = reader["field_a"] as string;

instead of:

int? field_a = Convert.ToInt32(reader["field_a"]);
string field_b = Convert.ToString(reader["field_a"]);

http://stackoverflow.com/questions/2141575/how-to-efficiently-convert-cast-a-sqldatareader-field-to-its-corresponding

How to convert a string to a stream, without using encoding in C#

If you want to convert a string to a stream, without using encoding. You can use the following string extension:

 

usage

namespace PTB.Cs.Test.Research
{
using PTB.Cs.Extensions;
using System;
using System.IO;
using Xunit;
public class RliTester
{
[Fact]
public void Test()
{
string subject = "Some text.";
long expected = 10;
long actual = 0;
using (Stream stream = subject.ToStream())
{
actual = stream.Length;
}    
Assert.Equal(expected, actual);
}
}
}

Code

namespace Extensions
{
using System.IO;
public static class StringExtensions
{
/// <summary>
/// Convert a string to a stream, without using encoding.
/// Don't forget to dispose the "result" stream in the calling method.
/// </summary>
/// <param name="subject">The string to convert.</param>
/// <returns>A stream with position set to 0.</returns>
public static Stream ToStream(this string subject)
{
var result = new MemoryStream();
using(var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
{
writer.Write(subject);
writer.Flush();
stream.Position = 0;
stream.CopyTo(result);
result.Position = 0;
}
return result;
}
}
}