tag:blogger.com,1999:blog-73861052083941073442024-03-14T02:36:30.406-04:00.Net coding discoveries, focused on reusability<a href="http://code.msdn.microsoft.com/ImaginaryDevelopment"> Msdn code repository</a>
<a href="http://github.com/ImaginaryDevelopment"> My Git repository</a>Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.comBlogger124125tag:blogger.com,1999:blog-7386105208394107344.post-70886012230151681662022-04-18T13:05:00.002-04:002022-04-18T13:05:31.385-04:00Throwing stones from a potentially glass house - proper citations and clarity in code snippets and articles<p>implicit usings in C# is a terrible idea.
It is hard enough to get people writing articles or posting sample code to include the damn using clauses, let alone references or assembly version numbers. lets make it so people are used to looking at code without any and requiring every onlooker to go find the source repo to really be able to replicate the code.
VB had it for more than 10 years and there were announcements of feature parity around 10 years ago, why does C# suddenly think it is a good idea now?
</p>
I'm repeatedly running into code examples with many major problems for reproduction:
<ul>
<li>the usings/opens are excluded from snippets (and often from the entire article/post/page)</li>
<li>the packages needed aren't mentioned</li>
<li>the repo doesn't exist or isn't linked</li>
<li>the references don't specify a version</li>
<li>the article/snippet has no date/time visibility</li>
<li>implicit usings are turned on (making even a full repo far less clear unless you download it)</li>
<li><p>there is some implicit context that makes it unclear the true target</p>
<ul>
<li>turning in implicit usings is a special kind of evil for example/sample repos.</li>
<li>is this how you write a framework service or a net core service?</li>
<li>is this how you use something magical built-in to hangfire to install your hangire app as a service, or is there a dependency some hangfire package depends on that is doing it?</li>
</ul>
</li>
</ul>
<p>I'm having a really tough time finding an updated article on setting up an app that goes 1 of 4 ways:</p>
<ul>
<li>Run as a console spitting out simple information on the command line arguments</li>
<li>Run as a console executing a specific potentially long running job</li>
<li>Run as a console app starting up a web server with a dashboard using Hangfire Asp.Net Core</li>
<li>Run as a console app to install some part of itself as a windows service (including the Hangfire Asp.Net Core dashboard).</li>
</ul>
I understand that asking for a simple article or post, up to date, that shows how to do 4 things at once is a lot to ask for.
However, the above major problems for reproducing little parts individually combine to form serious frustration.
</p>Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-66644026776545128082021-07-09T16:59:00.002-04:002021-07-09T16:59:22.047-04:00TypeScript: typeof Foo.BarIn typescript if you have 2 complex objects and you want to use an instance of one as the other, it might just work. In the case of generic type arguments at least, prepare for potentially really difficult to read tooltip parsing. Or say you are importing a json file, the type it is importing as may be much more generic than you desired. This is especially true if you have any properties that are only allowed to be specific strings instead of any string.
Given the limited context here from the typescript package <pre>@types/topojson-specification</pre>
<pre>
type Positions = number[];
type Arc = Positions[];
interface Objects<P> { [ key: string]: GeometryObject<P>}
interface Topology<T extends Objects<GeoJSON.GeoJsonProperties> = Objects<GeoJsonProperties>>{
type:"Topology",
arcs:Arc[]
}
import * as globeData from './globedata.json';
</pre>
We can see the types are quite a mental mess to think about. Using Type alias proxies can work wonders.
<pre>
type TopKey = keyof Topology
type Top = {
[K in TopKey]: Topology[K]
}
</pre>
Now with these defined (that we can remove later once we figure out what all is going on) we can proceed with property checks.
<pre>
// type is a special case, the types match, but they must be an allowed string, so we do this workaround
var gdt:Top['type'] | undefined = globeData.type == "Topology"? globeData.type : undefined;
var gda:Top['arcs'] = globeData.arcs;
</pre>
We see here that we can declare a type in the context of another type's property. This greatly slims the surface of the tooltip explaining if something is wrong on that property and what it is. Repeat the breakdown process as necessary.
Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-24679686958149768922018-10-11T10:26:00.001-04:002018-10-11T10:28:32.814-04:00Making the Dynamic Operator More ... Dynamic<p>So in F# you can (and have to if you want it) make a <a href="http://codebetter.com/matthewpodwysocki/2010/02/05/using-and-abusing-the-f-dynamic-lookup-operator/">dynamic operator implementation</a>.
</p>
Like so:
<br />
<pre class="brush: fsharp"> let (?) (this : 'Source) (prop : string) : 'Result =
let t : Type = this.GetType()
let p : PropertyInfo = t.GetProperty(prop)
if isNull p then failwithf "could not find prop %s" prop
p.GetValue(this, null) :?> 'Result
</pre>
Or dynamic setters even:
<br />
<pre class="brush: fsharp"> let (?<-) (this: 'Source) (prop:string) (value: 'Value) =
this.GetType().GetProperty(prop).SetValue(this,value,null)
</pre>
Which are accessed by name not by string (even though the effect is like a magic string):
<br />
<pre class="brush: fsharp"> type Test(code:string) =
member val Code = code with get,set
let nakedTest : Test = Test("hello")
let x : obj = box nakedTest
let value : string = x?Code
printfn "I found the property and retrieved the value, it was %s" value
</pre>
<p>
Or the setter like: <code>x?Code <- "dynamic"</code></p>
<p>
Now what happens if I want part of that implementation to be variable, I want to pass different combinations of <code>BindingFlag</code> bits, and the keep the rest of the implementation the same?
</p>
<p>Perhaps I want to access static or private properties, with different <code>(?)</code> implementations.
</p>
<p>Well, it's possible!
</p>
<br />
<pre class="brush: fsharp"> let makePropAccess (flags:BindingFlags) =
let (?) (this : 'Source) (prop : string) : 'Result =
let t = this.GetType()
if isNull t then failwithf "bad getType"
let p = t.GetProperty(prop,flags)
if isNull p then failwithf"could not find prop %s" prop
p.GetValue(this, null) :?> 'Result
(?)
let (?) = makePropAccess (BindingFlags.Public ||| BindingFlags.Instance)
let x = Test("Code")
x?Code
</pre>
full <a href="https://www.linqpad.net/">LINQPad</a> code at <a href="https://github.com/ImaginaryDevelopment/LinqPad/blob/master/LINQPad%20Queries/Reflection/F%23%20dynamic%20operator%20implementation.linq">Github.com/ImaginaryDevelopment/LinqPad/LINQPad Queries/Reflection/F# dynamic operator implementation.linq</a>
<p>
I wonder how (or if) you could even access this from C#
</p>Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-9529169261315531762017-05-09T09:44:00.002-04:002017-05-09T09:53:19.244-04:00Where has all the browser javascripting gone?I guess it's time to admit I'm behind the times. I have little experience with many of the things I'm hearing the web-community talk about like it's something everyone is already using. Webpack, AMD, UMD, Flow, JsDoc, EsLint, Typings. I have some experience with node, but 99% of the code I write/have written is for the browser and blissfully ignorant of server-side JavaScript.<br />
<br />
Individual parts of this I imagine I'd have no problem learning. It seems each one of them takes for granted that you already know or are willing to go learn the other system, and then not discuss how you would (as a beginner or intermediate to both systems) integrate them. Also, forget how you would use them on a path that doesn't perfectly match whatever path they choose for you to learn on, especially for any advanced usage.<br />
<h3>
Motivation</h3>
VsCode has a combination platter of lovely features/extensions to enable things like intellisense, auto-completion, and design-time error detection. However, what it seems I'm running into is they rely on using (non-AMD) module systems. I'm having code types/method thrashing in a live personal project (also a similar issue in a non-live non-personal project).<br />
<br />
<h3>
Solutions (from my Understanding/Attempts)</h3>
<ul>
<li>JsDoc</li>
<ul>
<li>where it seems I have to redefine the same <code>@typedef MyClass</code>
in each and every file rather than somehow referencing the source of an object's shape.</li>
<li>There doesn't appear to be anyway to get the annotations out of every file to centralize (which is of dubious benefit in many cases, unless it causes you to have to copy-paste your shape comments around to multiple files.</li>
<li>Some of it (no idea how much) isn't supported by VsCode</li>
<li><a href="http://usejsdoc.org/tags-callback.html">This is the documentation for a callback</a> </li>
<ul>
<li>notice there is no example that doesn't involve using prototypes or resorting to classes?</li>
<li>I can find nothing that talks about a way to reference code in other files</li>
</ul>
</ul>
<li>Flow</li>
<ul>
<li>all the documentation appears to say it always needs to be babel-ized to be in browser</li>
<li>running it, it complains like crazy about react </li>
<ul>
<li>I added it as a package, most likely to try to get flow or one of these other systems working</li>
<li>should the node react module be in the libs section of <code>.flowconfig</code> ?</li>
<li>when not in the <code>libs</code> section, and <code>.*/node_modules/.*</code> is in the ignore, it doesn't seem to care, still reports errors in <code>node_modules/React/lib/ReactChildren.js</code></li>
</ul>
<li>Whereas, I can use my jsx directly in browser via babel, I don't think I can do this with adding in flow-typing.</li>
<ul>
<li>which means I don't have to figure out or take on learning Watcherify or something else to auto-magically detect changes and recompile on the fly.</li>
</ul>
<li>aims at being an amazingly safe type system (vs TypeScript looking to strike a balance between safety and productivity) - <a href="https://discuss.reactjs.org/t/if-typescript-is-so-great-how-come-all-notable-reactjs-projects-use-babel/4887">see here</a></li>
</ul>
<li>TypeScript </li>
<ul>
<li>I have no idea where to even start into this thing. It seems like a beast of changes to tackle to migrate anything into try it.</li>
<ul>
<li>The examples demonstrate how to use either node or Visual Studio. I'm using (and loving) VsCode for all my JavaScript and some of my F#.</li>
</ul>
<li>I suspect, were I to download the typings for react, my project (no TypeScript in it) would suddenly be able to design-time detect all kinds of problems.</li>
<li>I have no idea if I could write my own typings files for my non-JavaScript somewhere that would help anything. Furthermore, when I go look at something called DefinitelyTyped it looks like you have to do a lot of installations, and then elsewhere on the web, it's semi-deprecated since there is (or will be?) auto-discovery</li>
</ul>
<li>React PropTypes</li>
<ul>
<li>Everywhere it says move to an NPM package. no indications what to do if you don't develop your JavaScript in node. </li>
<li>No sign, upon searching, of a CDN or other browser-capable file to help find method/object shape/type errors (which wouldn't happen until run time anyhow)</li>
</ul>
<li>Fable - oh my beloved F# - produces so-so JavaScript, always bundled up in webpack that also doesn't seem to appreciate not having everything bundled into their own module system.</li>
<li>VsCode - oh so lovingly lightweight</li>
<ul>
<li>doesn't support an unknown percentage of the JsDoc syntax</li>
<li>allows you to add <code>///<reference path="./x.extension"></code> but <a href="http://stackoverflow.com/questions/30136319/what-is-reference-path-in-vscode">apparently</a> only supports typescript files</li>
</ul>
</ul>
<div>
<br /></div>
<h3>
This is at least partially a rant and venting</h3>
<div>
I have very little idea of what I'm talking about, having been stuck in a Wpf project for almost 2 years now. This is my way of categorizing and venting while trying to pick the flag back up. Also, a recounting of my experiences having been a cutting edge resource on near every coding-topic on every team I've ever been to now, feeling entirely lost trying to get a few features that seem almost entirely built-in to my IDE of choice for JavaScript. These are features I never even had a taste of before, and now it seems like I'm in instant withdrawals as soon as what I consider to be a near-critical feature is missing. (the features don't work if you aren't developing in node, aren't using webpack, are using AMD, etc..)</div>
<h3>
The topic</h3>
<div>
Very little of what I find that people recommend for usage explicitly appears to support JavaScript in browser. Node seems to have come up and choked out search results or the community or both from talking about in-browser or code that doesn't take a dependency in the entire codebase on a module system.</div>
Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-62573891063406126492017-01-05T16:37:00.000-05:002019-04-09T08:46:26.450-04:00Run build tasks and targets via F# in VsCode<link href="https://assets-cdn.github.com/assets/gist-embed-e108cbee1bd1bd4f0b487c8a1c295c19a1d2b8a979d848999a34221642de6132.css" rel="stylesheet"></link>
For developing the client side of a web app, I've switched from the ultra heavy Visual Studio 2015 to Visual Studio Code. It's written in JavaScript and runs pretty smoothly. It has hooks for kicking off a build, and tests.<br />
<br />
I hooked up the build kick off to FAKE (an F# build engine) tasks (Via an <code>.ionide</code> file at the project root).<br />
<!-- put ionide file here -->
<!-- finish ionide file -->
<br />
so I just hit Ctrl+Shift+P -> Fake -> and pick the build.fsx target I want to run from the menu (via keyboard, mouse if desired)<br />
<br />
Things I have hooked up to be kicked off from Fake<br />
<br />
<br />
<ul>
<li>Setup/install node (Via installing Chocolatey first)</li>
<ul>
<li>install npm packages</li>
</ul>
<li>Transpile Coffeescripts</li>
<li>Transpile Babel (React preset)</li>
<li>Transpile Sass</li>
<li>List packages installed to this project</li>
<li>Kill any running instances of the app</li>
<li>Clean the app</li>
<li>MsBuild the app</li>
<li>Kick off Mocha tests</li>
</ul>
<br />
<br />
<!-- put package.json file here -->
<!-- finish package.json file -->
<br />
<!-- put build.fsx file here -->
<!-- finish build.fsx file -->
<div id="gist43513509" class="gist">
<div class="gist-file">
<div class="gist-data">
<div class="js-gist-file-update-container js-task-list-container file-box">
<div id="file-ionide" class="file">
<div itemprop="text" class="blob-wrapper data type-text">
<table class="highlight tab-size js-file-line-container" data-tab-size="8">
<tr>
<td id="file-ionide-L1" class="blob-num js-line-number" data-line-number="1"></td>
<td id="file-ionide-LC1" class="blob-code blob-code-inner js-file-line">[Fake]</td>
</tr>
<tr>
<td id="file-ionide-L2" class="blob-num js-line-number" data-line-number="2"></td>
<td id="file-ionide-LC2" class="blob-code blob-code-inner js-file-line">linuxPrefix = "mono"</td>
</tr>
<tr>
<td id="file-ionide-L3" class="blob-num js-line-number" data-line-number="3"></td>
<td id="file-ionide-LC3" class="blob-code blob-code-inner js-file-line">command = "build.cmd"</td>
</tr>
<tr>
<td id="file-ionide-L4" class="blob-num js-line-number" data-line-number="4"></td>
<td id="file-ionide-LC4" class="blob-code blob-code-inner js-file-line">build = "build.fsx"</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="gist-meta">
<a href="https://gist.github.com/ImaginaryDevelopment/4a735576a7f7349b4bdf7ba345e5bbb7/raw/3bffd8678f88caf955bfa90362f816f435538522/.ionide" style="float:right">view raw</a>
<a href="https://gist.github.com/ImaginaryDevelopment/4a735576a7f7349b4bdf7ba345e5bbb7#file-ionide">.ionide</a>
hosted with ❤ by <a href="https://github.com">GitHub</a>
</div>
</div>
<div class="gist-file">
<div class="gist-data">
<div class="js-gist-file-update-container js-task-list-container file-box">
<div id="file-build-fsx" class="file">
<code>// include Fake lib
#r @"../packages/FAKE/tools/FakeLib.dll"
open System
open System.Diagnostics
open System.IO
open Fake
// Properties
let buildDir = "./bin/"
let targetScript =
let linqPadQueriesFolder = @"C:\projects\LinqPad\LinqPad\LINQPad Queries\"
Path.Combine(linqPadQueriesFolder,@"WIP\runPmWeb.linq")
let lprunPath = @"C:\ProgramData\LINQPad\Updates50\510\lprun.exe"
let configLpRun (pi: ProcessStartInfo) =
trace (sprintf "Target script:%s" targetScript)
pi.FileName <- lprunPath
pi.Arguments <- sprintf "\"%s\"" targetScript
// Helpers
let flip f y x = f x y
let warn msg = trace (sprintf "WARNING: %s" msg)
type System.String with
static member Delimit delimiter (items:string seq) =
String.Join(delimiter,items |> Array.ofSeq)
module Sec =
open System.Security.Principal
let getIsAdmin() =
WindowsIdentity.GetCurrent()
|> WindowsPrincipal
|> fun wp -> wp.IsInRole(WindowsBuiltInRole.Administrator)
let requireAdmin () =
let runningAsAdmin = getIsAdmin()
if not runningAsAdmin then
failwithf "Requested feature is not known to work without administrator permissions"
module Proc =
//let execCmd prog args timeout =
let findCmd cmd =
let processResult =
ExecProcessAndReturnMessages (fun psi ->
psi.FileName <- "where"
psi.Arguments <- quoteIfNeeded cmd
) (TimeSpan.FromSeconds 2.)
if processResult.OK then
// require the result not be a directory
let cmdPath =
processResult.Messages
|> Seq.filter (Directory.Exists >> not)
|> Seq.filter (File.Exists)
|> Seq.filter (fun x -> x.EndsWith ".bat" || x.EndsWith ".exe" || x.EndsWith ".cmd")
|> Seq.tryHead
if processResult.Messages.Count > 1 then
warn (sprintf "found multiple items matching '%s'" cmd)
trace (processResult.Messages |> String.Delimit ";")
match cmdPath with
| Some path ->
trace (sprintf "found %s at %s" cmd path)
Some path
| None ->
warn "where didn't return a valid file"
None
else None
let runWithOutput cmd args timeOut =
let cmd =
// consider: what if the cmd is in the current dir? where may find one elsewhere first?
if Path.IsPathRooted cmd then
cmd
else
match findCmd cmd with
| Some x -> x
| None ->
warn (sprintf "findCmd didn't find %s" cmd)
cmd
let result =
ExecProcessAndReturnMessages (fun f ->
//ExecProcessRedirected (fun f ->
//f.FileName <- @"gulp"
//f. Arguments <- "sass"
// why did 'where' with no full path work, but this fails?
f.FileName <- cmd
f.Arguments <- args
) (TimeSpan.FromMinutes 1.0)
result,cmd
let showInExplorer path =
Process.Start("explorer.exe",sprintf "/select, \"%s\"" path)
// wrapper for fake built-in in case we want the entire process results, not just the exitcode
let runElevated cmd args timeOut =
let tempFilePath = System.IO.Path.GetTempFileName()
// could also redirect error stream with 2> tempErrorFilePath
// see also http://www.robvanderwoude.com/battech_redirection.php
let resultCode = ExecProcessElevated "cmd" (sprintf "/c %s %s > %s" cmd args tempFilePath) timeOut
trace "reading output results of runElevated"
let outputResults = File.ReadAllLines tempFilePath
File.Delete tempFilePath
let processResult = ProcessResult.New resultCode (ResizeArray<_> outputResults) (ResizeArray<_>())
(String.Delimit "\r\n" outputResults)
|> trace
processResult
type FindOrInstallResult =
|Found
|InstalledThenFound
let findOrInstall cmd fInstall =
match findCmd cmd with
| Some x -> Some (x,Found)
| None ->
fInstall()
findCmd cmd
|> Option.map (fun x -> (x,InstalledThenFound))
module Node =
let npmPath = lazy(Proc.findCmd "npm")
// assumes the output is unimportant, just the result code
let npmInstall args =
let resultCode =
let filename, useShell =
match npmPath.Value with
| Some x -> x, false
// can't capture output with true
| None -> "npm", true
trace (sprintf "npm filename is %s" filename)
ExecProcess (fun psi ->
psi.FileName <- filename
psi.Arguments <- "install"
psi.UseShellExecute <- useShell
) (TimeSpan.FromMinutes 1.)
resultCode
// Targets
Target "SetupNode" (fun _ ->
// goal: install and setup everything required for any node dependencies this project has
// including nodejs, gulp, node-sass
// install Choco
let chocoPath =
let fInstall () =
let resultCode =
ExecProcessElevated
"@powershell"
"""-NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin" """
(TimeSpan.FromMinutes 3.)
resultCode
|> sprintf "choco install script returned %i"
|> trace
if resultCode <> 0 then
failwithf "Task failed"
// choco is installled, we think
// probably won't work if it was just installed, the %path% variable given to/used by a process is immutable
match Proc.findOrInstall "choco" fInstall with
//| Some (x,Proc.FindOrInstallResult.Found) -> x
| Some (x,_) -> x
| None -> failwithf "choco was installed, in order for choco to be found or used, this has process has to be restarted"
// choco install nodeJs
let nodePath =
let fInstall () =
let results = Proc.runElevated "choco" "install nodejs -y" (TimeSpan.FromSeconds 3.)
trace (sprintf "%A" results)
match Proc.findOrInstall "node" fInstall with
| Some (x,_) -> x
| None -> failwithf "nodejs was installed, in order for node to be found or used, this process has to be restarted"
// node should have installed npm
// npm
let npmPath = Proc.findCmd "npm"
// install all packages that packages.json says this project needs
let resultCode =
let filename, useShell =
match npmPath with
| Some x -> x, false
// can't capture output with true
| None -> "npm", true
trace (sprintf "npm filename is %s" filename)
ExecProcess (fun psi ->
psi.FileName <- filename
psi.Arguments <- "install"
psi.UseShellExecute <- useShell
) (TimeSpan.FromMinutes 1.)
()
)
// run node tests and whatever else
Target "Test" (fun _ ->
let result, _ = Proc.runWithOutput "npm" "test" (TimeSpan.FromSeconds 4.)
result.Messages
|> Seq.iter (printfn "test-msg:%s")
if result.ExitCode <> 0 then
result.Errors
|> Seq.iter(printfn "test-err:%s")
failwithf "Task failed: %i" result.ExitCode
)
Target "Coffee" (fun _ ->
let coffees = [
"test/test.coffee"
]
let compileCoffee relPath =
let result,_ = Proc.runWithOutput "node" (sprintf "node_modules/coffee-script/bin/coffee -b -m --no-header -c %s" relPath) (TimeSpan.FromSeconds 2.)
if result.ExitCode <> 0 then
failwithf "Task failed: %A" result
coffees
|> Seq.iter compileCoffee
)
Target "Babel" (fun _ ->
// run jsx compilation
let babels = [
"Scripts/pm-era.jsx"
"Scripts/pm-era-remitdetail.jsx"
]
let babel relPath =
let targetPath =
let fullPath = Path.GetFullPath relPath
Path.Combine(fullPath |> Path.GetDirectoryName, fullPath |> Path.GetFileNameWithoutExtension |> flip (+) ".react.js")
let result,_ = Proc.runWithOutput "node" (sprintf "node_modules/babel-cli/bin/babel %s -o %s -s --presets react" relPath targetPath) (TimeSpan.FromSeconds 2.)
if result.ExitCode <> 0 then
result.Messages
|> Seq.iter (printfn "babel-msg:%s")
result.Errors
|> Seq.iter(printfn "babel-err:%s")
failwithf "Task failed: %i" result.ExitCode
else
result.Messages
|> Seq.iter (printfn "babel-msg:%s")
babels
|> Seq.iter babel
)
//node node_modules\coffee-script\bin\coffee -b -c test/test.coffee
Target "Clean" (fun _ ->
CleanDir buildDir
let files = Directory.GetFiles buildDir |> Seq.length
let directories = Directory.GetDirectories buildDir |> Seq.length
printfn "cleaned directory had %i item(s) remaining after clean" (files + directories)
)
Target "BuildApp" (fun _ ->
let output =
if isNullOrEmpty buildDir then ""
else
buildDir
|> FullName
|> trimSeparator
let setParams defaults =
{ defaults with
MSBuildParams.Targets= ["Build"]
//Verbosity = Some(MSBuildVerbosity.Diagnostic)
Properties =
[
"Configuration", "Debug"
"Platform", "AnyCPU"
"DebugSymbols", "True"
"OutputPath", buildDir
"SolutionDir", ".." |> FullName
]
}
//https://github.com/fsharp/FAKE/blob/master/src/app/FakeLib/MSBuildHelper.fs
build setParams "Pm.Web.fsproj"
if isNotNullOrEmpty output then !!(buildDir @@ "/**/*.*") |> Seq.toList
else []
|> Log "AppBuild-Output: "
)
Target "StartApp" (fun _ ->
let proc = new Process()
configLpRun proc.StartInfo
//proc.StartInfo.UseShellExecute <- false
// not using ProcessHelper.Start we don't want fake killing it
proc.Start() |> ignore
trace (sprintf "started app with pid:%i" proc.Id)
)
Target "Fire" (fun _ ->
ProcessHelper.fireAndForget configLpRun
)
Target "Run" (fun _ ->
//C:\projects\LinqPad\LinqPad\LINQPad Queries\WIP\runPmWeb.linq
//https://github.com/fsharp/FAKE/blob/master/src/app/FakeLib/ProcessHelper.fs
asyncShellExec {
Program = lprunPath
WorkingDirectory = null
CommandLine = sprintf "\"%s\"" targetScript
Args = list.Empty
}
|> Async.RunSynchronously
//Shell.Exec (,@"C:\projects\LinqPad\LinqPad\LINQPad Queries\",@"C:\projects\LinqPad\LinqPad\LINQPad Queries\WIP\")
|> sprintf "script returned %i"
|> trace
)
Target "Stop" (fun _ ->
killProcess "lprun"
)
Target "NodeTasks" (fun _ ->
trace "Node Tasks completed"
)
Target "Sass" (fun _ ->
let result,_ = Proc.runWithOutput "node-sass" "content/site.scss content/site.css" (TimeSpan.FromSeconds 4.)
trace "finished attempting sass"
printfn "sass:%A" result
//result.Messages
//|> Seq.iter (printfn "sass:%A")
if result.ExitCode <> 0 then
failwithf "Task failed"
)
Target "NodeList" (fun _ ->
let result = Proc.runWithOutput "npm" "list --depth=0" (TimeSpan.FromSeconds 2.0)
printfn "npm list -g --depth=0:\r\n%A" result
)
Target "Default" (fun _ ->
trace "Hello World from FAKE"
)
Target "AfterAll" (fun _ ->
trace (sprintf "%A" DateTime.Now)
)
// Dependencies
// this should be NodeTasks depends on Sass, Coffee, and jsx compilation
// ==> "Coffee"
"Sass"
==> "NodeTasks"
"Coffee"
==> "NodeTasks"
"Stop" ==> "Run"
For "NodeTasks" ["Sass";"Coffee";"Babel";"Test"]
// "Stop" ==> "Fire"
For "Fire" [ "Stop" ]
For "StartApp" [ "Stop" ]
For "StartApp" [ "Stop" ]
For "Test" [ "Coffee" ]
// default doesn't include starting the app
"Stop"
==> "Clean"
==> "BuildApp"
==> "StartApp"
==> "Default"
==> "AfterAll"
// make sure nodetasks happens before buildapp if nodetasks is executed
"NodeTasks" ?=> "BuildApp"
"Coffee" ?=> "Test"
RunTargetOrDefault "Default"</code>
</div>
</div>
</div>
<div class="gist-meta">
<a href="https://gist.github.com/ImaginaryDevelopment/4a735576a7f7349b4bdf7ba345e5bbb7/raw/3bffd8678f88caf955bfa90362f816f435538522/build.fsx" style="float:right">view raw</a>
<a href="https://gist.github.com/ImaginaryDevelopment/4a735576a7f7349b4bdf7ba345e5bbb7#file-build-fsx">build.fsx</a>
hosted with ❤ by <a href="https://github.com">GitHub</a>
</div>
</div>
<div class="gist-file">
<div class="gist-data">
<div class="js-gist-file-update-container js-task-list-container file-box">
<div id="file-package-json" class="file">
<div itemprop="text" class="blob-wrapper data type-json">
<table class="highlight tab-size js-file-line-container" data-tab-size="8">
<tr>
<td id="file-package-json-L1" class="blob-num js-line-number" data-line-number="1"></td>
<td id="file-package-json-LC1" class="blob-code blob-code-inner js-file-line">{</td>
</tr>
<tr>
<td id="file-package-json-L2" class="blob-num js-line-number" data-line-number="2"></td>
<td id="file-package-json-LC2" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>name<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>pm.web<span class="pl-pds">"</span></span>,</td>
</tr>
<tr>
<td id="file-package-json-L3" class="blob-num js-line-number" data-line-number="3"></td>
<td id="file-package-json-LC3" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>version<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>1.0.0<span class="pl-pds">"</span></span>,</td>
</tr>
<tr>
<td id="file-package-json-L4" class="blob-num js-line-number" data-line-number="4"></td>
<td id="file-package-json-LC4" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>description<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>Registry additions have been made in order to provide you the best web development experience. See http://bloggemdano.blogspot.com/2013/11/adding-new-items-to-pure-f-aspnet.html for more information.<span class="pl-pds">"</span></span>,</td>
</tr>
<tr>
<td id="file-package-json-L5" class="blob-num js-line-number" data-line-number="5"></td>
<td id="file-package-json-LC5" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>main<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>index.js<span class="pl-pds">"</span></span>,</td>
</tr>
<tr>
<td id="file-package-json-L6" class="blob-num js-line-number" data-line-number="6"></td>
<td id="file-package-json-LC6" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>dependencies<span class="pl-pds">"</span></span>: {</td>
</tr>
<tr>
<td id="file-package-json-L7" class="blob-num js-line-number" data-line-number="7"></td>
<td id="file-package-json-LC7" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>requirejs<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>^2.3.2<span class="pl-pds">"</span></span></td>
</tr>
<tr>
<td id="file-package-json-L8" class="blob-num js-line-number" data-line-number="8"></td>
<td id="file-package-json-LC8" class="blob-code blob-code-inner js-file-line"> },</td>
</tr>
<tr>
<td id="file-package-json-L9" class="blob-num js-line-number" data-line-number="9"></td>
<td id="file-package-json-LC9" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>devDependencies<span class="pl-pds">"</span></span>: {</td>
</tr>
<tr>
<td id="file-package-json-L10" class="blob-num js-line-number" data-line-number="10"></td>
<td id="file-package-json-LC10" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>babel-cli<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>^6.18.0<span class="pl-pds">"</span></span>,</td>
</tr>
<tr>
<td id="file-package-json-L11" class="blob-num js-line-number" data-line-number="11"></td>
<td id="file-package-json-LC11" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>babel-preset-react<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>^6.16.0<span class="pl-pds">"</span></span>,</td>
</tr>
<tr>
<td id="file-package-json-L12" class="blob-num js-line-number" data-line-number="12"></td>
<td id="file-package-json-LC12" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>coffee-script<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>github:jashkenas/coffeescript<span class="pl-pds">"</span></span>,</td>
</tr>
<tr>
<td id="file-package-json-L13" class="blob-num js-line-number" data-line-number="13"></td>
<td id="file-package-json-LC13" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>mocha<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>^3.2.0<span class="pl-pds">"</span></span></td>
</tr>
<tr>
<td id="file-package-json-L14" class="blob-num js-line-number" data-line-number="14"></td>
<td id="file-package-json-LC14" class="blob-code blob-code-inner js-file-line"> },</td>
</tr>
<tr>
<td id="file-package-json-L15" class="blob-num js-line-number" data-line-number="15"></td>
<td id="file-package-json-LC15" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>scripts<span class="pl-pds">"</span></span>: {</td>
</tr>
<tr>
<td id="file-package-json-L16" class="blob-num js-line-number" data-line-number="16"></td>
<td id="file-package-json-LC16" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>wastest<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>echo <span class="pl-cce">\"</span>Error: no test specified<span class="pl-cce">\"</span> && exit 1<span class="pl-pds">"</span></span>,</td>
</tr>
<tr>
<td id="file-package-json-L17" class="blob-num js-line-number" data-line-number="17"></td>
<td id="file-package-json-LC17" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>test<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>mocha<span class="pl-pds">"</span></span></td>
</tr>
<tr>
<td id="file-package-json-L18" class="blob-num js-line-number" data-line-number="18"></td>
<td id="file-package-json-LC18" class="blob-code blob-code-inner js-file-line"> },</td>
</tr>
<tr>
<td id="file-package-json-L19" class="blob-num js-line-number" data-line-number="19"></td>
<td id="file-package-json-LC19" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>author<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span><span class="pl-pds">"</span></span>,</td>
</tr>
<tr>
<td id="file-package-json-L20" class="blob-num js-line-number" data-line-number="20"></td>
<td id="file-package-json-LC20" class="blob-code blob-code-inner js-file-line"> <span class="pl-s"><span class="pl-pds">"</span>license<span class="pl-pds">"</span></span>: <span class="pl-s"><span class="pl-pds">"</span>ISC<span class="pl-pds">"</span></span></td>
</tr>
<tr>
<td id="file-package-json-L21" class="blob-num js-line-number" data-line-number="21"></td>
<td id="file-package-json-LC21" class="blob-code blob-code-inner js-file-line">}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="gist-meta">
<a href="https://gist.github.com/ImaginaryDevelopment/4a735576a7f7349b4bdf7ba345e5bbb7/raw/3bffd8678f88caf955bfa90362f816f435538522/package.json" style="float:right">view raw</a>
<a href="https://gist.github.com/ImaginaryDevelopment/4a735576a7f7349b4bdf7ba345e5bbb7#file-package-json">package.json</a>
hosted with ❤ by <a href="https://github.com">GitHub</a>
</div>
</div>
</div>
Version information:<br />
<br />
<ul>
<li><a href="http://code.visualstudio.com/">Visual Studio Code</a> 1.8.1</li>
<li><a href="http://fsharp.github.io/FAKE/">FAKE</a> 4.46.1</li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=Ionide.Ionide-FAKE">Ionide-FAKE</a> 1.2.3</li>
<li>Ionide-Paket 1.4.0 (just for VsCode to nuget fetch FAKE so far)</li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=TwentyChung.jsx">jsx</a> 0.0.1</li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=vscodevim.vim">Vim</a> 0.4.10</li>
</ul>
Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-37149937073652162332016-06-17T10:20:00.003-04:002016-06-17T10:20:37.688-04:00Compare before every check-inIf you don't compare every file before you check-in, you are more likely to check something in that was only needed for testing the change. (or something that was you trying to figure out how to solve the problem, and is now dead code)
Additionally, If you do compare every file, you get to see what a diff program (and another human would see) when trying to compare the before to after. You can then optimize the code so that a diff is far more readable (if only for that check-in, then check-in again if you desire a specific order for how the file should read).
Version Control Systems that have easy staging of parts of a file instead of all changes can make this even easier.
Since, you may want to keep your local changes for testing, without checking them in for more testing or more feature additions. (or even permanent testing additions for your local workspace)
Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-42723995682058383682016-05-11T11:20:00.000-04:002016-05-11T11:20:40.963-04:00Vim Regex C# to F#This is really trying my patience. Converting a C# class to F# using regex. Not the cleanest or sanest approach, but really with the patterns in the code I'm targeting it shouldn't be a very big deal<br />
I welcome any and all input or suggestions besides do it with a real code parser/generator (I've done lots of work in this area and realize the benefits, but I'm working on something that isn't as robust but gets the job done) <br />
<br />
vim <code>:set magic?</code> returns:<code>magic</code><br />
I started out with the following code<br />
<br />
<br />
<pre class="brush: csharp">namespace PracticeManagement.Foundation.DataModels
{
public class BillingStageDataAccess
{
public static IEnumerable<BillingStageDataModel> LoadRejectedGrid(string payerId, Connector cn)
{
return Pm.Dal.BillingStages.loadRejectedGrid(payerId, SessionVariables.Instance.CurrentFacility.FacilityID, cn)
.Select(r => new BillingStageDataModel(r));
}
public static IEnumerable<BillingStageDataModel> LoadPendingGrid(string payerId, Connector cn)
{
return Pm.Dal.BillingStages.loadPendingGrid(payerId, SessionVariables.Instance.CurrentFacility.FacilityID, cn)
.Select(r=> new BillingStageDataModel(r));
}
public static IEnumerable<BillingStageDataModel> LoadReviewGrid(string payerId, Connector cn)
{
return Pm.Dal.BillingStages.loadReviewGrid(payerId, SessionVariables.Instance.CurrentFacility.FacilityID, cn)
.Select(r => new BillingStageDataModel(r));
}
public static IEnumerable<BillingStageDataModel> LoadPatientBilling(bool? includeThirdParty, Connector cn)
{
return Pm.Dal.BillingStages.loadPatientBilling(SessionVariables.Instance.CurrentFacility.FacilityID, includeThirdParty, cn).Select(bsm => new BillingStageDataModel(new Pm.Schema.BillingStageRecord(bsm.Age.ToString(), 0m, 0m, bsm.DOS ?? DateTime.Now, null, 0, bsm.FirstStatement ?? DateTime.Now, bsm.NofStatements ?? 0, bsm.PatientName, bsm.ThirdPartyPayerId ?? 0, null, null, bsm.Appointmentid ?? 0, null, null)) { PrimaryGuarantorType= bsm.AppointmentPrimaryGuarantorType });
}
public static IEnumerable<BillingStageDataModel> LoadCompleted(Connector cn)
{
return Pm.Dal.BillingStages.loadCompleted(SessionVariables.Instance.CurrentFacility.FacilityID, cn).Select(bsm => new BillingStageDataModel(new Pm.Schema.BillingStageRecord(null,bsm.ClaimAmount ?? 0, 0, bsm.DOS ?? DateTime.Now, bsm.Claimdate ?? DateTime.Now, bsm.Daysfilled ?? 0, null, 0,bsm.PatientName, 0, bsm.Claimstatus, null, bsm.Appointmentid ?? 0,null,null)));
}
public static IEnumerable<BillingStageDataModel> LoadPrepGrid(int facilityId, Connector cn)
{
return Pm.Dal.BillingStages.loadPrepClaimGrid(facilityId, cn).Select(r => new BillingStageDataModel(r));
}
}
}
</pre>
<h3>
Stage 1</h3>
<ol>
<li>use <code>dd</code> to delete the namespace and class lines</li>
<li>add <code>module name =</code></li>
<li>highlight that block pasted into an F# file.</li>
<li>then apply the regex to remove lines that only open or close blocks <code>:'<,'>s:^\s*[{}]\s*$:</code></li>
</ol>
<b>regex irritation</b>
<br />
<ul>
<li><code>*</code> doesn't need an escape but in other places <code>+</code> does</li>
<li>that leaves a bunch of empty lines unfortunately</li>
</ul>
<h3>
Stage 2</h3>
<ol>
<li>re-highlight with <code>gv</code></li>
<li>then eliminate the <code>public static T</code> with
<code>:'<,'>s:public static [\w<>]\+:let</code></li>
</ol>
<b> regex irritation</b><br />
<ul>
<li><code>+</code> needed to be escaped!</li>
</ul>
<h3>
Stage 3</h3>
<ol>
<li>re-highlight with <code>gv</code></li>
<li>fix indention with <code><<</code> followed by <code>.</code> as needed
</li>
<li>re-highlight with <code>gv</code></li>
<li>eliminate returns with <code>:'<,'>s/return /</code></li>
<li>re-highlight with <code>gv</code></li>
<li>add <code>=</code> on the end of the let lines with <code>:'<,'>s:let .*:\0 =</code></li>
<li>re-highlight with <code>gv</code></li>
<li>Fix casing to my preference for module methods (camel-case) with <code>:'<,'>s:let \(\u\w\+\):let \l\1</code> or closer to regular regex <code>:'<,'>s:let \([A-Z]\w\+\):let \l\1</code></li>
</ol>
<b>regex inconsistencies and irritations</b>
<br />
<ul>
<li>had to escape <code>(</code> and <code>)</code> for capturing groups</li>
<li>had to escape <code>+</code> (<code>:'<,'>s:let \(\u\w+\):let \l\1</code> did not work)
</li>
<li>didn't have to escape any of the following <code>.</code><code>*</code></li>
</ul>
<h3>
Stage 4 (work in progress)</h3>
fix up the method declarations a little more
<br />
<ol>
<li>re-highlight with <code>gv</code></li>
<li>regex: <code>:'<,'>s/\(let \w\+\)(\?\(\w\+\)\(?\)\? \(\w\+\)[,)]/\1(\3\4:\2)/</code> <ul>
<li>switch the order of type and name</li>
<li> move <code>?</code> (Nullable to the front for replacement with the word `Nullable` in F#)</li>
<li> add : of the first argument on method declaration lines with </li>
</ul>
</li>
</ol>
which leaves me with the following to convert so far
<pre class='brush: csharp'>
module BillingStages =
let loadRejectedGrid(payerId:string) Connector cn) =
Pm.Dal.BillingStages.loadRejectedGrid(payerId, SessionVariables.Instance.CurrentFacility.FacilityID, cn)
.Select(r => new BillingStageDataModel(r));
let loadPendingGrid(payerId:string) Connector cn) =
Pm.Dal.BillingStages.loadPendingGrid(payerId, SessionVariables.Instance.CurrentFacility.FacilityID, cn)
.Select(r=> new BillingStageDataModel(r));
let loadReviewGrid(payerId:string) Connector cn) =
Pm.Dal.BillingStages.loadReviewGrid(payerId, SessionVariables.Instance.CurrentFacility.FacilityID, cn)
.Select(r => new BillingStageDataModel(r));
let loadPatientBilling(?includeThirdParty:bool) Connector cn) =
Pm.Dal.BillingStages.loadPatientBilling(SessionVariables.Instance.CurrentFacility.FacilityID, includeThirdParty, cn).Select(bsm => new BillingStageDataModel(new Pm.Schema.BillingStageRecord(bsm.Age.ToString(), 0m, 0m, bsm.DOS ?? DateTime.Now, null, 0, bsm.FirstStatement ?? DateTime.Now, bsm.NofStatements ?? 0, bsm.PatientName, bsm.ThirdPartyPayerId ?? 0, null, null, bsm.Appointmentid ?? 0, null, null)) { PrimaryGuarantorType= bsm.AppointmentPrimaryGuarantorType });
let loadCompleted(cn:Connector) =
Pm.Dal.BillingStages.loadCompleted(SessionVariables.Instance.CurrentFacility.FacilityID, cn).Select(bsm => new BillingStageDataModel(new Pm.Schema.BillingStageRecord(null,bsm.ClaimAmount ?? 0, 0, bsm.DOS ?? DateTime.Now, bsm.Claimdate ?? DateTime.Now, bsm.Daysfilled ?? 0, null, 0,bsm.PatientName, 0, bsm.Claimstatus, null, bsm.Appointmentid ?? 0,null,null)));
let loadPrepGrid(facilityId:int) Connector cn) =
Pm.Dal.BillingStages.loadPrepClaimGrid(facilityId, cn).Select(r => new BillingStageDataModel(r));
</pre>Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-68251109197576891432016-02-09T10:05:00.001-05:002016-02-09T10:05:44.706-05:00Requested Post: Dev machine setupHow do I setup a machine to develop on?
<br />
<br />
Everywhere I've gone, one of my first steps is to document every single thing I'm told to do to setup the machine for development. Not a single one of the shops where did I this, was it appreciated or possibly even used as far as I can tell. Even in the many that had badly outdated instructions for new developers, I updated them to no avail or interest.<br />
<br />
So here's my generic procedure for a machine (mostly independent of target platform/ui)<br />
<ol>
<li>Ask about getting a 2nd monitor if there isn't one.</li>
<li>evaluate provided keyboard/mouse</li>
<li>evaluate if using a git layer over top of the solution would be helpful</li>
<ol>
<li>branching in git is cake</li>
<li>branching in TFS is very painful</li>
</ol>
<li>document and do all solution/job/domain specific setup</li>
<ol>
<li>ask around about pain points in setup</li>
</ol>
<li>ensure the work solution actually runs</li>
<li>installs</li>
<ol>
<li>setup Chrome (or Firefox)</li>
<li>install git or <a href="http://gitextensions.github.io/">git-extensions</a> (even if the shop doesn't use git)</li>
<li><a href="https://www.linqpad.net/">Linqpad</a> </li>
<ol>
<li>download (4 and 5)</li>
<li>register it </li>
<li>set it to point to the script repository created in 2</li>
</ol>
<li><a href="https://www.sublimetext.com/3">SublimeText</a></li>
<li>Telerik JustDecompile</li>
<li><a href="http://www.wingrep.com/">Windows Grep</a> - I am considering trying out <a href="http://stefanstools.sourceforge.net/grepWin.html">this one</a></li>
</ol>
<li>pull down my <a href="https://github.com/ImaginaryDevelopment/LinqPad">script repository</a> (this has all my most reusable/interesting scripting code)</li>
<li>write some scripting code </li>
<ol>
<li>to help verify and/or do step 3.</li>
<li>to help with any manual work done in switching contexts (ie. Dev/QA/Prod debugging)</li>
</ol>
<li>Consider (but not necessarily do, <a href="https://github.com/ImaginaryDevelopment/config">creating a git repo over my user profile folder</a>)</li>
<ol>
<li>this lets me watch changes to those folders for important settings or files that I might want to keep</li>
<li>also allows settings to roam between machines/domains</li>
</ol>
<li>Run VS code metrics analysis of work solution(s) - </li>
<ol>
<li>ask for approval to clean the methods with any of the following criteria:</li>
<ol>
<li>over 30 cyclomatic complexity</li>
<li>under 30 maintainability</li>
</ol>
</ol>
<li>evaluate build/<a href="https://en.wikipedia.org/wiki/Continuous_integration">CI</a> process</li>
<li>evaluate deploy process</li>
</ol>
<div>
<br /></div>
<div>
Additional very wise steps I need to start doing:</div>
<div>
<ul>
<li>Install <a href="http://www.sonarqube.org/">sonar</a> </li>
<ul>
<li>install the <a href="http://docs.sonarqube.org/display/PLUG/C%23+Plugin">C# plugin</a></li>
<li>test run current/latest code</li>
<ul>
<li>customize violation rules and re-run</li>
<li>document metrics (lines of code, number of projects, violations)</li>
</ul>
<li>setup a build process that would iterate from the beginning of solution all check-ins for code trending</li>
</ul>
</ul>
<div>
<br /></div>
</div>
<div>
VS Extensions to install:</div>
<div>
<ul>
<li><a href="https://visualstudiogallery.msdn.microsoft.com/59ca71b3-a4a3-46ca-8fe1-0e90e3f79329">VsVim</a></li>
<li>Web Essentials</li>
<li>Visual F# Power Tools</li>
</ul>
</div>
<div>
<br /></div>
<div>
Other tools I might install depending on platform/ui type:</div>
<div>
<ul>
<li><a href="http://www.telerik.com/fiddler">Fiddler</a></li>
<li><a href="http://www.getpaint.net/index.html">Paint.NET</a></li>
<li>Xaml</li>
<ul>
<li><a href="https://snoopwpf.codeplex.com/">SnoopWpf</a></li>
<li><a href="http://wpfinspector.codeplex.com/">WpfInspector</a></li>
</ul>
<li><a href="https://technet.microsoft.com/en-us/sysinternals/bb842062">SysInternal suite</a></li>
<li><a href="http://hawkeye.codeplex.com/">Hawkeye</a></li>
<li><a href="http://www.seleniumhq.org/">Selenium</a></li>
</ul>
<div>
Tools wishlist:</div>
</div>
<div>
<ul>
<li><a href="http://www.typemock.com/isolator-product-page">TypeMock</a> - Isolator</li>
<li>NDepend</li>
<li><a href="http://hakshop.myshopify.com/products/usb-rubber-ducky-deluxe?variant=353378649">Rubber Ducky</a></li>
<ul>
<li>https://github.com/hak5darren/USB-Rubber-Ducky/</li>
</ul>
</ul>
</div>
Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com1tag:blogger.com,1999:blog-7386105208394107344.post-22306344684165471812015-11-17T13:37:00.001-05:002015-11-17T13:46:58.686-05:00T4 Generates my business objects for me revisited with F#A long time ago I wrote an article about the code I was using in <a href="http://imaginarydevelopment.blogspot.com/2010/01/t4-generates-my-business-objects-for-me.html">T4 Generates my business objects for me</a>. Today I've rewritten the idea with F# as the target. F# projects don't support T4 so I used a C# project to generate F# code into an F# project.
The parts of the generated code
<ul>
<li>A readonly interface</li>
<li>A read-write interface</li>
<li>An F# record type </li>
<li>An F# module for mapping code methods (with <a href="https://msdn.microsoft.com/en-us/library/dd548046.aspx">Statically Resolved Type Parameter</a> support aka Duck Typing or Structural Typing) </li>
<li>An F# class with <code>INotifyPropertyChanged</code> for WPF consumption</li>
</ul>
Benefits
<ul>
<li>Implementing interfaces is not allowed to be implicit so I've automated that headache.</li>
<li><code>INotifyPropertyChanged</code> desires magic strings (exception in the lovely new C# 6 <code>nameof</code> operator) so automation makes sure the strings are correct as of the last time T4 was run</li>
<li>If I pass around the read-only interface, I don't care how far down the rabbit hole of that object goes, it's immutable, no one is modifying it</li>
<li>If I pass around the read-write interface, all the consuming methods can be ignorant of concrete types</li>
<li>I'm generating Metadata from the db into the comments on the properties (tons of untapped potential here)</li>
</ul>
<h4>View the generated code and generator t4</h4><p> - <a href="https://gist.github.com/ImaginaryDevelopment/f0b282a0e814a1a9f9ec">Generator and generated code from new T4 to F#</a></p>
<p>Things I want to add:</p>
<ul>
<li>foreign key information to the auto-generated property comments</li>
<li>consider having the Read-Write interface implement the Read-Only interface</li>
<li>figure out how to update Microsoft's Type Providers to have them implement my interfaces.</li>
<li>add an option for generating Sql Sprocs that work against these types
</ul>
Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-35234343690608583072015-02-20T10:52:00.000-05:002015-02-20T10:52:13.065-05:00Get Code Coverage from VS without writing automated testsCreate a batch file somewhere in your system as such:
<pre class="code shell">
"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\vsinstr.exe" -coverage "%1"
"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\VSPerfCmd.exe" /start:coverage /output:run.coverage
start "%1" /wait "%1"
"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\VSPerfCmd.exe" /shutdown
notepad "run.coverage"
echo "%1"
echo "%2"
pause
</pre>
Create an External Tool Entry in your Visual Studio Tools Menu.
<ul>
<li>Title: RunCovered</p></li>
<li>Command: c:\projects\config\batches\RunCovered.bat</li>
<li>Arguments <pre>$(TargetName)$(TargetExt) $(BinDir)</pre></li>
<li>Initial Directory: <pre>$(BinDir)</pre></li>
</ul>
<p>replace the command with wherever your new shiny batch file lives.</p>
<p>Then in trigger that External Tool on the Tools Menu.</p>
<p>It will launch your app, and you can then click around or do whatever is needed to activate parts of your application, and the results will be saved into the same directory as your executable file as run.coverage. Open this file in your already open Visual Studio instance, or drag and drop the file from explorer into VS. </p>
<p>There you have your App's coverage metrics for that run.</p>Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-86775466709590231862014-11-13T16:26:00.001-05:002014-11-13T16:28:06.905-05:00Binding redirects at runtimeHopfully the C# audience can read this F#, but this is how you would do it =)
<pre class='brush: csharp'>
// 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"
</pre>Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-75547864930974740502014-10-09T12:51:00.001-04:002014-10-09T12:51:55.720-04:00Using a Git layer on top of TFSNot having git bugs me. Working with any other source control system takes away some of my power.<br />
<br />
With a git layer over whatever other source control system is required for the team:<br />
<br />
<ul>
<li>I can check-in or revert individual lines of code in a file. </li>
<li>I can create a branch for local modifications that should never be checked into the team source control.</li>
<li>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)</li>
<ul>
<li>I can create my own personal historical record of change save-points and notes/comments</li>
</ul>
<li>I can select which lines of a file should go into a check-in without shelving the entire change.</li>
</ul>
<br />
How to:<br />
<br />
Create a <a href="http://serverfault.com/questions/7109/how-do-i-create-a-symbolic-link-in-windows">junction</a> or <a href="http://superuser.com/questions/255731/make-a-hard-link-without-extra-programs-in-windows-7">hardlink</a> 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.<br />
<br />
So I have all my code under <code>c:\development\foo</code><br />
<br />
<ol>
<li> I change working directory to <code>c:\projects\bar</code> and <code>mklink /h /j foobar c:\development\foo</code></li>
<li><code>git init</code></li>
<li>Always point visual studio at the legacy folder <code>c:\development\foo</code>.</li>
<li>Always point your git tool(s) at <code>c:\projects\bar</code></li>
<li>profit!</li>
</ol>
<div>
<br /></div>
<div>
<br /></div>
Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-71259086412674062802014-06-30T10:49:00.002-04:002014-06-30T10:54:34.526-04:00Why am I so enthusiastic about F#?I'm suddenly really passionate about F#. This is new as of ~3 weeks ago.<br />
Why?<br />
<br />
<br />
<ul>
<li><a href="http://fpbridge.co.uk/why-fsharp.html">Why F#?</a></li>
<li>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</li>
<ul>
<li><a href="http://blog.ploeh.dk/2014/03/10/solid-the-next-step-is-functional/">SOLID: the next step is Functional</a>- Mark Seemann (Writer of Dependency Injection in .Net)</li>
<ul>
<li>Dependency Injection is not even relevant in F#</li>
</ul>
</ul>
<li>When and where do we do our domain modelling in C#?</li>
<ul>
<li>Why are we deciding all these things in C# first?</li>
<ul>
<li>DAL (or ORM)</li>
<li>To Write tests or not</li>
<li>the db implementation</li>
<li>the ui</li>
<li>service layer</li>
</ul>
<li>Is it any wonder problem domain logic is found everywhere from the ui, to the ORM layer, to the persistence code?</li>
<li>Domain modelling with the F# type system - Scott Wlaschin - NDC (Norway Developer's Conference)</li>
<ul>
<li><a href="http://vimeo.com/97507575">Video</a></li>
<li><a href="https://www.slideshare.net/slideshow/embed_code/28992749">slides</a></li>
</ul>
</ul>
<li>What can C# do that F# can't? Null Reference Exception!</li>
<ul>
<li><a href="http://www.codemag.com/Article/1203081">Smashing the Myth: Why You Must Learn F# - Even If You Aren’t Writing Rocket Science Apps</a></li>
<ul>
</ul>
</ul>
<li>What kind of boilerplate required cruft is there in C# that you might not realize?</li>
<ul>
<li><a href="http://simontcousins.azurewebsites.net/does-the-language-you-use-make-a-difference-revisited/">Does the language you use make a difference (C# vs F#)</a> </li>
</ul>
<li>What kind of boilerplate cruft might you find in <a href="http://flyingfrogblog.blogspot.com/2011/02/size-of-industrial-f-code-bases.html">OCaml compared to F#</a>?</li>
<ul>
<li>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 </li>
</ul>
<li>F# not permitted at work?</li>
<ul>
<li><a href="http://fsharpforfunandprofit.com/posts/low-risk-ways-to-use-fsharp-at-work/">26 low risk ways to use F# at work </a></li>
</ul>
</ul>
Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-73580135340066272752014-04-17T09:45:00.001-04:002014-04-17T09:45:41.381-04:00T4 ApiController GeneratorThis T4 assumes your <code>DbContext</code> is called <code>ApplicationDbContext</code> 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 <code>IRepository</code> pattern or anything similar. It also expects to be in a directory below the <code>EnvDteHelper.ttinclude</code>.
<pre class='brush: csharp'>
<#@ 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);
}
}
<#}#>
}
</pre>
Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-76076772463069703732014-04-14T16:42:00.001-04:002014-04-14T16:42:44.526-04:00VS2013 negative look-behind on a sql server database projectFind me all the sql tables that have an unnamed foreign key.
<pre class="code">Find all "(?>!(CONSTRAINT|--) .*)FOREIGN KEY", Regular expressions, Subfolders, Find Results 1, Current Project: Project.Sql\Project.Sql.sqlproj, "*.SQL"</pre>Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-62925130963196948762014-03-12T10:51:00.003-04:002014-03-12T10:51:53.588-04:00Vs2013 search and replace in html classesThis lovely regex helps pull extra whitespace out of a class attribute.
search:
<code>class="\s*(\w+)\s+(\s\w+)?\s*"</code>
replace:
<code>class="$1$2"</code>
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.
Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com1tag:blogger.com,1999:blog-7386105208394107344.post-54126812402083892172014-01-20T14:09:00.000-05:002014-01-20T14:11:37.428-05:00T4 Generating Dummy Adapters<p>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.<p>
<p>Added my Nuget packages for code generation: <a href="https://www.nuget.org/packages/T4EnvDte/0.11.0">T4EnvDte</a> and <a href="https://www.nuget.org/packages/T4MultiFile/">T4MultiFile</a>.</p>
<p>
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.</p>
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.
<pre class='brush: csharp'>
<#@ 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;#>
</pre>
<p>
This is the basic start of the file, with 2 variables declared thus far. <code>Dte</code> and <code>doMultiFile</code>. 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.
</p>
<pre class='brush: csharp'>
Main file output
Last run at <#=DateTime.UtcNow.ToString()#>
</pre>
<p>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.</p>
<pre class='brush: csharp'>
<# 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
#>
</pre>
<p>This is all the setup and starting the file per type looping.
<ol><li><code>targetNs</code> asks what the proper namespace for our current file output location would be</li>
<li><code>projects</code> gets all the projects in the current solution using the new EnvDte helper code</li>
<li><code>project</code> the actual project name we are looking for</li>
<li><code>toGen</code> gets the projectItems in the source <code>project</code> we are interested in</li>
</ol>
Now we look over those items we will be generating
<ol>
<li><code>className</code> holds the name of the new adapter class</li>
<li>tell the helper to start a new file for the class</li>
<li><code>fcModel</code> holds the FileCodeModel of the current source projectItem</li>
<li><code>ns</code> holds the namespace that wraps our target class and assumes there is only one namespace in the source file</li>
<li><code>codeClass</code>holds the reference to the target class and assumes there is only one class in the source file</li>
<li><code>codeMethods</code> gets all the methods of the source class</li>
</ol>
</p>
<p> the meat of the individual file generation</p>
<pre class='brush: csharp'>
//<#=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
</pre>
<p><ol>
<li>adds a comment of the source file name</li>
<li>adds a using clause for the source file's namespace</li>
<li>adds a namespace for our new file</li>
<li>adds a class to our new adapter</li>
<li> loop over the public methods in the class<ol><li><code>parms</code> holds a reference to the parameters of the method if any</li>
<li>Generate the method signature</li><li>Generate the method return code (which includes the singleton-based <code>Instance</code> property requirement of this domain layer</li>
</ol></li>
<li> close up the single file generator and the foreach</li>
</ol>
</p>
<p> the last little bits of code that complete the file</p>
<pre class='brush: csharp'>
manager.Process(doMultiFile);
#>
<#+
string Before(string text, string delimiter)
{
return text.Substring(0,text.IndexOf(delimiter,StringComparison.CurrentCulture));
}
#>
</pre>
<p>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</p> Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-59382610559379819582013-12-10T16:56:00.000-05:002013-12-10T17:16:12.818-05:00A little JQuery <-> Json <-> Mvc<p>It has been quite awhile since I've dealt with complex model binding across a set of inputs in mvc.
</p>
<p>
If one controller action's params (or a property on one of those params) is an <code>IEnumerable<T></code> then Mvc will not write the element names in a way that will properly go back to the server.
</p>
Short and sweet (and probably hacky way) to do it:
<p>
I Json serialized the parent model into an html attribute on to some parent of the value(s) I want to submit. Some questions allow many answers, and I want to save each time a question answer is changed.
</p>
I'm working on making that serialization not contain the about to be consumed <code>IEnumerable<T></code>property.
<br />
<pre class="brush: js">var save = function (element, value, event, previousValue,debug) {
console.log('saving:' + value + ' to ' + saveUrl);
var demoText = $(element).closest('[data-demographic]').attr('data-demographic');
var demoJson = htmlDecode(demoText);
var demoModel = JSON.parse(demoJson);
var oldAnswers = demoModel.PossibleAnswers;
demoModel.PossibleAnswers = [];
//TODO: handle multi-select, single-select radio, etc...
if (Object.prototype.toString.call(value) === '[object Array]') {
//value and previousValue could be arrays for multi-select answers
$.each(value, function (i, e) {
demoModel.PossibleAnswers.push({ Id: e });
});
} else {
demoModel.PossibleAnswers.push({ Id: value,Text:$(element).parent().text().trim() });
}
var data = JSON.stringify(demoModel);
data=data.replace('][', '].[');
$.ajax({
dataType: 'json',
url: saveUrl,
contentType: 'application/json; charset=utf-8',
accept: debug ? {
json: 'application/json',
} : {},
type: 'POST', data: data,
success: function (data, status, jqXhr) {
if (debug) {
var answerMirror = data.PossibleAnswers;
delete data.PossibleAnswers;
console.log(data);
console.log(answerMirror);
} else { //update dom to new read-only model?
//TODO: adjust with new html
console.log(data);
}
}
});
};
</pre>
There's the heart of it. Notice I don't actually have to use the special <code>name=foo.bar[0].Id</code> on my inputs, but that is probably still a good idea.
This thing was almost working without doing any <code>JSON.stringify</code> but the <code>IEnumerable<T></code> data was ignored by Mvc's model binder. With that in mind, I had to specify the <code>contentType</code> of the ajax request.Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-67893375031463863862013-12-04T15:56:00.000-05:002013-12-04T16:55:54.777-05:00Inline markup delegates and razor helper delegate warsSo I had some markup that was being duplicated all over the place in MVC.<br />
<br />
This is mostly boilerplate <a href="http://getbootstrap.com/components/#panels">bootstrap paneling</a>, with <a href="http://masonry.desandro.com/">masonry</a> on top.<br />
<br />
How would you provide a generic razor to meet bootstrap/masonry without creating a bunch of classes or partials to account for special case(s)? How in razor would you write a method that can take <a href="http://stackoverflow.com/questions/9157772/asp-net-mvc3-pass-razor-markup-as-a-parameter">inline markup</a> (for example any @<text> call)?<br />
<br />
One way is <code>@helper</code> Take note that HelperResult's documentation says specifically it is not intended to be used directly in your code.
So I headed in down the path of <a href="http://blog.slaks.net/2011/04/dissecting-razor-part-9-inline-helpers.html">inline razor helper delegates</a>:<br />
<br />
<a name='more'></a><br />
<br />
<pre class="brush: csharp">@{
var scope = (Web.ViewModels.ScopeModel)ViewBag.Scope;
}
@helper progressBar(int completion)
{
<div class="progress progress-striped">
<div class="progress-bar" role="progressbar"
aria-valuenow="@completion.ToString()" aria-valuemax="100" aria-valuemin="0"
style="width : @completion%;">
<span class="sr-only">@completion% Complete</span>
</div>
</div>
}
@helper widget(Func<dynamic,HelperResult> title,Func<dynamic,HelperResult> body,Func<dynamic, HelperResult> footer)
{
<div class="widget-wrapper md-widget">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">@title(null)</h3>
</div>
<div class="panel-body text-center">
@body(null)
</div>
<div class="panel-footer">
@footer(null)
<div class="clearfix"></div>
</div>
</div>
<div class="clearfix"></div>
</div>
}
<div class="tile-layout">
<div class="row">
<div class="col-md-12">
<div class="widget-container">
@widget(@<text>Profiles</text>, @<text>
<p>Your profile is @scope.Member.Completion% complete! To complete your profile click on the incomplete profiles below.</p>
@progressBar(scope.Member.Completion)
</text>,
@<text>
<a class="btn btn-default pull-right" href="#" role="button" data-toggle="modal" data-target="#pending-rewards-info">Learn more &raquo;</a>
</text>)
@foreach (var p in scope.Member.Profiles)
{
@widget(@<text>
<img src="@p.Icon" /><a href="">@p.Name Profile</a><a href="#" class="btn" rel="tooltip" data-placement="bottom" title="" class="remaining" data-original-title="Number of Unanswered Questions for this Profile">@p.UnansweredCount</a>
</text>, @<text>
@progressBar(p.CompletionLevel)
</text>,@<text>
</text>)
}
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</pre>
holy cow that's a mess. To boot, formatting the document would actually break it, it would remove some tags and add others in inappropriate places.<br />
<br />
calling @<text> is a way to pass markup as a delegate to a method so that you can provide parts and it can control where those go( or if they go).<br />
<br />
So after a few hours of trying to improve it and find out what else I could do:
<br />
<pre class="brush: csharp">@{
Layout = MVC.Shared.Views._LayoutTiles;
var scope = (Web.ViewModels.ScopeModel)ViewBag.Scope;
}
<div class="tile-layout">
<div class="row">
<div class="col-md-12">
<div class="widget-container">
@Html.Widget("lg", "panel-default panel-lead", @<h3 class="panel-title">Profiles</h3>,
_ => profilesHeaderBody(scope.Member.Completion))
</div>
<div class="widget-container">
@foreach (var p in scope.Member.Profiles)
{
@Html.Widget(addPanelClasses:"panel-default",header: _ => profileHeader(p),body: _ => profileBody(p))
}
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
@helper profileHeader(Web.ViewModels.Interfaces.Profile p)
{
<h3 class="panel-title">
<img style="width:33px;height:24px;margin-top:-3px;" src="@p.Icon" />&nbsp;<a href="@Url.Action(p.Name)">@p.Name Profile</a><span class="pull-right"><small><a href="#">Edit</a></small></span>
</h3>
}
@helper profileBody(Web.ViewModels.Interfaces.Profile p)
{
@progressBar(p.CompletionLevel, "", "margin-bottom:0;")
<span class="pull-left"><small>Completion: @p.CompletionLevel%</small></span>
<span class="pull-right"><small>Missing answers: @p.UnansweredCount</small></span>
}
@helper profilesHeaderBody(int completion)
{
<p class="lead"><strong>Total completion: @completion%</strong></p>
@progressBar(completion, "progress-bar-success", "max-width:500px;margin-left:auto;margin-right:auto;")
}
</pre>
Still unwanted syntax elements, and we have added a small helper method, and a partial view.
<br />
<pre class="brush: csharp">@model CVS.Member.Web.ViewModels.Shared.WidgetViewModel
<div class="widget-wrapper @Model.WidgetSize-widget" style="@Model.WidgetStyle">
<div class="panel @Model.AddPanelClasses">
@if (Model.HeaderMarkup != null)
{
<div class="panel-heading">
@Model.HeaderMarkup(null)
</div>
}
@if (Model.Body != null)
{
<div class="panel-body text-center">
@Model.Body(null)
</div>
}
@if (Model.UnwrappedContent!=null)
{
@Model.UnwrappedContent(null)
}
@if (Model.Footer != null)
{
<div class="panel-footer">
@Model.Footer(null)
<div class="clearfix"></div>
</div>
}
</div>
<div class="clearfix"></div>
</div>
</pre>
Every possible combination of a Func<> wound up with extra work in the calling site. Here's the model class:
<br />
<pre class="brush: csharp">using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.WebPages;
namespace Web.ViewModels.Shared
{
public class WidgetViewModel
{
public string HeaderClass { get; set; }
public string AddPanelClasses { get; set; }
public string WidgetSize { get; set; }
public Func<dynamic, HelperResult> HeaderMarkup { get; set; }
public Func<dynamic, HelperResult> Body { get; set; }
public Func<dynamic, HelperResult> UnwrappedContent { get; set; }
public Func<dynamic, HelperResult> Footer { get; set; }
public string WidgetStyle { get; set; }
public WidgetViewModel()
{
WidgetSize = "md";
}
}
}
</pre>
So to call that with either inline helpers or inline markup:
<br />
<pre class="brush: csharp"><div class="tile-layout">
<div class="row">
<div class="col-md-12">
<div class="widget-container">
@Html.Widget("lg", "panel-default panel-lead", @<h3 class="panel-title">Profiles</h3>,
_ => profilesHeaderBody(scope.Member.Completion))
</div>
<div class="widget-container">
@foreach (var p in scope.Member.Profiles)
{
@Html.Widget(addPanelClasses:"panel-default",header: _ => profileHeader(p),body: _ => profileBody(p))
}
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</pre>
See how I have to delegate the call to a delegate? and still haven't gotten around the problem of short sweet stuff for inline markup.
<br />
Perhaps we can eliminate that somehow!<br />
<br />
<pre class="brush: csharp">public static Func<HelperResult> GetMarkupResult(this HtmlHelper helper, Func<dynamic, HelperResult> markup)
{
return ()=>markup(null);
}
</pre>
<h1>Solution</h1>
Finally what broke the linguistics barrier...
looking at HelperResult and seeing that it already implements IHtmlString. I was concerned that it was going to use ToString and re-encode the markup. Reworked the helper and model.
<br />
<br />
<pre class="brush: csharp"> public static IHtmlString GetMarkupResult(this HtmlHelper helper, Func<object, HelperResult> markup)
{
return markup(null);
}
</pre>
<pre class="brush: csharp">Web.ViewModels.Shared
{
public class WidgetViewModel
{
public WidgetViewModel(string headerText = null)
{
WidgetSize = "md";
if (headerText.IsNullOrEmpty() == false)
HeaderMarkup = new MvcHtmlString(headerText);
}
public string HeaderClass { get; set; }
public string AddPanelClasses { get; set; }
public string WidgetSize { get; set; }
public IHtmlString HeaderMarkup { get; set; }
public IHtmlString Body { get; set; }
public IHtmlString UnwrappedContent { get; set; }
public IHtmlString Footer { get; set; }
public string WidgetStyle { get; set; }
}
}
</pre>
Here's the partial, it is cleaner as well
<br />
<pre class="brush: csharp"><div class="widget-wrapper @Model.WidgetSize-widget" style="@Model.WidgetStyle">
<div class="panel panel-default @Model.AddPanelClasses">
@if (Model.HeaderMarkup != null)
{
<div class="panel-heading">
@Model.HeaderMarkup
</div>
}
@if (Model.Body != null)
{
<div class="panel-body text-center">
@Model.Body
</div>
}
@if (Model.UnwrappedContent!=null)
{
@Model.UnwrappedContent
}
@if (Model.Footer != null)
{
<div class="panel-footer">
@Model.Footer
<div class="clearfix"></div>
</div>
}
</div>
<div class="clearfix"></div>
</div>
</pre>
A cleaner result for the caller and an improved api for the system:
<br />
<pre class="brush: csharp"><div class="tile-layout">
<div class="row">
<div class="col-md-12">
<div class="widget-container">
@Html.Widget(new WidgetViewModel
{
WidgetSize = "lg",
AddPanelClasses = "panel-default panel-lead",
HeaderMarkup = Html.GetMarkupResult(@<h3 class="panel-title">Profiles</h3>),
Body = profilesHeaderBody(scope.Member.Completion)
})
</div>
<div class="widget-container">
@foreach (var p in scope.Member.Profiles)
{
@Html.Widget(new WidgetViewModel
{
HeaderMarkup =profileHeader(p),
Body = profileBody(p)
})
}
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</pre>Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-77085647700490632172013-07-16T17:30:00.000-04:002013-07-16T17:30:00.396-04:00How do you like your noodle baked? medium or well done?<i>The second reason to avoid this is simply because it bakes the noodle of anyone who reads the code.</i> - <a href="http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx">Eric Lippert</a>
<div>
So when I see the following... I want to scream, yell, and run away.
</div>
Csla.BusinessListBase:
<pre class='brush: csharp'>
public abstract class BusinessListBase<T, C> : ExtendedBindingList<C>, IEditableCollection, IBusinessObject, ISupportUndo, IUndoableObject, ICloneable, ISavable, IParent
where T : Csla.BusinessListBase<T, C>
where C : Csla.Core.IEditableBusinessObject
{
// ... other code
}
</pre>
CompanyName.Library.Base:
<pre class='brush: csharp'>
public abstract class CompanyBusinessBaseCollection<T, TCollection> : BusinessListBase<T, TCollection>
where T : Csla.BusinessListBase<T, TCollection>
where TCollection : Csla.Core.IEditableBusinessObject
{
// ... other code
}
</pre>
Take note... that that's 2 levels of abstract class so far.. climbing the chain we have: <code>Csla.Core.ExtendedBindingList<T></code> (where <code>T</code> now means <code>C</code>), <code>System.ComponentModel<T></code>. I only stopped once I hit the framework inheritance chain start.
<div>
I just had to get that out...
</div>
<div>
Now to find references to cite on my rant:
</div>
<ul>
<li>
<i>since inheritance is a very intimate (and encapsulation breaking) relationship, it's easy for subclasses to effectively break their superclasses by, for instance, omitting necessary behavior when they the methods are called</i> - <a href="http://martinfowler.com/bliki/DesignedInheritance.html">Martin Fowler - Designed Inheritance</a>
</li>
<li>
<i>In fact, the <a href="http://en.wikipedia.org/wiki/Design_Patterns_%28book%29">Object-Oriented Design Patterns</a> book from "the gang of four" has this to say about inheritance: Prefer object composition to class inheritance</li>
<li>
<i>Inheritance is pretty enticing especially coming from procedural-land and it often looks deceptively elegant. I mean all I need to do is add this one bit of functionality to another other class, right? Well, one of the problems is that inheritance is probably the worst form of coupling you can have. Your base class breaks encapsulation by exposing implementation details to subclasses in the form of protected members. This makes your system rigid and fragile.
The more tragic flaw however is the new subclass brings with it all the baggage and opinion of the inheritance chain. Why does a model that enforces validation attributes care how the model is constructed? The answer is it shouldn’t, and to add insult to injury, the better design is actually easier to implement. Listen closely and you may hear the wise whispers of our forefathers…</i> - <a href="http://www.agileatwork.com/inheritance-is-evil-the-story-of-the-epic-fail-of-dataannotationsmodelbinder/">Inheritance is Evil: The Epic Fail of the DataAnnotationsModelBinder</a>
</li>
<li>
Prefer object composition to class inheritance
</i> - <a href="http://www.jspatterns.com/a-case-against-inheritance/">JsPatterns.com - A case against inheritance</a>
</li><li>
<i>Joshua Bloch hits it on the nail in his Effective Java book item about “Favoring composition over inheritance.”</i> - <a href="http://theshyam.com/2009/08/is-inheritance-overrated-needed-even/">The Shyam - Is Inheritance Overrated? Needed even?</a></li>
</ul>
Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com2tag:blogger.com,1999:blog-7386105208394107344.post-9243923027512691462013-07-09T18:00:00.000-04:002013-07-09T18:00:02.322-04:00Using a local source repo to work on vs2008 projects in vs2012If the project type can't be opened or worked in the newer Visual Studio then you are still out of luck, but otherwise here we go.
Assuming you have <a href="http://git-scm.com/">Git</a> installed, go to the top level of a directory that would include both your .sln and .proj files.
<pre class="brush: bash">$ git init</pre> inside that directory
then if you don't have a default set of ignores. you will want to set that up.
Before opening Visual Studio, do your initial commit.
<pre class="brush: bash">$ git checkout -b Vs2012</pre> to create the new branch.
Open in Visual Studio 2012.
Commit the changes.
Now you can make all your edits and commit them. When you want to pull the changes to the old VS branch change to that branch and do a <a href="http://git-scm.com/docs/git-cherry-pick">git-cherry-pick</a>
<pre class="brush: bash">$ git cherry-pick SHA</pre> replace SHA with the SHA hash of the commit you want to pick.
<a href="http://stackoverflow.com/a/3933416/57883">Or to do them in bulk</a>...
<pre class="brush: bash">$ git cherry-pick SHA1..SHA2</pre> replace SHA1 with the SHA hash of the commit you want to skip, and SHA2 with the last one you want.Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-46937945799148832462013-05-14T14:18:00.002-04:002013-05-14T14:18:55.954-04:00High speed data scraping exampleSituation, I wanted to get the names of the different playable champions to print out and discuss.
<div>
So went to http://na.leagueoflegends.com/champions and ran jquery to get the names:
<code>$('td.description span a[href]').text()</code>
<p>
This produced all the names run together. Perhaps there's a more straightforward way to aggregate them in a short single line, but I was in a hurry.</p>
Open My LinqPad (which includes the <a href="https://nuget.org/packages/Inflector/">Inflector</a> NuGetPackage)
Paste in the string and add <code>.Titleize()</code> on the end. Job Done.
</div>
<div>
<p>
Next up marvelheroes.com at https://marvelheroes.com/game-info/game-guide/heroes/grid</p>
<p>
jQuery is loaded but not as $ here
</p>
<code>jQuery('div.field-content a[href*=/heroes]').map(function(i,e){return jQuery(e).attr('href').split('/')[2];})</code>
in this case did more jquery, so the inflector wasn't needed.
</div>
<div>
Steam games on sale:
Click specials tab:
<code>jQuery('div.tabbar div[onclick]:contains(Specials)').click()</code>
<p>
Enter the unspeakable as the first found working solution to help load all the items:
<code>eval(jQuery('#tab_Discounts_next a[href]').attr('href'))</code>
</p>
make it get all the pages <code>var getNext=jQuery('#tab_Discounts_next a[href]').attr('href'); var times=Math.ceil(getNext.split(':')[1].split(', ')[2]/10); eval(getNext);for(var i=1;i>times-1;i++){setTimeout(function(){console.log('getting more'); eval(getNext);},1500*i);}</code>
Get all the names
<code>jQuery('#tab_Discounts_items div.tab_desc.with_discount h4').map(function(i,e){return jQuery(e).text();})</code>
Done.
</div>Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-83715536218254868822013-02-25T16:56:00.001-05:002013-02-25T16:56:12.556-05:00Run Chai based tests with PhantomJs<br />
PhantomJs is installed at <code>C:\Program Files (x86)\phantomjs-1.8.1-windows</code> <br />
then add it to the path <br />
i had to put <code>chai.js</code> in the target directory of the phantom script which makes me sad, but perhaps I'll figure that out next.<br />
<br />
at the command prompt:<br />
<br />
<code>C:\Projects\mine\phantomjs>phantomjs payerportal.js>phantomjs hellophantom.js</code>
<br />
<pre class="brush: js">phantom.injectJs("chai.js");
var assert = chai.assert;
var pageToTest= 'http://localhost/helloworld/';
var url = pageToTest;
page.open(url, function(status) {
try{
assert.typeOf('test','string','test is a string');
} catch(err) {console.log('Test failed:'+err);}
});
</pre>Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-20946831358793682142013-02-11T10:22:00.003-05:002013-02-11T10:22:43.478-05:00What did you do this weekend?<br />
<ul>
<li>Started some online classes</li>
<ul>
<li><a href="https://www.udacity.com/course/cs262">Programming Languages</a></li>
<li><a href="https://www.udacity.com/course/cs255">Html5 Game Programming</a></li>
<li>Microsoft's - <a href="https://www.microsoftvirtualacademy.com/tracks/developing-html5-apps-jump-start">Developing in HTML5 with JavaScript and CSS3 Jump Start</a></li>
</ul>
<li>Read about tons of javascript libraries/frameworks</li>
<ul>
<li>Game development stuffs</li>
<ul>
<li><a href="http://www.melonjs.org/">MelonJs</a></li>
<li><a href="http://impactjs.com/">ImpactJs</a></li>
<li><a href="http://www.limejs.com/">LimeJs</a></li>
</ul>
<li>Read about and applied some javascript unit testing</li>
<ul>
<li>In <a href="http://pivotal.github.com/jasmine/">JasmineJs</a></li>
</ul>
</ul>
<li>Did just a little<a href="http://nodejs.org/"> Node.js</a> in <a href="http://c9.io/">Cloud9</a> while saving to <a href="https://github.com/">Github</a></li>
<li>Did some <a href="http://en.wikipedia.org/wiki/JSONP">JsonP</a> against the <a href="http://blizzard.github.com/api-wow-docs/">World of Warcraft API</a></li>
<ul>
<li>Used <a href="http://knockoutjs.com/">Knockoutjs</a> to consume that JsonP data</li>
<ul>
<li>made that knockout shape a table complete with filter columns that are dynamic based on the schema (shape) of the jsonp data coming in</li>
</ul>
<li>Wrote code that allows a text area on a web page to submit <a href="http://en.wikipedia.org/wiki/Text_Template_Transformation_Toolkit">T4</a> and have it compile and transform into output using <a href="http://www.asp.net/mvc">Asp.net Mvc</a>, with some <a href="http://www.mono-project.com/">Mono</a></li>
<ul>
<li>Bought a domain to host this from</li>
<li>Deployed to said domain</li>
</ul>
</ul>
<li>Invited some developers over on friday to practice <a href="http://jquery.com/">jQuery</a> and then teach them KnockoutJs</li>
<li>Pushed up an <a href="http://orchardproject.net/">OrchardCms</a> site for a local developer user group</li>
<ul>
<li>added <a href="http://en.wikipedia.org/wiki/OAuth">OAuth</a> logins for</li>
<ul>
<li>Windows Live</li>
<li>Google</li>
</ul>
</ul>
<li>Created a few Mvc Projects some of them using</li>
<ul>
<li><a href="http://t4mvc.codeplex.com/">T4Mvc</a></li>
<li><a href="http://www.ninject.org/">Ninject</a></li>
</ul>
<li>Web-based IDEs</li>
<ul>
<li>Used</li>
<ul>
<li><a href="http://ideone.com/">IDEOne</a></li>
<li><a href="http://jsfiddle.net/">JsFiddle.net</a></li>
</ul>
<li>Explored</li>
<ul>
<li><a href="http://c9.io/">Cloud 9</a></li>
<li><a href="http://drawter.com/">Drawter</a></li>
</ul>
</ul>
<li>Started helping a friend explore the possibility of moving his business sites from using <a href="http://www.perl.org/">perl</a> for everything towards letting perl just fetch and secure the data while html and knockoutjs serve/prettify it.</li>
</ul>
<div>
Well it's been quite a full weekend!</div>
<div>
<br /></div>
<div>
During the work week:</div>
<div>
<br /></div>
<div>
<ul>
<li>Learned about <a href="http://codebetter.com/johnvpetersen/2012/03/22/bringing-odata-to-your-webapi-just-use-iqueryable/">WebAPI's IQueryable</a> possibilities</li>
<li>Found out they changed the ruling on <a href="http://msdn.microsoft.com/en-us/data/ef.aspx">Entity Framework</a></li>
<ul>
<li>We can now select straight into POCOs instead of only anonymous types or EF types</li>
<li>Still can't find any articles, blog posts, or announcements about this change.</li>
</ul>
<li>Forced EF to allow me to do queries that span linked databases.</li>
<li>Used Knockout to consume the <a href="http://www.odata.org/">OData</a> (or WebApi IQueryable)</li>
<ul>
<li>complete with a user friendly search form</li>
</ul>
<li>Explored <a href="http://phantomjs.org/">PhantomJs</a> for headless browser automation and testing</li>
<li>Updated my win forms tool for scraping data from our <a href="http://www.atlassian.com/software/stash/overview">Stash</a> and <a href="http://www.atlassian.com/software/crucible/overview">Crucible</a></li>
<li>Explored some <a href="http://www.specflow.org/">SpecFlow</a></li>
</ul>
<div>
<br /></div>
</div>
<div>
What did you do?</div>
Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0tag:blogger.com,1999:blog-7386105208394107344.post-68213611601827256152013-02-11T08:00:00.000-05:002013-02-11T08:00:05.593-05:00MyModelMetadataProviderSo I was tired of not having any inheritance on <code>Attribute</code>s in Mvc for scaffolding, column order, display names and the rest.
Here's some code to Allow an attribute called <code>ParentsAttribute</code> to allow you to inject parental metadata for another class that Mvc's <code>DisplayFor</code> will listen to.
<pre class='brush: csharp'>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MyMvc.Models
{
public class ParentsAttribute:Attribute
{
public Type[] Values { get; set; }
public ParentsAttribute(params Type[] parents)
{
Values = parents;
}
}
public class MyModelMetadataProvider:DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
//called once for each property of the viewmodel
var attrs =attributes.ToArray(); // for possible multiple enumeration
var modelMetadata=base.CreateMetadata(attrs, containerType, modelAccessor, modelType, propertyName);
if (containerType == null) return modelMetadata;
var parents =(ParentsAttribute) containerType.GetCustomAttributes(typeof(ParentsAttribute),false).FirstOrDefault(); //search all parents for display attributes that we can inherit
if(parents==null)
return modelMetadata;
var props = from metdataParents in parents.Values
from mProp in metdataParents.GetProperties()
where mProp.CustomAttributes.Any() && mProp.Name == propertyName
select new { parent = metdataParents, mProp };
var sample=props.ToArray();
var q = from metdataParents in parents.Values
from mProp in metdataParents.GetProperties()
where mProp.CustomAttributes.Any() && mProp.Name == propertyName
let da = mProp.GetCustomAttributes(typeof(DisplayAttribute),true).OfType<DisplayAttribute>().FirstOrDefault()
let dna = mProp.GetCustomAttributes(typeof(DisplayNameAttribute),true).OfType<DisplayNameAttribute>().FirstOrDefault()
//don't copy down required, may not be required for the current class or viewmodel
//let ra = mProp.GetCustomAttributes(typeof(RequiredAttribute), true).OfType<RequiredAttribute>().FirstOrDefault()
where da !=null || dna !=null
select new { mProp,da,dna };
foreach (var modifier in q)
{
if (modifier.da != null)
{
//http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/1b78397f32fc#src/System.Web.Mvc/DataAnnotationsModelMetadataProvider.cs
if (string.IsNullOrEmpty(modelMetadata.Description)) modelMetadata.Description = modifier.da.GetDescription();
if (string.IsNullOrEmpty(modelMetadata.ShortDisplayName)) modelMetadata.ShortDisplayName = modifier.da.GetShortName();
if (string.IsNullOrEmpty(modelMetadata.Watermark)) modelMetadata.Watermark = modifier.da.GetPrompt();
modelMetadata.Order = modifier.da.GetOrder() ?? modelMetadata.Order;
if (string.IsNullOrEmpty(modifier.da.GetName()) == false) //only set current object if it is overriding the display
{
modelMetadata.DisplayName = modifier.da.GetName();
} else if (modifier.dna != null)
{
modelMetadata.DisplayName = modifier.dna.DisplayName;
}
}
}
return modelMetadata;
}
}
}
</pre>
And don't forget to hook it up in your <code>Global.asax</code>!
<code>ModelMetadataProviders.Current = new MyModelMetadataProvider();</code>Sir Richard Hoarehttp://www.blogger.com/profile/09089938342974440835noreply@blogger.com0