// https://dotnetfiddle.net/UnS2vU printfn "starting up" open System open System.Reflection let RedirectAssembly shortName (targetVersion : Version) publicKeyToken = let rec onResolveEvent = new ResolveEventHandler( fun sender evArgs -> let requestedAssembly = AssemblyName(evArgs.Name) if requestedAssembly.Name <> shortName then printfn "redirect firing for %s" requestedAssembly.Name; Unchecked.defaultof<Assembly> else printfn "Redirecting assembly load of %s ,\tloaded by %s" evArgs.Name (if evArgs.RequestingAssembly = null then "(unknown)" else evArgs.RequestingAssembly.FullName) requestedAssembly.Version <- targetVersion requestedAssembly.SetPublicKeyToken (AssemblyName(sprintf "x, PublicKeyToken=%s" publicKeyToken).GetPublicKeyToken()) requestedAssembly.CultureInfo <- System.Globalization.CultureInfo.InvariantCulture AppDomain.CurrentDomain.remove_AssemblyResolve(onResolveEvent) Assembly.Load (requestedAssembly) ) AppDomain.CurrentDomain.add_AssemblyResolve(onResolveEvent) // sample usage RedirectAssembly "FSharp.Core" (Version("4.3.1.0")) "b03f5f7f11d50a3a"
Thursday, November 13, 2014
Binding redirects at runtime
Thursday, October 9, 2014
Using a Git layer on top of TFS
With a git layer over whatever other source control system is required for the team:
- I can check-in or revert individual lines of code in a file.
- I can create a branch for local modifications that should never be checked into the team source control.
- I can create mini-commits that don't have to be logged as individual commits in the team source (for teams that prefer one large change-set)
- I can create my own personal historical record of change save-points and notes/comments
- I can select which lines of a file should go into a check-in without shelving the entire change.
How to:
Create a junction or hardlink to the folder that contains your code. This junction must not be in the tree that your tfs workspace mapping sees. If it is, Visual Studio will then, forcefully and persistently, use git as your source control and refuse to talk to TFS about your changes.
So I have all my code under
c:\development\foo
- I change working directory to
c:\projects\bar
andmklink /h /j foobar c:\development\foo
git init
- Always point visual studio at the legacy folder
c:\development\foo
. - Always point your git tool(s) at
c:\projects\bar
- profit!
Monday, June 30, 2014
Why am I so enthusiastic about F#?
Why?
- Why F#?
- I don't know about you, but more and more of the code I see in the industry is more procedural and barely qualifies as OOP, let alone SOLID OOP
- SOLID: the next step is Functional- Mark Seemann (Writer of Dependency Injection in .Net)
- Dependency Injection is not even relevant in F#
- When and where do we do our domain modelling in C#?
- Why are we deciding all these things in C# first?
- DAL (or ORM)
- To Write tests or not
- the db implementation
- the ui
- service layer
- Is it any wonder problem domain logic is found everywhere from the ui, to the ORM layer, to the persistence code?
- Domain modelling with the F# type system - Scott Wlaschin - NDC (Norway Developer's Conference)
- What can C# do that F# can't? Null Reference Exception!
- What kind of boilerplate required cruft is there in C# that you might not realize?
- What kind of boilerplate cruft might you find in OCaml compared to F#?
- In 1 year they go from 345k lines of code in OCaml and 171k in F# and now just 261k in F#, no more OCaml
- F# not permitted at work?
Thursday, April 17, 2014
T4 ApiController Generator
DbContext
is called ApplicationDbContext
in a file called ApplicationDbContext.cs
and does not account for whatever custom namespaces need to be imported for this file for your use. It is set up for DI, but lacks consumption of an IRepository
pattern or anything similar. It also expects to be in a directory below the EnvDteHelper.ttinclude
.
<#@ template debug="false" hostspecific="true" language="C#" #> <#@ assembly name="System.Core" #> <#@ assembly name="System.Data.Entity.Design" #> <#@ import namespace="System.Globalization" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Data.Entity.Design.PluralizationServices" #> <#@ import namespace="System.Collections.Generic" #> <#@ output extension=".cs" #> <# DTE Dte;#> <#@ include file="../EnvDteHelper.ttinclude"#> <# var suggestedNs=System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint"); var projects = RecurseSolutionProjects(Dte); var q= from p in projects from pi in p.ProjectItems.Cast<ProjectItem>() where pi.FileCodeModel!=null select new{p,pi,CodeElements=pi.FileCodeModel.CodeElements.Cast<CodeElement>()}; var context= q.Where(x=>x.pi.Name=="ApplicationDbContext.cs").First(); var typesToMap = Descendants(context.CodeElements,ce=>ce.Children.Cast<CodeElement>()).OfType<CodeProperty>(); var project=GetProjectContainingT4File(Dte,false); var pluralizationService = PluralizationService.CreateService(new CultureInfo("en-US")); #> using System.Linq; using System.Web.Http.OData; using Microsoft.AspNet.Identity.EntityFramework; namespace <#=suggestedNs#> { <# foreach(var tm in typesToMap){ var singular=pluralizationService.Singularize(tm.Name); var plural=pluralizationService.Pluralize(tm.Name); #> public class <#=tm.Name#>Controller : ODataController { readonly ApplicationIdentityContext _db; public <#=tm.Name#>Controller(ApplicationIdentityContext db) { _db = db; } /// GET api/<#=singular#> public IQueryable<<#=singular#>> Get() { return _db.<#=plural#>; } // GET api/<#=singular#>/5 public <#=singular#> Get(int id) { return _db.<#=plural#>.FirstOrDefault(x=>x.<#=singular#>Id==id); } } <#}#> }
Monday, April 14, 2014
VS2013 negative look-behind on a sql server database project
Find all "(?>!(CONSTRAINT|--) .*)FOREIGN KEY", Regular expressions, Subfolders, Find Results 1, Current Project: Project.Sql\Project.Sql.sqlproj, "*.SQL"
Wednesday, March 12, 2014
Vs2013 search and replace in html classes
class="\s*(\w+)\s+(\s\w+)?\s*"
replace:
class="$1$2"
It only handles up to 2 classes being inside and... I forgot to consider the possibility of having razor inside.
However, this shouldn't break that it just won't clean it.
Monday, January 20, 2014
T4 Generating Dummy Adapters
I needed to code up an adapter layer between the domain and the ui. For now, and until something more specific is needed, just expose out the public methods of the domain and their result types. There is more that the adapter is encapsulating but the premise remains.
Added my Nuget packages for code generation: T4EnvDte and T4MultiFile.
As it turns out some of the code that was useful for this operation required a modification of T4EnvDte. So I added an assembly reference to EnvDTE80. This was to finally write usable code to get all the projects in a solution recursing through solution folders.
Let's have a look at the actual T4 line by line, that uses the new code so we can see what is required for this task.<#@ template debug="True" language="C#" hostspecific="True" #> <#@ output extension=".txt"#> <#@ include file="MultipleOutputHelper.ttinclude" #> <#@ import namespace="EnvDTE"#> <# EnvDTE.DTE Dte; #> <#@ include file="EnvDteHelper.ttinclude" #> <# bool doMultiFile=true;#>
This is the basic start of the file, with 2 variables declared thus far. Dte
and doMultiFile
. One is the Visual studio com object, the other is a flag telling T4MultiFile if we want to generated multiple files or all into one.
Main file output Last run at <#=DateTime.UtcNow.ToString()#>
This simple code is all that is going to the main file output, a simple date/time string for showing when the file was last run. The usefulness is debatable in any proper source controlled scenario.
<# var manager = Manager.Create(Host, GenerationEnvironment); var targetNs=System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint"); //1 var projects= RecurseSolutionProjects(Dte); //2 var project = projects.First(p=>p.Name=="Company.Domain"); //3 var toGen= RecurseProjectItems(project).Where(pi=>pi.Name.StartsWith("Org")); //4 foreach(var p in toGen){ var className=Before(p.Name,".")+"Adapter"; //1 manager.StartNewFile(className+".generated.cs"); //2 var fcModel= p.FileCodeModel; //3 var ns=fcModel.CodeElements.OfType<EnvDTE.CodeNamespace>().First(); //4 var codeClass= ns.Children.OfType<EnvDTE.CodeClass>().First(); //5 var codeMethods= codeClass.Children.OfType<EnvDTE.CodeFunction>(); //6 #>
This is all the setup and starting the file per type looping.
targetNs
asks what the proper namespace for our current file output location would beprojects
gets all the projects in the current solution using the new EnvDte helper codeproject
the actual project name we are looking fortoGen
gets the projectItems in the sourceproject
we are interested in
className
holds the name of the new adapter class- tell the helper to start a new file for the class
fcModel
holds the FileCodeModel of the current source projectItemns
holds the namespace that wraps our target class and assumes there is only one namespace in the source filecodeClass
holds the reference to the target class and assumes there is only one class in the source filecodeMethods
gets all the methods of the source class
the meat of the individual file generation
//<#=p.Name#> <# /* 1 */ #> using <#= ns.Name#>; <# /* 2 */ #> namespace <#=targetNs#>{ <# /* 3 */ #> public class <#=className#>{ <# /* 4 */ #> <# foreach (var m in codeMethods.Where(m=>m.Access==EnvDTE.vsCMAccess.vsCMAccessPublic)){ var parms= m.Parameters.OfType<CodeParameter>(); #> <# /* 5-5.1 */ #> public <#= m.Type.AsFullName#> <#=m.Name#>(<#=parms.Any()? parms.Select(parm=>parm.Type.AsString+" "+parm.Name).Aggregate((s1,s2)=>s1+","+s2):string.Empty#>){ <# /* 5.2 */ #> return <#=Before(p.Name,".")#>.Instance.<#=m.Name#>(<#=parms.Any()?parms.Select(parm=>parm.Name).Aggregate((s1,s2)=>s1+","+s2):string.Empty#>); <# /* 5.3 */ #> } <# } #> } } <# manager.EndBlock(); } //end foreach
- adds a comment of the source file name
- adds a using clause for the source file's namespace
- adds a namespace for our new file
- adds a class to our new adapter
- loop over the public methods in the class
parms
holds a reference to the parameters of the method if any- Generate the method signature
- Generate the method return code (which includes the singleton-based
Instance
property requirement of this domain layer
- close up the single file generator and the foreach
the last little bits of code that complete the file
manager.Process(doMultiFile); #> <#+ string Before(string text, string delimiter) { return text.Substring(0,text.IndexOf(delimiter,StringComparison.CurrentCulture)); } #>
So there we see using T4EnvDte and T4MultiFile together to generate specific classes from another project in the solution. I welcome any and all questions or suggestions for improvement