<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6908201609754153877</id><updated>2025-06-05T03:49:39.213+02:00</updated><category term="C#"/><category term="Visual Studio"/><category term="EPiServer"/><category term="MVC"/><category term="ASP.NET"/><category term="REST"/><category term="WCF"/><category term="CCNET"/><category term="Macros"/><category term="Azure"/><category term="ClickOnce"/><category term="Generics"/><category term="IIS"/><category term="ImageVault"/><category term="Lambda"/><category term="SharePoint"/><category term="Swedish"/><category term="Tfs"/><category term="Transaction"/><category term="Unittest"/><category term="Winforms"/><category term="Debugging"/><category term="Hardware"/><category term="Linq"/><category term="NuGet"/><category term="OLAP"/><category term="Oracle"/><category term="Pipelines"/><category term="Resource manager"/><category term="SQL"/><category term="Sandcastle"/><category term="Vista x64 .Net"/><category term="Windows Media Center"/><category term="WorkItem"/><category term="WorkItemQueries"/><category term="XDT"/><category term="config"/><category term="fun"/><category term="log4net"/><title type='text'>Developing dotnet architect</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default?start-index=26&amp;max-results=25'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>46</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-6192235061662755724</id><published>2020-01-31T06:49:00.002+01:00</published><updated>2020-01-31T06:49:34.277+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Azure"/><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="Pipelines"/><title type='text'>Building strong named assemblies in Azure Pipelines (or solving the MSB3325 error)</title><content type='html'>If you, like me, are using a certificate to sign your assemblies, making them strong named, you will get in trouble when trying to build the project on another machine (or as in this case, using an azure pipeline). MSBuild will throw an error (MSB3325) that looks like below.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Error MSB3325: Cannot import the following key file: mycertificate.pfx. &lt;/pre&gt;
&lt;pre&gt;The key file may be password protected. &lt;/pre&gt;
&lt;pre&gt;To correct this, try to import the certificate again &lt;/pre&gt;
&lt;pre&gt;or manually install the certificate to the Strong Name CSP &lt;/pre&gt;
&lt;pre&gt;with the following key container name: VS_KEY_F287A1878DD02205&lt;/pre&gt;
&lt;h4&gt;
Problem background&lt;/h4&gt;
Certificates (in pfx containers) are password protected and for MSBuild to be able to use them, they must be installed in a key container. (MSBuild uses the &quot;Microsoft Strong Cryptographic Provider&quot;, MSC)&lt;br /&gt;
&lt;br /&gt;
Normally, when selecting a certificate in Visual Studio, you are prompted for the password and Visual studio creates the container for you. When the container is created, you do not need the password as long as the container exists. If you would like to build the project on another machine you will get the error above since the container is not present.&lt;br /&gt;
&lt;h4&gt;
Manual solutions&lt;/h4&gt;
If you go to another machine, one simple way to create the container is to deselect and select the certificate again (in visual studio, in the properties/signing window) which will prompt you for the password.&lt;br /&gt;
Another way is to use sn.exe and pass the certificate and key container name as parameters&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;Sn.exe -i mycertificate.pfx VS_KEY_F287A1878DD02205&lt;/pre&gt;
&lt;br /&gt;
This command will then prompt you for the password and create the container&lt;br /&gt;
&lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/framework/tools/sn-exe-strong-name-tool&quot;&gt;https://docs.microsoft.com/en-us/dotnet/framework/tools/sn-exe-strong-name-tool&lt;/a&gt;&lt;br /&gt;
&lt;h4&gt;
Running on Azure pipelines&lt;/h4&gt;
To be able to build this on Azure Pipelines you need to create the container beforehand.&lt;br /&gt;
One way is to create a self-hosted agent that you install the container on.&lt;br /&gt;
&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/v2-windows?view=azure-devops&quot;&gt;https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/v2-windows?view=azure-devops&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
If you would like to use a Microsoft-hosted agent, then you must include the creation of the container in the pipeline itself. Using sn.exe seems like a promising way.&lt;br /&gt;
&lt;h4&gt;
Automated solutions&lt;/h4&gt;
If we would like to automate the process we have two problems that need solving&lt;br /&gt;
&lt;span style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;1. We need to calculate the name of the container&lt;br /&gt;
&lt;span style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;2. We need to pass the password to the sn.exe tool somehow.&lt;br /&gt;
&lt;br /&gt;
The container name is determined by a class in Microsoft.Build.Tasks.v4.0.dll named ResolveKeySource. It will base the name upon the contents of the certificate, plus the user&#39;s domain and username that are running the MSBuild command. &lt;a href=&quot;https://stackoverflow.com/a/14775877/419294&quot;&gt;kdrapel posted an answer&lt;/a&gt; regarding this on StackOverflow&lt;br /&gt;
&lt;br /&gt;
The other issue, passing the password to the sn.exe tool seemed a bit harder. There were &lt;a href=&quot;https://stackoverflow.com/questions/3138106/auto-entering-password-in-sn-exe&quot;&gt;some examples&lt;/a&gt; where piping it to the exe or writing a wrapper around stdin but none of them worked for me.&lt;br /&gt;
&lt;h4&gt;
SnInstallPfx&lt;/h4&gt;
Thanks to honzajscz, who also had this issue, the &lt;a href=&quot;https://github.com/honzajscz/SnInstallPfx&quot;&gt;GitHub project SnInstallPfx&lt;/a&gt; solved both issues in a nice way. Both calculating the container name and allowing us to enter the password ended us up with a simple command line&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;SnInstallPfx.exe mycertificate.pfx mypassword&lt;/pre&gt;
&lt;h4&gt;
Final solution in Azure Pipelines&lt;/h4&gt;
To include it in the pipeline I used the library function&lt;br /&gt;
https://docs.microsoft.com/en-us/azure/devops/pipelines/library/?view=azure-devops&lt;br /&gt;
&lt;br /&gt;
In the library, I uploaded both the certificate and the sninstallpfx.exe tool.&lt;br /&gt;
The password I added in a variable group.&lt;br /&gt;
&lt;br /&gt;
In the YML I first added the variable group&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;variables:
- group: &#39;My password&#39;
&lt;/pre&gt;
&lt;br /&gt;
Then the files needed to be copied to the target host using DownloadSecureFile task&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;- task: DownloadSecureFile@1
  displayName: Download Pfx
  name: myCertificatePfx
  inputs:
    secureFile: MyCertificate.pfx

- task: DownloadSecureFile@1
  displayName: Download sni
  name: snInstallPfx
  inputs:
    secureFile: SnInstallPfx.exe
&lt;/pre&gt;
&lt;br /&gt;
Then added a PowerShell task where I imported the file paths and password as an environment variable and just invoked the exe&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;- task: PowerShell@2
  env:
    SN_INSTALL_PFX: $(snInstallPfx.secureFilePath)
    MYCERTIFICATE_PFX: $(myCertificatePfx.secureFilePath)
    MYCERTIFICATE_PFX_PASSWORD: $(myCertificatePfxPassword)
  inputs:
    targetType: &#39;inline&#39;
    script: &#39;&amp;amp;&quot;$($ENV:SN_INSTALL_PFX)&quot; &quot;$($ENV:MYCERTIFICATE_PFX)&quot; &quot;$($ENV:MYCERTIFICATE_PFX_PASSWORD)&quot;&#39;
&lt;/pre&gt;
&lt;br /&gt;
After a long research, I was finally able to build the project on a Microsoft hosted pipeline.</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/6192235061662755724/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2020/01/building-strong-named-assemblies-in.html#comment-form' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/6192235061662755724'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/6192235061662755724'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2020/01/building-strong-named-assemblies-in.html' title='Building strong named assemblies in Azure Pipelines (or solving the MSB3325 error)'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-8926851960498674296</id><published>2019-12-02T12:04:00.000+01:00</published><updated>2020-01-31T10:19:13.661+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><title type='text'>Uri.ToString() automatically decodes url encoded characters</title><content type='html'>Uri.ToString() has been discussed quite a lot and its output has not always been consequent.&lt;br /&gt;
It can decode URL encoded characters in the URL, especially in the query part where it will be troublesome.&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;See the article from Anders Abel that discusses the differences between framework 4.0 and framework 4.5 &lt;a href=&quot;https://coding.abel.nu/2014/10/beware-of-uri-tostring/&quot;&gt;https://coding.abel.nu/2014/10/beware-of-uri-tostring/&lt;/a&gt;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
As shown in this article, more changes have been introduced int the newer frameworks.&lt;br /&gt;
&lt;br /&gt;
I have done some testing by setting different targetFramework in the httpRuntime element of web.config.&lt;br /&gt;
&lt;br /&gt;
Tests have been performed by creating a URI with a parameter and iterating all ASCII characters (256) and then calling ToString().&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;
for(var i=0;i&amp;lt;16*16;i++) {
  var enc = $&quot;%{i:x2}&quot;.ToUpper();
  var uri = new Uri($&quot;https://some.site/?key={enc}&quot;);
  uri.ToString();
}&lt;/pre&gt;
&lt;br /&gt;
Tests show that different encodings are being decoded when other encodings are kept and the following table shows the differences between each framework where x marks the characters being decoded and a blank space shows that the encoding is kept even after ToString().&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;table class=&quot;uri-to-string&quot; style=&quot;border-collapse: collapse; border: solid 1px black;&quot;&gt;&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;targetFramework &lt;/th&gt;&lt;th&gt;decoded&lt;/th&gt;&lt;th&gt;Control&lt;br /&gt;
00-1F&lt;/th&gt;&lt;th&gt;&lt;br /&gt;
20&lt;/th&gt;&lt;th&gt;!&lt;br /&gt;
21 &lt;/th&gt;&lt;th&gt;&quot;&lt;br /&gt;
22 &lt;/th&gt;&lt;th&gt;#&lt;br /&gt;
23 &lt;/th&gt;
&lt;th&gt;$&lt;br /&gt;
24 &lt;/th&gt;&lt;th&gt;%&lt;br /&gt;
25 &lt;/th&gt;&lt;th&gt;&amp;amp;&lt;br /&gt;
26 &lt;/th&gt;&lt;th&gt;&#39;()*+,&lt;br /&gt;
27-2C&lt;/th&gt;&lt;th&gt;- &lt;br /&gt;
2D&lt;/th&gt;&lt;th&gt;. &lt;br /&gt;
2E&lt;/th&gt;&lt;th&gt;/ &lt;br /&gt;
2F&lt;/th&gt;
&lt;th&gt;0-9  &lt;br /&gt;
&lt;div&gt;
30-39&lt;/div&gt;
&lt;/th&gt;&lt;th&gt;:&lt;br /&gt;
3A &lt;/th&gt;&lt;th&gt;; &lt;br /&gt;
3B&lt;/th&gt;&lt;th&gt;&amp;lt;&lt;br /&gt;
3C&lt;/th&gt;&lt;th&gt;=&lt;br /&gt;
3D &lt;/th&gt;&lt;th&gt;&amp;gt; &lt;br /&gt;
3E&lt;/th&gt;&lt;th&gt;? &lt;br /&gt;
3F&lt;/th&gt;
&lt;th&gt;@ &lt;br /&gt;
40&lt;/th&gt;&lt;th&gt;A-Z  &lt;br /&gt;
41-5A&lt;/th&gt;&lt;th&gt;[ &lt;br /&gt;
5B&lt;/th&gt;&lt;th&gt;\ &lt;br /&gt;
5C&lt;/th&gt;&lt;th&gt;] &lt;br /&gt;
5D&lt;/th&gt;&lt;th&gt;^_`a-z{\|}~ &lt;br /&gt;
5E-7E  &lt;/th&gt;&lt;th&gt;DEL&lt;br /&gt;
7F&lt;/th&gt;&lt;th&gt;extended&lt;br /&gt;
80-FF&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;4.0             &lt;/td&gt;&lt;td&gt;126 &lt;/td&gt;&lt;td&gt;x   &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x   &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x   &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x   &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x     &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4.5 - 4.7.1  &lt;/td&gt;&lt;td&gt;83 &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x   &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x   &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x   &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x     &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4.7.2 - 4.8  &lt;/td&gt;&lt;td&gt;75 &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;x   &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x   &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;x     &lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;i&gt;The decoded column lists the number of characters that is decoded (of the 256 possible ASCII characters available).&lt;/i&gt;&lt;br /&gt;
&lt;h3&gt;
Summary&lt;/h3&gt;
ToString always decodes&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;%20,%22,%2D,%2E,%2F,%30-%39,%3C,%3E,%41-%5A,%5E-%7E&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;nbsp;&quot;-./0-9&amp;lt;&amp;gt;A-Z^_`a-z{\|}~&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/span&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&lt;i&gt;I&#39;m not sure that I would like the %20 (whitespace) to be decoded but that&#39;s perhaps a personal preference. Of course, it&#39;s readability is improving but for programming use, I&#39;m not a fan.&lt;/i&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
4.5-4.7.1 also decodes&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;%21,%27-%2C,%3A,%5B,%5D&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;!&#39;()*+,:[]&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
4.0 also decodes&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;-%1F,%24,%26,%3B,%3D,%3F,%40,%5C&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;Control characters $&amp;amp;;=?@\&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
4.0 is not good since an embedded query string will be decoded since an encoded &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;&amp;amp;&lt;/span&gt; and &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;=&lt;/span&gt; &lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;(%26,%3D) &lt;/span&gt;in a value will be decoded.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Example&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;https://some.site/?key=key%3Dvalue&lt;/span&gt;&lt;br /&gt;
will be decoded to&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;https://some.site/?key=key=value&lt;/span&gt;&lt;br /&gt;
which will mess up the whole meaning&lt;br /&gt;
&lt;h3&gt;
Workaround&lt;/h3&gt;
If you would like to avoid decoding any URL:s you can use the following extension method.&lt;br /&gt;
This will use the AbsoluteUri when we are dealing with an absolute URI and the OriginalString for relative&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;
public static string ToUrl(this Uri uri)
{
    if (uri == null) return null;
    if (uri.IsAbsoluteUri) return uri.AbsoluteUri;
    return uri.OriginalString;
}
&lt;/pre&gt;
&lt;br /&gt;
&lt;h3&gt;
Conclusion&lt;/h3&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;&lt;b&gt;Don&#39;t use target framework 4.0&lt;/b&gt;, at least switch to 4.5.&lt;/li&gt;
&lt;li&gt;If you want to avoid encoding, &lt;b&gt;don&#39;t use Uri.ToString()&lt;/b&gt;. It will always decode some encoded characters (even if newer frameworks are better att avoiding characters that might get you in trouble).&lt;/li&gt;
&lt;/ol&gt;
</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/8926851960498674296/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2019/12/uritostring-automatically-decodes-url.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/8926851960498674296'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/8926851960498674296'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2019/12/uritostring-automatically-decodes-url.html' title='Uri.ToString() automatically decodes url encoded characters'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-9087042227221881694</id><published>2018-05-02T07:15:00.000+02:00</published><updated>2018-05-02T07:22:15.598+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="config"/><category scheme="http://www.blogger.com/atom/ns#" term="NuGet"/><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio"/><category scheme="http://www.blogger.com/atom/ns#" term="XDT"/><title type='text'>Maintaining config files in your project</title><content type='html'>Have a problem with config files (web.config, app.config) and individual settings in a team or for your building server? Look no further. Check the video below and see if&amp;nbsp;&lt;a href=&quot;https://github.com/meriworks/PowerShell.ConfigBuilder&quot;&gt;ConfigBuilder&lt;/a&gt;&amp;nbsp;can be the solution to your problems. Available as a NuGet package (&lt;a href=&quot;https://www.nuget.org/packages/Meriworks.PowerShell.ConfigBuilder/&quot;&gt;Meriworks.PowerShell.ConfigBuilder&lt;/a&gt;) to install in your project.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; class=&quot;YOUTUBE-iframe-video&quot; data-thumbnail-src=&quot;https://i.ytimg.com/vi/rodNUYriEYI/0.jpg&quot; frameborder=&quot;0&quot; height=&quot;450&quot; src=&quot;https://www.youtube.com/embed/rodNUYriEYI?feature=player_embedded&quot; width=&quot;800&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/9087042227221881694/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2018/05/maintaining-config-files-in-your-project.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/9087042227221881694'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/9087042227221881694'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2018/05/maintaining-config-files-in-your-project.html' title='Maintaining config files in your project'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/rodNUYriEYI/default.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-8854168093935919189</id><published>2018-04-18T13:54:00.000+02:00</published><updated>2020-01-31T10:21:28.887+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="EPiServer"/><category scheme="http://www.blogger.com/atom/ns#" term="SQL"/><title type='text'>Create admin user in episerver after creating new site</title><content type='html'>If you ever create a new Episerver site with a default membership provider and cannot login since you don&#39;t have any current user account in the membership database, use the SQL script below to create a user (tempadmin/tempadmin) that belongs to the WebEditors and WebAdmin groups.&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;&lt;b&gt;&lt;span style=&quot;color: red;&quot;&gt;Disclaimer:&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;
&lt;i&gt;&lt;span style=&quot;color: red;&quot;&gt;The created user will have it&#39;s &lt;b&gt;password &lt;/b&gt;in &lt;b&gt;clear text &lt;/b&gt;in the database so after creating this user, &lt;b&gt;be sure to create a real admin user and delete this one&lt;/b&gt;.&lt;/span&gt;&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;/**
  Will create an application user (epiadmin/epiadmin) and group (webeditors) for an asp.net identity database

**/
DECLARE @aid uniqueidentifier
DECLARE @applicationName nvarchar(235)
DECLARE @uid uniqueidentifier
DECLARE @rolename nvarchar(256)

set @applicationName=N&#39;/&#39;
DECLARE @username nvarchar(50)
DECLARE @password nvarchar(50)
SET @username=N&#39;tempadmin&#39;
SET @password=N&#39;tempadmin&#39;

--create application
IF( NOT EXISTS (SELECT * FROM applications where ApplicationName=@applicationName))
BEGIN
 SET @aid=NEWID()
 insert into applications values(@aid,@applicationName,null)
END

SELECT @aid=ApplicationId FROM applications where ApplicationName=@applicationName
select * from applications

--create user
IF(NOT EXISTS (SELECT * FROM users where ApplicationId=@aid and UserName = @username))
BEGIN
set @uid=NEWID()
 INSERT INTO Users VALUES(@uid,@aid,@username,0,GETDATE())
END
SELECT @uid=UserId FROM Users where ApplicationId=@aid and UserName=@username
select * from users

--create membership data
IF(NOT EXISTS (SELECT * FROM memberships where ApplicationId=@aid and UserId= @uid))
BEGIN
 INSERT INTO Memberships(UserId,ApplicationId,Password,PasswordFormat,PasswordSalt,Email,PasswordQuestion,PasswordAnswer,IsApproved,IsLockedOut,CreateDate,LastLoginDate,LastPasswordChangedDate,LastLockoutDate,FailedPasswordAttemptCount,FailedPasswordAttemptWindowStart,FailedPasswordAnswerAttemptCount,FailedPasswordAnswerAttemptWindowsStart,Comment) 
                  VALUES(@uid,@aid,@password,0,&#39;Tc7EIFB+4dw33bRNOVJTGQ==&#39;,&#39;tempadmin@example.com&#39;,NULL,NULL,1,0,GETDATE(),GETDATE(),GETDATE(),&#39;1754-01-01 00:00:00.000&#39;,0,&#39;1754-01-01 00:00:00.000&#39;,0,&#39;1754-01-01 00:00:00.000&#39;,NULL)
END
select * from memberships

SET @rolename=&#39;WebEditors&#39;
DECLARE @roleid uniqueidentifier

IF(NOT EXISTS (SELECT * from roles where applicationId=@aid and RoleName=@rolename))
BEGIN
 SET @roleid=NEWID()
 INSERT INTO Roles(roleid,applicationid,rolename) 
 values(@roleid,@aid,@rolename)
END
SELECT @roleid=RoleId FROM Roles where ApplicationId=@aid and RoleName=@rolename
select * from roles

IF(NOT EXISTS(SELECT * FROM usersinroles where roleid=@roleid AND userid=@uid))
BEGIN
 INSERT INTO usersinroles(userid,roleid) values(@uid,@roleid)
END
select * from usersinroles

SET @rolename=&#39;WebAdmins&#39;
IF(NOT EXISTS (SELECT * from roles where applicationId=@aid and RoleName=@rolename))
BEGIN
 SET @roleid=NEWID()
 INSERT INTO Roles(roleid,applicationid,rolename) 
 values(@roleid,@aid,@rolename)
END
SELECT @roleid=RoleId FROM Roles where ApplicationId=@aid and RoleName=@rolename
select * from roles

IF(NOT EXISTS(SELECT * FROM usersinroles where roleid=@roleid AND userid=@uid))
BEGIN
 INSERT INTO usersinroles(userid,roleid) values(@uid,@roleid)
END
select * from usersinroles

&lt;/pre&gt;
</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/8854168093935919189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2018/04/create-admin-user-in-episerver-after.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/8854168093935919189'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/8854168093935919189'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2018/04/create-admin-user-in-episerver-after.html' title='Create admin user in episerver after creating new site'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-5825965750844320580</id><published>2017-11-06T07:09:00.000+01:00</published><updated>2020-01-31T10:26:57.097+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Azure"/><title type='text'>Link to resource in Azure portal gives you Access denied?</title><content type='html'>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggCsvIzVj6Tv9GFoEg7hWcxg2WXGE90qSNtslIPlx-GEHTdjTYeT9MdnGZ1Lj2qjd2CA_7aDXlwMWXrf1EG4g6aD8sm0lCa1pvb7DcWMmvJlFuX0-cDoMrUy9wQGh41tWpuWd2-QiFDNhr/s1600/azure_noaccess.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;270&quot; data-original-width=&quot;798&quot; height=&quot;108&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggCsvIzVj6Tv9GFoEg7hWcxg2WXGE90qSNtslIPlx-GEHTdjTYeT9MdnGZ1Lj2qjd2CA_7aDXlwMWXrf1EG4g6aD8sm0lCa1pvb7DcWMmvJlFuX0-cDoMrUy9wQGh41tWpuWd2-QiFDNhr/s320/azure_noaccess.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;Ever needed to create a link to a resource in the Azure portal and when you try it or send it to a colleague, they are faced with an &quot;Access denied&quot; message?&lt;br /&gt;
&lt;br /&gt;
This can happen if the referred resource belongs to a subscription that not belongs to your currently selected directory.&lt;br /&gt;
&lt;br /&gt;
A resource URL looks like the one below where the path to the resource&amp;nbsp;is following the hash sign&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;https://portal.azure.com/#resource/subscriptions/aaaa.../resourceGroups/my/providers/Microsoft.Sql/servers/myserver/databases/mydb/overview&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
A resource belongs to a subscription (subscription&lt;span style=&quot;background-color: white;&quot;&gt; id&amp;nbsp;i&lt;/span&gt;s part of the resource path) and a subscription is connected to a tenant (not part of the resource path).&lt;br /&gt;
&lt;br /&gt;
When you navigate to the link, the currently active directory will be used to access the resource and if the subscription cannot be found in that directory, the &quot;Access denied&quot; message will be shown.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-GY6t1gAq1sP2ZT4HuxKDY4JJXKDDQhyphenhypheni6dz39H7QTNqoHeb35syvHfbFT5y5UGwoj7PZ6ZX4SzfbqRQaOT61Iz-JLvIOEKVZESZUGr3J8pVNz3h1l4eWnE_c81CvEay_tERNOBqjO1R_/s1600/azure_directories.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;365&quot; data-original-width=&quot;205&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-GY6t1gAq1sP2ZT4HuxKDY4JJXKDDQhyphenhypheni6dz39H7QTNqoHeb35syvHfbFT5y5UGwoj7PZ6ZX4SzfbqRQaOT61Iz-JLvIOEKVZESZUGr3J8pVNz3h1l4eWnE_c81CvEay_tERNOBqjO1R_/s320/azure_directories.png&quot; width=&quot;180&quot; /&gt;&lt;/a&gt;To fix this, first, check the name of the directory/tenant that the resource belongs to. (An easy way is to check what directory to use is checking the drop-down menu in the Azure portal.)&lt;br /&gt;
&lt;br /&gt;
You can then create a complete URL on the following path&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;https://portal.azure.com/signin/index/@[directoryName]#[resourcePath]&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;
&lt;br /&gt;
If the resource belongs to the d1.onmicrosoft.com directory, modify the link to go to&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: &amp;quot;courier new&amp;quot; , &amp;quot;courier&amp;quot; , monospace;&quot;&gt;https://portal.azure.com/signin/index/@d1.onmicrosoft.com#resource/subscriptions/aaaa.../resourceGroups/my/providers/Microsoft.Sql/servers/myserver/databases/mydb/overview&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
There you have a fully portable link that will work regardless where you currently are browsing the portal (provided that your account has access to the resource of course).</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/5825965750844320580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2017/11/link-to-resource-in-azure-portal-gives.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/5825965750844320580'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/5825965750844320580'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2017/11/link-to-resource-in-azure-portal-gives.html' title='Link to resource in Azure portal gives you Access denied?'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggCsvIzVj6Tv9GFoEg7hWcxg2WXGE90qSNtslIPlx-GEHTdjTYeT9MdnGZ1Lj2qjd2CA_7aDXlwMWXrf1EG4g6aD8sm0lCa1pvb7DcWMmvJlFuX0-cDoMrUy9wQGh41tWpuWd2-QiFDNhr/s72-c/azure_noaccess.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-6320440509448893899</id><published>2014-12-28T16:19:00.000+01:00</published><updated>2014-12-28T16:28:50.646+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Hardware"/><category scheme="http://www.blogger.com/atom/ns#" term="Swedish"/><title type='text'>Diskmaskin fixad (Whirlpool, IKEA)</title><content type='html'>Hade problem med vår diskmaskin, en inbyggd Whirlpool maskin inköpt från IKEA. När man skulle starta den så började den som vanligt, men efter någon minut så började den pipa och när man öppnade den så blinkade startlampan.&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZMC15Z3UZt9dOZM_8ENLASOgGK80LUsXl4Ju0P6wDCY1AHI8gMwYLyMO6nQjtQVPitsoO8qkQa7RJ9BH9cogv57WViv2DpE9_4v8s3DychDc7uIT0t7GV4NPNAnWQ7Xl5A7vo9uOktPr1/s1600/IMG_2855_cropped.JPG&quot; imageanchor=&quot;1&quot; &gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZMC15Z3UZt9dOZM_8ENLASOgGK80LUsXl4Ju0P6wDCY1AHI8gMwYLyMO6nQjtQVPitsoO8qkQa7RJ9BH9cogv57WViv2DpE9_4v8s3DychDc7uIT0t7GV4NPNAnWQ7Xl5A7vo9uOktPr1/s1600/IMG_2855_cropped.JPG&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;em&gt;Kontrollpanelen för Whirpool DWH B00&lt;/em&gt;&lt;br&gt;
&lt;br&gt;
Efter lite sökande på nätet så visade sig att man kan, utifrån antal blinkningar, utläsa en felkod och i mitt fall var det 11 blinkningar vilket enligt &lt;a href=&quot;http://elektrotanya.com/whirlpool_dwf-b00w.pdf/download.html&quot;&gt;servicemanualen&lt;/a&gt; indikerade på fel &quot;FA WI failure&quot;. Detta fel kan uppstå om vattenindikatorn inte slår på när vattnet spolas in i maskinen eller om det läcker ut vatten i maskinen och flottören slår ifrån. Efter att ha öppnat maskinen så fanns det dock inte något vatten i botten på maskinen så troligtvis så är det vattenindikatorn som inte ger rätt signal.&lt;br&gt;
Vattenindikatorn sitter i botten på maskinen, under filterplåten och ser ut som ett svart litet galler. Enligt några drabbade på internet kunde man lösa detta genom att med en borste eller svamp, tvätta indikatorn från smuts el dyl. I mitt fall hjälpte inte detta något. Felet kvarstod när jag försökte köra maskinen igen.&lt;br&gt;
För att verifiera att det var indikatorn som var problemet så kortslöt jag anslutningen så att den alltid gav en tillslagen signal (kretschemat i servicemanualen är till god hjälp för att förstå). (Spänning i mitten, tillslagen till höger och frånslagen till vänster)&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6d6I9XyUlVR8YUOgqGvVHh4Zvz-9OdlBraAVYsde7AmAlSusv0zR7OkUpBU6JgGSNFdVtcZ5EkegFtvFn1e7jJhkrcyCjEn4DI5wCbuQVd6ZkMtyoMa6X11jodrSlPOo9_5w9fEljQtky/s1600/IMG_2854_edited.JPG&quot; imageanchor=&quot;1&quot; &gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6d6I9XyUlVR8YUOgqGvVHh4Zvz-9OdlBraAVYsde7AmAlSusv0zR7OkUpBU6JgGSNFdVtcZ5EkegFtvFn1e7jJhkrcyCjEn4DI5wCbuQVd6ZkMtyoMa6X11jodrSlPOo9_5w9fEljQtky/s1600/IMG_2854_edited.JPG&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;em&gt;Vattenindikatorn är markerad med WI i kretsschemat, LS6 är flottörens krets. PA6 A är kontakten till vattenindikatorn där 1,2,3 i kretsschemat motsvarar kontaktens anslutningar från v-&gt;h 2,1,3&lt;/em&gt;&lt;br&gt;
&lt;br&gt;
Detta lät maskinens program komma vidare ända till fördisken, men när fördiskens vatten pumpats ut så kommer felet upp igen, då troligtvis för att indikatorn skall slå ifrån.&lt;br&gt;
Dock så räckte fördiskens varma vatten för att lösa upp resterna av smutsen på indikatorn så efter jag kopplat tillbaka anslutningen till indikatorn så fungerade maskinen igen. Nu kanske den lever ett par månader till. En citronsyradisk är nog behövligt snart :)&lt;br&gt;
&lt;br&gt;
Obs! Om du vill mecka med din egna maskin, se till att koppla bort strömmen från maskinen innan du öppnar den.</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/6320440509448893899/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2014/12/diskmaskin-fixad-whirlpool-ikea.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/6320440509448893899'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/6320440509448893899'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2014/12/diskmaskin-fixad-whirlpool-ikea.html' title='Diskmaskin fixad (Whirlpool, IKEA)'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZMC15Z3UZt9dOZM_8ENLASOgGK80LUsXl4Ju0P6wDCY1AHI8gMwYLyMO6nQjtQVPitsoO8qkQa7RJ9BH9cogv57WViv2DpE9_4v8s3DychDc7uIT0t7GV4NPNAnWQ7Xl5A7vo9uOktPr1/s72-c/IMG_2855_cropped.JPG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-540863969531095254</id><published>2014-02-20T13:46:00.001+01:00</published><updated>2014-02-20T13:49:58.271+01:00</updated><title type='text'>Duplicate config sections in web.config and internal server errors...</title><content type='html'>&lt;h2&gt;
There is a duplicate &#39;myApp&#39; section defined, Internal Server Error&lt;/h2&gt;
&lt;div&gt;
Had a breakdown moment today when two sites didn&#39;t behave. Each site had a virtual application with its own web.config.&lt;/div&gt;
&lt;div&gt;
Both sites where configured almost the same but one of the virtual applications worked and not the other.&lt;/div&gt;
&lt;div&gt;
The following error was displayed.&lt;br /&gt;&lt;/div&gt;
&lt;pre&gt;

HTTP Error 500.19 - Internal Server Error

The requested page cannot be accessed because the related configuration data for the page is invalid.

Detailed Error Information:

Module &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IIS Web Core
Notification &amp;nbsp;&amp;nbsp;&amp;nbsp; BeginRequest
Handler &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Not yet determined
Error Code &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0x800700b7
Config Error &amp;nbsp;&amp;nbsp;&amp;nbsp; There is a duplicate &#39;myapp&#39; section defined
Config File &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; \\?\c:\MyApp\vapp\web.config
Requested URL &amp;nbsp;&amp;nbsp; http://myapp:80/vapp/
Physical Path &amp;nbsp;&amp;nbsp; c:\MyApp\
Logon Method &amp;nbsp;&amp;nbsp;&amp;nbsp; Not yet determined
Logon User &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Not yet determined

Config Source:
&amp;nbsp; &amp;nbsp; 7: &amp;nbsp; &amp;lt;configsections&amp;gt;
&amp;nbsp; &amp;nbsp; 8: &amp;nbsp; &amp;nbsp; &amp;lt;section name=&quot;myApp&quot; type=&quot;MyApp.MyConfigSection, MyApp&quot;/&amp;gt;
&amp;nbsp; &amp;nbsp; 9: &amp;nbsp; &amp;lt;/configsections&amp;gt;
&lt;/pre&gt;
&lt;div&gt;
&lt;br /&gt;
This configuration section was present in my site as well, like below.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre&gt;&amp;lt;section name=&quot;myApp&quot; type=&quot;MyApp.MyConfigSection,MyApp&quot;/&amp;gt;&lt;/pre&gt;
&lt;div&gt;
&lt;br /&gt;
Removing the section from the vapp did the trick, but why did it work on the other site?&lt;br /&gt;
&lt;br /&gt;
Finally after some testing and borrowing a second pair of eyes (thx robert), a small difference in the setup was detected. One of the sections where declared with the type &quot;MyApp.MyConfigSection, MyApp&quot; and the other with &quot;MyApp.MyConfigSection,MyApp&quot;. Did you see it?! A small whitespace between the type and assembly names where missing.&lt;br /&gt;
After adjusting the typo to either keeping the whitespace or removing it on both, it started to work...&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/540863969531095254/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2014/02/there-is-duplicate-myapp-section.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/540863969531095254'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/540863969531095254'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2014/02/there-is-duplicate-myapp-section.html' title='Duplicate config sections in web.config and internal server errors...'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-8968744915444778111</id><published>2013-10-14T14:24:00.001+02:00</published><updated>2013-10-14T14:24:49.295+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ASP.NET"/><category scheme="http://www.blogger.com/atom/ns#" term="IIS"/><category scheme="http://www.blogger.com/atom/ns#" term="MVC"/><title type='text'>Kernel mode caching and HttpHandlers</title><content type='html'>Ever tried to get a IHttpHandler delivered data to use the IIS (7) kernel mode cache?&lt;br /&gt;
Ever tried to get fancy and make the handler auto configured (ie managing it programmatically)?&lt;br /&gt;
Well, I tried a lot but gave up.&lt;br /&gt;
&lt;br /&gt;
Basic setup to use IHttpHandler and kernel mode cache&lt;br /&gt;
&lt;br /&gt;
1. To get a IHttpHandler deliver data (like images) and make it use the IIS kernel-mode cache, there are a few points that you must not miss. (&lt;a href=&quot;http://support.microsoft.com/kb/817445&quot;&gt;http://support.microsoft.com/kb/817445&lt;/a&gt;)&lt;br /&gt;
2. You need to register the handler in the system.webServer/handlers section (&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/46c5ddfy(v=vs.100).aspx&quot;&gt;http://msdn.microsoft.com/en-us/library/46c5ddfy(v=vs.100).aspx&lt;/a&gt;)&lt;br /&gt;
3. If you run a routed site (like a MVC site) you must Ignore the route of the handler (else it will not be added to the kernel cache).&lt;br /&gt;
&lt;br /&gt;
Trying to get fancy and using any method below to minimize configuration (avoid step 2 above).&lt;br /&gt;
a. Adding the IHttpHandler using a IRouteHandler (&lt;a href=&quot;http://www.mikesdotnetting.com/Article/126/ASP.NET-MVC-Prevent-Image-Leeching-with-a-Custom-RouteHandler&quot;&gt;http://www.mikesdotnetting.com/Article/126/ASP.NET-MVC-Prevent-Image-Leeching-with-a-Custom-RouteHandler&lt;/a&gt;)&lt;br /&gt;
b. Changing handler after the handler has been calculated (&lt;a href=&quot;http://stackoverflow.com/questions/1888016/any-way-to-add-httphandler-programatically-in-net&quot;&gt;http://stackoverflow.com/questions/1888016/any-way-to-add-httphandler-programatically-in-net&lt;/a&gt;)&lt;br /&gt;
c. Using a IHttpHandlerFactory (same registration as point 2 but if you have several handlers you can configure one factory and redirect to each handler)&lt;br /&gt;
d. Modifying system.webServer/handlers programmatically doesn&#39;t even work. No API, no hacks, no nothing?! &lt;i&gt;Didn&#39;t even find a way to read which handlers were registered in web.config without parsing it manually and backtracking inherited configuration files...&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
None of the fancy methods works (1-3 works but not with kernel cache). Problem is that kernel cache is disabled IF the handler is changed along the way. (&lt;a href=&quot;http://blogs.msdn.com/b/tmarq/archive/2010/04/01/asp-net-4-0-enables-routing-of-extensionless-urls-without-impacting-static-requests.aspx&quot;&gt;http://blogs.msdn.com/b/tmarq/archive/2010/04/01/asp-net-4-0-enables-routing-of-extensionless-urls-without-impacting-static-requests.aspx&lt;/a&gt;)&lt;br /&gt;
&lt;br /&gt;
So I&#39;m back with a standard IHttpHandler, manually registering it in web.config and ignoring routes...&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/8968744915444778111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2013/10/kernel-mode-caching-and-httphandlers.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/8968744915444778111'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/8968744915444778111'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2013/10/kernel-mode-caching-and-httphandlers.html' title='Kernel mode caching and HttpHandlers'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-4592436810177036710</id><published>2013-06-11T23:22:00.000+02:00</published><updated>2013-06-11T23:22:22.952+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="EPiServer"/><category scheme="http://www.blogger.com/atom/ns#" term="ImageVault"/><title type='text'>Using a custom format in ImageVault to specify compression quality in jpg images</title><content type='html'>If you want to request an converted image in ImageVault and also specify the compression quality for a jpeg image you cannot use the LINQ syntax in the C# API of ImageVault. The LINQ engine only wraps some functions in the API and compression quality is not yet added to the mix.&lt;br /&gt;
To do this we need to use another approach and access the service methods directly (like the LINQ parser does internally).&lt;br /&gt;
&lt;br /&gt;
First we need a client.&lt;br /&gt;
&lt;pre class=&quot;csharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;var client = ClientFactory.GetSdkClient();
&lt;/pre&gt;
Then we need to create a format that has the correct parameters.&lt;br /&gt;
&lt;pre class=&quot;csharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;//Compression quality can be set on ImageFormats where 100 is maximum quality
var format = new ImageFormat {
  CompressionQuality = 80,
  MediaFormatOutputType = MediaFormatOutputTypes.Jpeg;
};
format.Effects.Add(new ResizeEffect(200, 200));
&lt;/pre&gt;
Here we created an ImageFormat and specifies the Compression quality to 80 and specify that we want to have a jpeg output image. We can then add other effects to it, like in this case, a Resize effect.&lt;br /&gt;
&lt;br /&gt;
Next step is to create a query that will be used to find the media we are looking for.&lt;br /&gt;
&lt;pre class=&quot;csharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;var q = new MediaItemQuery {
  //we filter out the items we want to retrieve (in this case media with id 485)
  Filter = { Id = new List&amp;lt;int&amp;gt; { 485 } },
  //and supply the format that we want to populate
  Populate = {
    MediaFormats = { format },
    PublishIdentifier = client.PublishIdentifier
  }
};&lt;/pre&gt;
This query will filter out image with id 485 and populate the found MediaItem with the requested formats (in this case the one that we defined above).&lt;br /&gt;
&lt;i&gt;Note that we have not created the format, this will be done automatically (or if a matching format is found, it will be reused). These format are called system formats and is not visible in the UI.&lt;/i&gt;&lt;br /&gt;
We also specify a publish identifier that will generate the urls to the converted media as public urls (no authentication).&lt;br /&gt;
&lt;br /&gt;
This query is then passed to the service method.&lt;br /&gt;
&lt;pre class=&quot;csharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;//first create the media service channel
var mediaService = client.CreateChannel&amp;lt;IMediaService&amp;gt;();
//we then pass the query object to the find method
var mediaItem = mediaService.Find(q).Single();
//and since we only requested one Format, the converted image url will reside 
//in the first MediaConversion of the item.
var url = mediaItem.MediaConversions[0].Url;&lt;/pre&gt;
When all is done we have the url to the converted (and published) jpeg image.&lt;br /&gt;
&lt;i&gt;Note: the client.PublishIdentifier will be set if you are in an EPiServer site or if you configure it in the imagevault.client.config. You can also set this manually.&lt;/i&gt;</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/4592436810177036710/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2013/06/using-custom-format-in-imagevault-to.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/4592436810177036710'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/4592436810177036710'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2013/06/using-custom-format-in-imagevault-to.html' title='Using a custom format in ImageVault to specify compression quality in jpg images'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-6402885402888301126</id><published>2013-05-16T11:32:00.003+02:00</published><updated>2015-11-04T10:01:50.047+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="EPiServer"/><category scheme="http://www.blogger.com/atom/ns#" term="ImageVault"/><title type='text'>Using the REST API of ImageVault</title><content type='html'>In ImageVault 4 we introduced a REST API for the ImageVault Core service. This will show a small example on how to use that.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;
Javascript&lt;/h3&gt;
&lt;div&gt;
This example uses javascript and the javascript classes that are included in the ImageVault installation (in the UI). I will use the scripts that are located on the beta installation but you can use the ones that are on your installation as well.&lt;/div&gt;
&lt;div&gt;
We need three scripts, jquery, json2 and ImageVault.Client. ImageVault.Client handles authentication and CORS logic for the app.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;html:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;&amp;lt;script&amp;nbsp;type=&quot;text/javascript&quot; src=&quot;https://demo2.imagevault.se/ImageVault/Scripts/lib/jquery-1.6.1.js&quot; &amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot; src=&quot;https://demo2.imagevault.se/ImageVault/Scripts/lib/json2.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot; src=&quot;https://demo2.imagevault.se/ImageVault/Scripts/ImageVault.Client.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h3&gt;
Client&lt;/h3&gt;
&lt;div&gt;
To connect to core we need the javascript client.&lt;/div&gt;
&lt;pre class=&quot;jscript:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;var core = new ImageVault.Client({
    core: &quot;https://demo2.imagevault.se&quot;,
    username: &quot;sdkUser&quot;,
    password: &quot;sdkKey&quot;,
    resourceOwnerUsername: &quot;username&quot;,
    resourceOwnerPassword: &quot;password&quot;,
    grant_type:&quot;password&quot;
});
&lt;/pre&gt;
&lt;div&gt;
&lt;i&gt;There are several methods to authenticate to core, this shows the resource owner password credential grant method. See the 
&lt;a href=&quot;http://imagevault.se/en/documentation/api-documentation/?page=rest/authentication/index.html&quot;&gt;authentication documentation&lt;/a&gt; for more information.&lt;/i&gt;
&lt;/div&gt;
&lt;div&gt;
The core client is created using the following parameters
&lt;/div&gt;
&lt;dl&gt;
&lt;dt&gt;core&lt;/dt&gt;
&lt;dd&gt;This is the url to the Core server&lt;/dd&gt;
&lt;dt&gt;username/password&lt;/dt&gt;
&lt;dd&gt;the username and password of the sdkuser.&lt;/dd&gt;
&lt;dt&gt;resourceOwnerUsername/resourceOwnerPassword&lt;/dt&gt;
&lt;dd&gt;the username and password of the user.&lt;/dd&gt;
&lt;dt&gt;grant_type&lt;/dt&gt;
&lt;dd&gt;The authentication mechanism to use. For Resource Owner password credential grant method, the value &quot;password&quot; is used.&lt;/dd&gt;
&lt;/dl&gt;
&lt;h3&gt;
Authentication&lt;/h3&gt;
&lt;div&gt;
We use federated authentication and to be able to call the services you need to have an authentication ticket. To get the ticket you need to authenticate vs the IdentityProvider (Idp). This is handled by the ImageVault.Client script. Also reissuing of tickets when they exipire are handled by the client script.&lt;/div&gt;
&lt;h3&gt;
API calls to Core&lt;/h3&gt;
&lt;div&gt;
To call the REST API we use the json method of the client that performs an authenticated call to the core.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;pre class=&quot;jscript:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;core.json(&quot;MediaService/Find&quot;, {
  Filter: { SearchString: searchString },
  Populate: {
    MediaFormats: [
      //Thumbnail format
      {  
        $type: &quot;ImageVault.Common.Data.ThumbnailFormat,ImageVault.Common&quot;, 
        Effects:[
          {$type:&quot;ImageVault.Common.Data.Effects.ResizeEffect, ImageVault.Common&quot;,Width:200,Height:200,ResizeMode:&#39;ScaleToFill&#39;}
        ]
      }
    ],
    PublishIdentitifier:&quot;http://myserver.com/&quot;
  }
}, function (d) {
  alert(JSON.stringify(d));
});
&lt;/pre&gt;
&lt;div&gt;
The json call uses the following parameters&lt;br /&gt;
&lt;dl&gt;
&lt;dt&gt;path&lt;/dt&gt;
&lt;dd&gt;The service/method to call. Name of the service is the same as the interface (omit the leading I)&lt;/dd&gt;
&lt;dt&gt;arguments&lt;/dt&gt;
&lt;dd&gt;the arguments to the service method as a javascript object. If the method takes multiple arguments, wrap them in a single object (.e {arg1:&#39;test&#39;,arg2:&#39;test2&#39;} )&lt;/dd&gt;
&lt;dt&gt;success callback&lt;/dt&gt;
&lt;dd&gt;The function to call when the service call is done. The argument to the function is the return value of the service method.&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;div&gt;
The services are documented&amp;nbsp;&lt;a href=&quot;http://imagevault.se/DeveloperDocumentation/Reference/html/N_ImageVault_Common_Services.htm&quot; target=&quot;_blank&quot;&gt;at the ImageVault API reference documentation&lt;/a&gt;. The full ImageVault developer documentation can be found at&amp;nbsp;&lt;a href=&quot;http://imagevault.se/doc&quot; target=&quot;_blank&quot;&gt;http://imagevault.se/doc&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
You can test a fully fledged demo below that uses the ImageVault demo site scripts and content that performs a search and displays the thumbnail media&lt;/div&gt;
&lt;!--DEMO starts here--&gt;
&lt;script src=&quot;http://demo2.imagevault.se/ImageVault/Scripts/lib/jquery-1.6.1.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;http://demo2.imagevault.se/ImageVault/Scripts/lib/json2.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;http://demo2.imagevault.se/ImageVault/Scripts/ImageVault.Client.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
$(document).ready(function () {
  var core = new ImageVault.Client({
    core: &quot;https://demo2.imagevault.se&quot;,
    username: &quot;OfficeConnect&quot;,
    password: &quot;5OtBmAM#vfdu&quot;,
    resourceOwnerUsername: &quot;Office&quot;,
    resourceOwnerPassword: &quot;ImageVault&quot;,
    grant_type:&quot;password&quot;
  });
  //trigger enter on search form
  $(&quot;#coreSearchString&quot;).keyup(function(event){
    if(event.keyCode == 13){
      $(&quot;#coreSearch&quot;).click();
    }
  });
  $(&quot;#coreSearch&quot;).click(function () {
    var searchString = $(&quot;#coreSearchString&quot;).val();
    $(&quot;#searchResultInfo&quot;).text(&quot;searching for &quot; + searchString);
    $(&quot;#searchResult&quot;).text(&quot;&quot;);
    core.json(&quot;MediaService/Find&quot;, {
      Filter: { SearchString: searchString },
      Populate: {
        MediaFormats: [
          //Thumbnail format
          {  
            $type: &quot;ImageVault.Common.Data.ThumbnailFormat,ImageVault.Common&quot;, 
            Effects:[{$type:&quot;ImageVault.Common.Data.Effects.ResizeEffect, ImageVault.Common&quot;,Width:200,Height:200,ResizeMode:&#39;ScaleToFill&#39;}]
          }
        ],
        PublishIdentifier:&quot;http://demo.imagevault.se/&quot;
      }    
    }, function (d) {
      $(&quot;#searchResultInfo&quot;).text(&quot;Found &quot; + d.length + &quot; hits.&quot;);
      for (var i = 0; i &lt; d.length; i++) {
        var item = d[i];
        var thumbnail = item.MediaConversions[0];
        $(&quot;#searchResult&quot;).append(&quot;&lt;div style=&#39;float:left;text-align:center;&#39;&gt;&lt;img src=&#39;&quot; + thumbnail.Url + &quot;&#39;/&gt;&lt;br/&gt;&quot;+item.Name+&quot;&lt;/div&gt;&quot;);
      }
    });
  });
});
&lt;/script&gt;
&lt;br /&gt;
&lt;h1&gt;
Demo&lt;/h1&gt;
&lt;input id=&quot;coreSearchString&quot; type=&quot;text&quot; value=&quot;e1&quot; /&gt;
  &lt;button id=&quot;coreSearch&quot;&gt;Search media&lt;/button&gt;
  &lt;br /&gt;
&lt;div id=&quot;searchResultInfo&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;searchResult&quot;&gt;
&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/6402885402888301126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2013/05/using-rest-api-of-imagevault.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/6402885402888301126'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/6402885402888301126'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2013/05/using-rest-api-of-imagevault.html' title='Using the REST API of ImageVault'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>2</thr:total><georss:featurename>Kalmar, Sverige</georss:featurename><georss:point>56.688694417591648 16.364908218383789</georss:point><georss:box>56.686514417591646 16.359865718383791 56.690874417591651 16.369950718383787</georss:box></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-537153118519782603</id><published>2013-02-26T15:10:00.000+01:00</published><updated>2013-02-26T18:59:19.384+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="ASP.NET"/><category scheme="http://www.blogger.com/atom/ns#" term="EPiServer"/><title type='text'>Keeping configuration transforms after applying EPiServer 7 Patch</title><content type='html'>When installing the &lt;a href=&quot;http://world.episerver.com/Articles/Items/EPiServer-7---Patch-1/&quot;&gt;patch 1 for EPiServer CMS 7&lt;/a&gt; the &lt;a href=&quot;http://world.episerver.com/Documentation/Items/Installation-Instructions/Installing-EPiServer-Patches/&quot;&gt;installation instructions&lt;/a&gt; stated that you needed to turn of the msbuild build tasks that created the configuration files from different xml fragments using xml transform.&lt;br /&gt;
The patch also noted that you needed to manually replace the assembly redirect assembly versions directly in the web.config file.&lt;br /&gt;
&lt;br /&gt;
Since I have grown quite fond of using the xml fragments to apply configuration changes for our entire development team I found a way to apply the patch changes by a simple xml transform.&lt;br /&gt;
Just edit the [Configuration]/Common/Web.Common.config and add the redirects that you need to fix.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;
&amp;lt;configuration xmlns:xdt=&quot;http://schemas.microsoft.com/XML-Document-Transform&quot; xmlns:asm=&quot;urn:schemas-microsoft-com:asm.v1&quot;&amp;gt;
  &amp;lt;runtime&amp;gt;
    &amp;lt;assemblyBinding xmlns=&quot;urn:schemas-microsoft-com:asm.v1&quot;&amp;gt;
      &amp;lt;dependentAssembly xdt:Transform=&quot;Replace&quot; xdt:Locator=&quot;Condition(asm:assemblyIdentity/@name=&#39;EPiServer.Framework&#39;)&quot;&amp;gt;
        &amp;lt;assemblyIdentity name=&quot;EPiServer.Framework&quot; publicKeyToken=&quot;8fe83dea738b45b7&quot; culture=&quot;neutral&quot; /&amp;gt;
        &amp;lt;bindingRedirect oldVersion=&quot;1.0.0.0-7.0.859.4&quot; newVersion=&quot;7.0.859.4&quot; /&amp;gt;
      &amp;lt;/dependentAssembly&amp;gt;
    &amp;lt;/assemblyBinding&amp;gt;
  &amp;lt;/runtime&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/pre&gt;
&lt;br /&gt;
The key here is that we replace the original assembly redirect with the new binding (with the new version number) 
and use a Locator attribute to find the correct node in the original web.config file to replace.&lt;br /&gt;
&lt;br /&gt;
The tricky part in this case is that the assemblyBinding changes the default namespace. To get a correct xpath we need to use this namespace to locate the child node. To use a namespace in an xpath expression we first need to register it with a prefix (here using asm) in the root node (or somewhere in scope). We can then build a correct xpath expression (asm:assemblyIdentity/@name) to locate the node.&lt;br /&gt;
&lt;br /&gt;
&lt;strike&gt;I will follow up with a final post stating all adjustments needed to get this to work.&lt;/strike&gt;&lt;br /&gt;
&lt;strike&gt;&lt;br /&gt;&lt;/strike&gt;
&lt;b&gt;Update/follow up&lt;/b&gt;&lt;br /&gt;
I completed the adjustments for both the Framework and CMS packages and collected the needed transforms in the following file.&lt;br /&gt;
&lt;a href=&quot;https://dl.dropbox.com/u/10119568/EPiServerCms7Patch1/web.common.config&quot;&gt;https://dl.dropbox.com/u/10119568/EPiServerCms7Patch1/web.common.config&lt;/a&gt;&lt;br /&gt;
&lt;span style=&quot;background-color: white; font-family: Georgia, serif; font-size: 13px; line-height: 20.796875px;&quot;&gt;The installation instructions also stated that other assemblies could be present (in my case there wasn&#39;t) but if that&#39;s so in your case, just add those transformations to the list.)&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;background-color: white; font-family: Georgia, serif; font-size: 13px; line-height: 20.796875px;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;background-color: white; font-family: Georgia, serif; font-size: 13px; line-height: 20.796875px;&quot;&gt;&lt;i&gt;Note: I needed to remove the old dll:s from the [Libraries] folder in the Alloy template or my VisualStudio didn&#39;t get the hint path correctly.&lt;/i&gt;&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/537153118519782603/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2013/02/keeping-configuration-transforms-after.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/537153118519782603'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/537153118519782603'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2013/02/keeping-configuration-transforms-after.html' title='Keeping configuration transforms after applying EPiServer 7 Patch'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>0</thr:total><georss:featurename>Kalmar, Sverige</georss:featurename><georss:point>56.6634447 16.35677899999996</georss:point><georss:box>56.523915699999996 16.034055499999962 56.8029737 16.679502499999959</georss:box></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-2283046007214772785</id><published>2011-06-24T01:00:00.008+02:00</published><updated>2020-01-31T10:16:03.567+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="REST"/><category scheme="http://www.blogger.com/atom/ns#" term="WCF"/><title type='text'>Supporting Cross Origin Resource Sharing (CORS) requests in a WCF Rest service - Part 4</title><content type='html'>&lt;div&gt;
For background information, see &lt;a href=&quot;http://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource.html&quot;&gt;part 1&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
For message inspection code, see &lt;a href=&quot;http://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource_23.html&quot;&gt;part 2&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
For operation dispatcher code, see &lt;a href=&quot;http://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource_24.html&quot;&gt;part 3&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
To include the message inspection in our wcf service for our service we need to bind it to our service. This is done using behaviors. To bind the IDispatchMessageInspector that works on the ServiceEndpoint level we need to implement an IEndpointBehavior and assign it to our endpoint. &lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
To bind the IOperationInvoker and IDispatchMessageFormatter we need to implement the IOperationBehavior that works on the operation level.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
This class also works as an attribute so you can decorate your contract and operations with it if need be. It also contains some properties if you want to control the header values.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/dhvik/Wcf-cors-behavior/blob/master/CorsBehaviorAttribute.cs&quot;&gt;https://github.com/dhvik/Wcf-cors-behavior/blob/master/CorsBehaviorAttribute.cs&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div&gt;
Assigning can be done in many ways. By declarations on the contract (attribute), configuration in the applications configuration file or direct using code. I&#39;m using code since I host the services in a windows service.&lt;/div&gt;
&lt;pre class=&quot;brush:csharp&quot; name=&quot;code&quot;&gt;var host = new WebServiceHost2(serviceType, false,
new Uri(serviceBaseUrl + serviceName));
var endpoint = host.AddServiceEndpoint(contractType,
new WebHttpBinding(WebHttpSecurityMode.None), &quot;&quot;);
//add support for cors (both for the endpoint to detect and create reply)
endpoint.Behaviors.Add(new CorsBehaviorAttribute());

foreach (var operation in endpoint.Contract.Operations) {
//add support for cors (and for operation to be able to not
//invoke the operation if we have a preflight cors request)
operation.Behaviors.Add(new CorsBehaviorAttribute());
}&lt;/pre&gt;
When we now have all pieces in place we don&#39;t need to bother about cross domain requests. Our endpoint and operation behaviors handles this and we don&#39;t need to litter our service code with things that don&#39;t belong there anyway.</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/2283046007214772785/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource_9123.html#comment-form' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/2283046007214772785'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/2283046007214772785'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource_9123.html' title='Supporting Cross Origin Resource Sharing (CORS) requests in a WCF Rest service - Part 4'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-1079613362748054917</id><published>2011-06-24T00:37:00.012+02:00</published><updated>2011-06-24T02:22:18.373+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="REST"/><category scheme="http://www.blogger.com/atom/ns#" term="WCF"/><title type='text'>Supporting Cross Origin Resource Sharing (CORS) requests in a WCF Rest service - Part 3</title><content type='html'>&lt;div&gt;For background information, see &lt;a href=&quot;http://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource.html&quot;&gt;part 1&lt;/a&gt;&lt;/div&gt;&lt;div&gt;For message inspection code, see &lt;a href=&quot;http://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource_23.html&quot;&gt;part 2&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When a cors request is identified we now have the CorsState object available on our operation level and can act upon this. To be able to skip calling the service method in case of a preflight request we need to implement two extension points in the operation level. Message formatting and operation invocation. Since the preflight request won&#39;t have the correct requests message format, we skip the deserializing process and  the message using the normal deserializator can results in errors. So in that case we skip both serialization and deserialization.&lt;/div&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/dhvik/Wcf-cors-behavior/blob/master/CorsFormatter.cs&quot;&gt;https://github.com/dhvik/Wcf-cors-behavior/blob/master/CorsFormatter.cs&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;When it comes to the IOperationInvoker we just skip invoking the operation if we have a preflight request. All other requests uses the original invoker.&lt;/div&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/dhvik/Wcf-cors-behavior/blob/master/CorsInvoker.cs&quot;&gt;https://github.com/dhvik/Wcf-cors-behavior/blob/master/CorsInvoker.cs&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;In the &lt;a href=&quot;http://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource_9123.html&quot;&gt;part 4&lt;/a&gt; we will see how we bind all these extension points together for our service.&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/1079613362748054917/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource_24.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/1079613362748054917'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/1079613362748054917'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource_24.html' title='Supporting Cross Origin Resource Sharing (CORS) requests in a WCF Rest service - Part 3'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-9125331008898546922</id><published>2011-06-23T23:55:00.006+02:00</published><updated>2011-06-24T02:15:08.241+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="REST"/><category scheme="http://www.blogger.com/atom/ns#" term="WCF"/><title type='text'>Supporting Cross Origin Resource Sharing (CORS) requests in a WCF Rest service - Part 2</title><content type='html'>For a background in the subject please see &lt;a href=&quot;http://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource.html&quot;&gt;part 1&lt;/a&gt;.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To handle &lt;a href=&quot;http://www.w3.org/TR/access-control/&quot;&gt;Cors&lt;/a&gt; requests we can take advantage of the extensibility mechanism that is embedded in WCF. There are many extension points that we can use and the cors problem can be issued in more than one way.&lt;/div&gt;&lt;div&gt;There is a &lt;a href=&quot;http://msdn.microsoft.com/en-us/magazine/cc163302.aspx&quot;&gt;great article&lt;/a&gt;, written by Aaron Skonnard, published in the Dec 2007 issue of MSDN magazine, that covers many parts of this mechanism that I can recommend for further reading.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Our main problem is that we need to analyse the incoming request and determine if it is a cors preflight request, cors normal request or a normal request. Preflight request should not reach the service method at all since we don&#39;t want to invoke the service method. The other requests should invoke the method but for all cors responses we should add extra headers.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I found a reply by Carlos Figueira in a &lt;a href=&quot;http://social.msdn.microsoft.com/Forums/eu/wcf/thread/55ef7692-25dc-4ece-9dde-9981c417c94a&quot;&gt;msdn thread&lt;/a&gt; that solves the &quot;analyse message and skip invoking the service method&quot;-part that I used as a start. (as a side note Richard Blewett stated (in the same thread) that we could intercept the message before it reaches the dispatcher by writing a protocol channel. I will leave that excercise to the reader or for another day.)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;a onblur=&quot;try {parent.deselectBloggerImageGracefully();} catch(e) {}&quot; href=&quot;http://i.msdn.microsoft.com/cc163302.fig03%28en-us%29.gif&quot;&gt;&lt;img style=&quot;float:right; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 400px; height: 305px;&quot; src=&quot;http://i.msdn.microsoft.com/cc163302.fig03%28en-us%29.gif&quot; alt=&quot;&quot; border=&quot;0&quot; /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;So to analyse the incoming message we use the first avaliable extension point in the dispatcher (server) that would be the Message inspection stage.&lt;/div&gt;&lt;div&gt;To use this extension point we implement an IDispatchMessageInspector. In this stage we are at the service endpoint and haven&#39;t decided wich method of the service that we should call. What we do here is that we look for a cors request (checking if the Origin header is present). If so we create a Cors state object and adds a property to the message so we can use this later on in other extension points.&lt;/div&gt;&lt;div&gt;If it is a preflight request we also create a response message that we should use.&lt;/div&gt;&lt;div&gt;When the message inspector receives the reply we add the headers (and if it was a preflight request, we replace the whole response. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In part 3 we will continue to the operation level where we check the property and omits the call to the service method in case of a preflight request.&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/dhvik/Wcf-cors-behavior/blob/master/CorsDispatchMessageInspector.cs&quot;&gt;View code on github &lt;/a&gt;&lt;a href=&quot;https://github.com/dhvik/Wcf-cors-behavior/blob/master/CorsDispatchMessageInspector.cs&quot;&gt;https://github.com/dhvik/Wcf-cors-behavior/blob/master/CorsDispatchMessageInspector.cs&lt;/a&gt;&lt;a href=&quot;https://github.com/dhvik/Wcf-cors-behavior/blob/master/CorsDispatchMessageInspector.cs&quot;&gt; &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In &lt;a href=&quot;http://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource_24.html&quot;&gt;part 3&lt;/a&gt; we will see how we handle things on the operation level.&lt;br /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/9125331008898546922/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource_23.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/9125331008898546922'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/9125331008898546922'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource_23.html' title='Supporting Cross Origin Resource Sharing (CORS) requests in a WCF Rest service - Part 2'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-8726878672616195820</id><published>2011-06-23T23:43:00.008+02:00</published><updated>2011-06-24T01:29:07.930+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="REST"/><category scheme="http://www.blogger.com/atom/ns#" term="WCF"/><title type='text'>Supporting Cross Origin Resource Sharing (CORS) requests in a WCF Rest service - Part I</title><content type='html'>&lt;p&gt;If you use try to consume a rest service using JavaScript (jquery) and are using chrome or Firefox (newer versions) you might run into cross domain issues that involves the &lt;a href=&quot;http://www.w3.org/TR/access-control/&quot;&gt;w3c standard&lt;/a&gt; for accessing resources on other domains (or ports).&lt;/p&gt;&lt;div&gt;In short, if you try to request a resource on a page using javascript(call a rest service) that is located on another domain/port then the XMLHttpRequest object that is implemented in the browser will first try to discover if the cross-origin resource is prepared/allows to accept requests from the origin. If this preflight request succeeds then the real request will be fired.&lt;br /&gt;All this is handled by the browsers XMLHttpRequest object so from the JavaScript side this don&#39;t affect the code.&lt;br /&gt;&lt;p&gt;The server side code must support  cors though.&lt;/p&gt;&lt;p&gt;An example: we take a simple service method that only returns a string.&lt;/p&gt;&lt;pre class=&quot;csharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;public string Hello() {&lt;br /&gt; return &quot;Hello!&quot;;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;If we want to allow cors requests, then we must detect the preflight request and reply accordingly and if it is the real request we should perform the actual method.&lt;/p&gt;&lt;pre class=&quot;csharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;public string Hello(){&lt;br /&gt; //for all cors requests&lt;br /&gt; WebOperationContext.Current.OutgoingResponse.Headers&lt;br /&gt;     .Add(&quot;Access-Control-Allow-Origin&quot;,&quot;*&quot;);&lt;br /&gt; //identify preflight request and add extra headers&lt;br /&gt; if (WebOperationContext.Current.IncomingRequest.Method == &quot;OPTIONS&quot;) {&lt;br /&gt;      WebOperationContext.Current.OutgoingResponse.Headers&lt;br /&gt;          .Add(&quot;Access-Control-Allow-Methods&quot;, &quot;POST, OPTIONS, GET&quot;);&lt;br /&gt;      WebOperationContext.Current.OutgoingResponse.Headers&lt;br /&gt;          .Add(&quot;Access-Control-Allow-Headers&quot;,&lt;br /&gt;               &quot;Content-Type, Accept, Authorization, x-requested-with&quot;);&lt;br /&gt;      return null;&lt;br /&gt;  }&lt;br /&gt;  return &quot;Hello!&quot;;&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Basically we first add the Access-Control-Allow-Origin header telling that we allow any origins (we can also specify an origin that matches the origin that the request comes from). Then we check if the request is a preflight request (method is OPTIONS). If it is we add extra headers to declare which methods and headers that we allow the real request to contain. There are a few more access-control headers that we can add if we need and these are described in the &lt;a href=&quot;http://www.w3.org/TR/access-control/&quot;&gt;w3c spec.&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;To add this code in every method is not a great solution but in &lt;a href=&quot;http://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource_23.html&quot;&gt;part 2&lt;/a&gt; we will see how we can use WCF extensibility to do this in a more elegant way.&lt;/p&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/8726878672616195820/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/8726878672616195820'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/8726878672616195820'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2011/06/supporting-cross-origin-resource.html' title='Supporting Cross Origin Resource Sharing (CORS) requests in a WCF Rest service - Part I'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-3155579258068970995</id><published>2011-01-02T02:24:00.002+01:00</published><updated>2011-01-02T02:30:06.096+01:00</updated><title type='text'>Alpha version of IOU tracker</title><content type='html'>Have been experimenting with .net4, MVC2 and EntityFramework. The result is a IOU tracker that is available at &lt;a href=&quot;http://iou.nu&quot;&gt;iou.nu&lt;/a&gt;.&lt;br /&gt;Service is free of charge and feel free to try it out.&lt;br /&gt;&lt;br /&gt;I know there are several other iou trackers out there, but since the most lack some features and I needed to dig deeper into net4/mvc2/EF anyway so why not give it a try!?&lt;br /&gt;&lt;br /&gt;/Dan</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/3155579258068970995/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2011/01/alpha-version-of-iou-tracker.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/3155579258068970995'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/3155579258068970995'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2011/01/alpha-version-of-iou-tracker.html' title='Alpha version of IOU tracker'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-3558034084517605127</id><published>2010-06-09T12:04:00.004+02:00</published><updated>2010-06-09T12:18:37.586+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="OLAP"/><category scheme="http://www.blogger.com/atom/ns#" term="Tfs"/><category scheme="http://www.blogger.com/atom/ns#" term="WorkItem"/><title type='text'>TF53010: Errors in the metadata manager when rebuilding OLAP cube</title><content type='html'>I was adding new fields to our TFS workitems the other day and wanted them to be part of the olap cube. But when I added them (marked them with the reportable attribute) and processed the cube, i couldn&#39;t see them in the cube.&lt;br /&gt;I went reading the Event log on the Data tier of the TFS and found log entries like below&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;TF53010: The following error has occurred in a Team Foundation component or extension:&lt;br /&gt;Date (UTC): 2010-06-09 09:31:26&lt;br /&gt;Machine: MyTfsServer&lt;br /&gt;Application Domain: /LM/W3SVC/830720315/Root/Warehouse-3-129205184075330719&lt;br /&gt;Assembly: Microsoft.TeamFoundation.Warehouse, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a; v2.0.50727&lt;br /&gt;Process Details:&lt;br /&gt; Process Name: w3wp&lt;br /&gt; Process Id: 9316&lt;br /&gt; Thread Id: 8484&lt;br /&gt; Account name: DOMAIN\admin&lt;br /&gt;&lt;br /&gt;Detailed Message: The pending configuration changes were not successfully added to&lt;br /&gt;the cube because of the following error: Microsoft.AnalysisServices.OperationException: &lt;br /&gt;XML parsing failed at line 1, column 0: A document must contain exactly one root element.&lt;br /&gt;.&lt;br /&gt;Errors in the metadata manager. An error occurred when instantiating a metadata &lt;br /&gt;object from the file, &#39;\\?\C:\Program Files\Microsoft SQL Server\MSSQL.3\OLAP\Data&lt;br /&gt;\TfsWarehouse.0.db\Team System.63837.cub.xml&#39;.&lt;br /&gt;&lt;br /&gt;  at Microsoft.AnalysisServices.AnalysisServicesClient.SendExecuteAndReadResponse(ImpactDetailCollection impacts, Boolean expectEmptyResults, Boolean throwIfError)&lt;br /&gt;  at Microsoft.AnalysisServices.AnalysisServicesClient.Alter(IMajorObject obj, ObjectExpansion expansion, ImpactDetailCollection impact, Boolean allowCreate)&lt;br /&gt;  at Microsoft.AnalysisServices.Server.Update(IMajorObject obj, UpdateOptions options, UpdateMode mode, XmlaWarningCollection warnings, ImpactDetailCollection impactResult)&lt;br /&gt;  at Microsoft.AnalysisServices.Server.SendUpdate(IMajorObject obj, UpdateOptions options, UpdateMode mode, XmlaWarningCollection warnings, ImpactDetailCollection impactResult)&lt;br /&gt;  at Microsoft.AnalysisServices.MajorObject.Update(UpdateOptions options, UpdateMode mode, XmlaWarningCollection warnings)&lt;br /&gt;  at Microsoft.AnalysisServices.MajorObject.Update(UpdateOptions options)&lt;br /&gt;  at Microsoft.TeamFoundation.Warehouse.OlapCreator.CreateOlap(WarehouseConfig whConf, String accessUser, String[] dataReaderAccounts, Boolean dropDB, Boolean processCube)&lt;br /&gt;  at Microsoft.TeamFoundation.Warehouse.AdapterScheduler.EnsureCubeIsUpToDate()&lt;br /&gt;&lt;br /&gt;For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I searched the path in the error message above (on the database server, little bit confusing since the path seems to be a local one, but since the error originates from the database server it makes sense).&lt;br /&gt;&lt;br /&gt;I the folder i found several files named Team System.xxxx.cub.xml (where xxxx is digits) and some where 0 bytes long and some contained error xml (seemed to be cut in the middle of writing).&lt;br /&gt;I removed these files and processed the cube again. (I used the Warehousecontroller webservice on the tfs server, https://mytfsserver:8143/Warehouse/v1.0/WarehouseController.asmx?op=Run)&lt;br /&gt;&lt;br /&gt;This time the processing went through without any problems and the fields apperared in my cube.&lt;br /&gt;&lt;br /&gt;I wonder how these error xml files where created, perhaps if the cube was instructed to reprocess while the iis was restarted or for some other reason.&lt;br /&gt;I&#39;ll better check the folder once and a while....</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/3558034084517605127/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2010/06/tf53010-errors-in-metadata-manager-when.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/3558034084517605127'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/3558034084517605127'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2010/06/tf53010-errors-in-metadata-manager-when.html' title='TF53010: Errors in the metadata manager when rebuilding OLAP cube'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-2479008046459397958</id><published>2010-05-21T00:51:00.016+02:00</published><updated>2010-06-18T23:31:59.244+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="CCNET"/><title type='text'>Writing a custom trigger for CruiseControl.Net</title><content type='html'>&lt;p&gt;I’ve had some problems regarding the triggering of our nightly builds and has not been able to solve this with the standard triggers that comes with ccnet (using v1.4.4). So finally I took some time (night time, kids are sleeping and there is peace and quiet in the house :) and wrote a trigger that fixed the issue at hand. I will come to the trigger in just a moment but I’ll take some time to explain our ccnet setup.&lt;/p&gt;&lt;h3&gt;Interval builds&lt;/h3&gt;&lt;p&gt;All our projects are built using CruiseControl.Net and for each project we setup two builds. One interval build that triggers when the source has been changed. This build uses the intervalTrigger that checks the source repository at regular intervals. The interval build compiles the project and performs unit testing and provides feedback to the programmer. This is quite standard setup I guess so there’s no rocket science here…&lt;/p&gt;&lt;h3&gt;Nightly builds&lt;/h3&gt;&lt;p&gt;The other build is a nightly build that (in addition to the tasks for the interval build) performs analysis, generates documentation and installation packages ready to deploy to a target system. It also creates deliverable folder in a specific ”drop zone” for the project. This folder is named using the label of the nightly build.&lt;/p&gt;&lt;h3&gt;Labels&lt;/h3&gt;&lt;p&gt;All projects uses the project name and build no as label (like MyProject – 234). We reuse the build number of the interval build for the nightly build so if the last interval build was ”MyProject – 234”, the next time the nightly build is triggered the label for the nightly build is  ”MyProject-Nightly – 234”. This creates a side effect that if the nightly build is triggered twice and the interval build hasn’t been triggered, the label is the same. If you remember the drop zone folder it uses the label of the nightly build as a name so if it is triggered twice using the same label, it tries to overwrite the drop zone folder. &lt;/p&gt;This is a big no-no and to solve this I added a nant task that fires in the beginning of the nightly build that fails if the drop zone folder exists.&lt;br /&gt;&lt;h3&gt;Triggering the nightly build&lt;/h3&gt;&lt;p&gt;So to avoid failing the nightly build, it should only be triggered if an interval build has successfully been completed and the nightly build for that interval build hasn’t been performed.&lt;/p&gt;&lt;p&gt;Normally nightly triggers are defined to run at a specific time every night using a ScheduleTrigger that fires at that time if any changes to the source has been made during the day. In combination with a projectTrigger that only fires if the interval build has been successful we can accomplish the trigger to fire only if the interval is successful.&lt;/p&gt;&lt;h3&gt;Problem with projectTrigger&lt;/h3&gt;&lt;p&gt;There is a limitation to the projectTrigger that affects our setup severely. It doesn’t remember the project status after the ccnet server has restarted. This causes the nightly projects to be rebuilt after a server restart. Since we have lots of projects on the server, we need to restart it atleast once a week and this causes the nightly builds to fail.&lt;/p&gt;&lt;h3&gt;New trigger?&lt;/h3&gt;&lt;p&gt;To solve this we need another way to trigger the build. The first solution that comes to mind is if there was a trigger that could check if a specific folder (the drop zone folder in this case) was missing, we could replace the projectTrigger with a ”Missing folder trigger”.&lt;/p&gt;&lt;p&gt;Perhaps could look like this.&lt;/p&gt;&lt;pre class=&quot;xml:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;&amp;lt;missingFolderTrigger path=&quot;Z:\PublishedBuilds\MyProject-Nightly\1.0.0.234&quot;/&amp;gt;&lt;/pre&gt;Problem here is that we don’t know the last folder name. The ”1.0.0.234”comes from the file version for our compiled assemblies.&lt;br /&gt;The first part of the version number can be any numbers and the 234 refers to the build number of the project. This changes for each build so we need a way to look this up.&lt;br /&gt;&lt;h3&gt;Final design&lt;/h3&gt;I speak highly of the KISS principle, but I’m also great at creating generic things and sometimes this ”generic gene” gets some overhand (and sometimes also get way out of hand, but I think I found a good level this time :)&lt;br /&gt;So the final design was a trigger syntax like below:&lt;br /&gt;&lt;pre class=&quot;xml:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;&amp;lt;fileExistsTrigger&lt;br /&gt; triggerOnMissing=&quot;true&quot;&lt;br /&gt; seconds=&quot;30&quot;&lt;br /&gt; path=&quot;Z:\PublishedBuilds\MyProject-Nightly\&quot;&lt;br /&gt; match=&quot;\d+\.\d+\.\d+\.[Regex.Match([Projects(MyProject).LastSuccessfulBuildLabel],\d+$)]&quot;/&amp;gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;This trigger is now a more ”generic” one allowing us to trigger on both files and directories.&lt;/li&gt;&lt;li&gt;The ”triggerOnMissing” allows us to trigger on both exists and not exists.&lt;/li&gt;&lt;li&gt;It’s based on an interval trigger and the ”seconds” parameter allows us to set on how often a file/directory should be checked.&lt;/li&gt;&lt;li&gt;The ”path” allows us to set the path that should be checked. If we only need to check a file/directory that we know the name of, this is enough. End with a \ to check a directory.&lt;/li&gt;&lt;/ul&gt;&lt;h4&gt;The match attribute&lt;/h4&gt;The ”match” allows us to perform a regular expression search that searches for a match for a directory/file in the supplied path.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The \d+ will match one or more digits &lt;/li&gt;&lt;li&gt;The \. will match a dot.&lt;/li&gt;&lt;li&gt;Repeating this match three times \d+\.\d+\.\d+\. will match the ”1.0.0.” part of the file (or any other valid version major.minor.build. combo)&lt;/li&gt;&lt;/ul&gt;Now the fun part begins :). I added some syntax to perform string operations and replacements. Any valid command is surrounded by [] and are replaced before the file/directory regex matching begins.&lt;br /&gt;In this example we have two commands, one property and one method call. Properties are evaluated first so &lt;strong&gt;[Projects(MyProject).LastSuccessfulBuildLabel]&lt;/strong&gt; allows us to instruct the trigger to lookup the project named ”MyProject” and return the LastSuccessfulBuildLabel. In this case this would be ”MyProject – 234”.&lt;br /&gt;The method call would look like this after the property replacement. &lt;strong&gt;[Regex.Match(MyProject - 234,\d+$)]&lt;/strong&gt;. This will call the Regex.Match method and return the matched pattern. In this case \d+$ will match the last digits in the ”MyProject – 234” and would return ”234”.&lt;br /&gt;So the final match attribute will be \d+\.\d+\.\d+\.234 and would in this case find a folder named 1.0.0.234. If the folder is missing, the build will be triggered, but if it is there, no trigger is fired. If the interval build fires and is successful, the LastSuccessfulBuildLabel increases and is 235. When the nightly build is triggered again, the folder is missing and the trigger will fire.&lt;br /&gt;&lt;h3&gt;Writing the trigger&lt;/h3&gt;The first part to think about when writing extensions/plugins to ccnet is that the assembly must be named ccnet.*.plugins.dll otherwise ccnet wont find it. Secondly, the trigger class needs to be decorated with the ReflectionTypeAttribute to tell the class what the xml element name for the class looks like (like [ReflectorType(&quot;fileExistsTrigger&quot;)]). Every field/property that should be read into the trigger should also be decorated with the ReflectorPropertyAttribute (like [ReflectorProperty(&quot;path&quot;, Required = true)]). I mainly looked at the ProjectTrigger that comes with ccnet and added stuff along the way.&lt;br /&gt;Beware that the code don&#39;t fix all special cases, like if your project name contains , ) or other characters that messes up the expressions, but this is what I needed (remember TDD).&lt;br /&gt;You can use the code below as it is or modify it to your needs. If you want the whole solution (including unit tests), just drop me a mail.&lt;br /&gt;Good luck :)&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Linq;&lt;br /&gt;using ThoughtWorks.CruiseControl.Remote;&lt;br /&gt;using ThoughtWorks.CruiseControl.Core.Triggers;&lt;br /&gt;using ThoughtWorks.CruiseControl.Core.Util;&lt;br /&gt;using Exortech.NetReflector;&lt;br /&gt;using System.Text.RegularExpressions;&lt;br /&gt;using System.IO;&lt;br /&gt;&lt;br /&gt;namespace Meridium.CruiseControl.Net.Triggers {&lt;br /&gt;    [ReflectorType(&quot;fileExistsTrigger&quot;)]&lt;br /&gt;    public class FileExistsTrigger : IntervalTrigger {&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// If the trigger should be active if the file is missing, default is false&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        [ReflectorProperty(&quot;triggerOnMissing&quot;, Required = false)]&lt;br /&gt;        public bool TriggerOnMissing;&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// The url to the ccnet server, defaults to a local ccnet installation tcp://localhost:21234/CruiseManager.rem&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        [ReflectorProperty(&quot;serverUri&quot;, Required = false)]&lt;br /&gt;        public string ServerUri;&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// The file/directory path to see if it exists or not&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        [ReflectorProperty(&quot;path&quot;, Required = true)]&lt;br /&gt;        public string Path;&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Optional parameter to use if a regular expression should be used to match a file or directory in the path. Default is an empty string.&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        [ReflectorProperty(&quot;match&quot;, Required = false)]&lt;br /&gt;        public string Match;&lt;br /&gt;&lt;br /&gt;        private readonly ICruiseManagerFactory _managerFactory;&lt;br /&gt;&lt;br /&gt;        public IDictionary&amp;lt;string, ProjectStatus&amp;gt; ProjectStatus {&lt;br /&gt;            get {&lt;br /&gt;                lock (ProjectStatusLock) {&lt;br /&gt;                    //if cache has timed out, clear cache.&lt;br /&gt;                    if (_projectStatus != null &amp;&amp; DateTime.Now &amp;gt; _cacheValidUntil) {&lt;br /&gt;                        _projectStatus = null;&lt;br /&gt;                        Log.Debug(&quot;Cache was deleted, was valid until &quot; + _cacheValidUntil.ToString(&quot;yyyy-MM-dd HH:mm:ss,fff&quot;));&lt;br /&gt;                    }&lt;br /&gt;                    if (_projectStatus == null) {&lt;br /&gt;                        Log.Debug(&quot;Updating ProjectStatus cache from server: &quot; + ServerUri);&lt;br /&gt;                        _projectStatus = new Dictionary&amp;lt;string, ProjectStatus&amp;gt;();&lt;br /&gt;                        foreach (ProjectStatus status in _managerFactory.GetCruiseManager(ServerUri).GetProjectStatus()) {&lt;br /&gt;                            _projectStatus.Add(status.Name, status);&lt;br /&gt;                        }&lt;br /&gt;                        _cacheValidUntil = DateTime.Now + CacheTime;&lt;br /&gt;                        Log.Debug(&quot;Cache valid until &quot; + _cacheValidUntil.ToString(&quot;yyyy-MM-dd HH:mm:ss,fff&quot;));&lt;br /&gt;                    }&lt;br /&gt;                    return _projectStatus;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        private static DateTime _cacheValidUntil = DateTime.MinValue;&lt;br /&gt;        private static readonly TimeSpan CacheTime = new TimeSpan(0, 10, 0);&lt;br /&gt;        private static Dictionary&amp;lt;string, ProjectStatus&amp;gt; _projectStatus;&lt;br /&gt;        private static readonly object ProjectStatusLock = new object();&lt;br /&gt;&lt;br /&gt;        public FileExistsTrigger()&lt;br /&gt;            : this(new DateTimeProvider(), new RemoteCruiseManagerFactory()) {&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public FileExistsTrigger(DateTimeProvider dtp, ICruiseManagerFactory managerFactory)&lt;br /&gt;            : base(dtp) {&lt;br /&gt;            ServerUri = &quot;tcp://localhost:21234/CruiseManager.rem&quot;;&lt;br /&gt;            _managerFactory = managerFactory;&lt;br /&gt;        }&lt;br /&gt;        public override IntegrationRequest Fire() {&lt;br /&gt;            //only check on intervals&lt;br /&gt;            if (base.Fire() != null) {&lt;br /&gt;                try {&lt;br /&gt;                    Log.Debug(string.Format(&quot;More than {0} seconds since last integration, checking url.&quot;, IntervalSeconds));&lt;br /&gt;                    if (FileExists() != TriggerOnMissing) {&lt;br /&gt;                        Log.Debug(&quot;Trigger matched, fire IntegrationRequest&quot;);&lt;br /&gt;                        return new IntegrationRequest(BuildCondition, Name);&lt;br /&gt;                    }&lt;br /&gt;                } catch (Exception ex) {&lt;br /&gt;                    Log.Error(ex);&lt;br /&gt;                } finally {&lt;br /&gt;                    IncrementNextBuildTime();&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            return null;&lt;br /&gt;        }&lt;br /&gt;        private string HandleProjectPropertyMatches(Match m) {&lt;br /&gt;            string projectName = m.Groups[&quot;projectName&quot;].Value;&lt;br /&gt;            string property = m.Groups[&quot;property&quot;].Value;&lt;br /&gt;            var ps = GetCurrentProjectStatus(projectName);&lt;br /&gt;            switch (property.ToLower()) {&lt;br /&gt;                case &quot;lastsuccessfulbuildlabel&quot;:&lt;br /&gt;                    return ps.LastSuccessfulBuildLabel;&lt;br /&gt;                case &quot;name&quot;:&lt;br /&gt;                    return ps.Name;&lt;br /&gt;                case &quot;buildstatus&quot;:&lt;br /&gt;                    return ps.BuildStatus.ToString();&lt;br /&gt;                default:&lt;br /&gt;                    throw new NotImplementedException(string.Format(&quot;Support for property {0} is not implemented yet!&quot;, property));&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        private static string HandleRegexMatchMatches(Match m) {&lt;br /&gt;            string input = m.Groups[&quot;input&quot;].Value;&lt;br /&gt;            string pattern = m.Groups[&quot;pattern&quot;].Value;&lt;br /&gt;            Match match = Regex.Match(input, pattern);&lt;br /&gt;            return match.Success ? match.Value : string.Empty;&lt;br /&gt;        }&lt;br /&gt;        private bool FileExists() {&lt;br /&gt;            string fp = TranslateValue(Path);&lt;br /&gt;            string match = TranslateValue(Match);&lt;br /&gt;            if (!string.IsNullOrEmpty(match)) {&lt;br /&gt;                if (!fp.EndsWith(@&quot;\&quot;))//&quot;&lt;br /&gt;                    fp += @&quot;\&quot;;//&quot;&lt;br /&gt;                var dir = new DirectoryInfo(fp);&lt;br /&gt;                if (!dir.Exists) {&lt;br /&gt;                    Log.Debug(string.Format(&quot;Matching path {0} failed. Directory does not exist.&quot;, fp));&lt;br /&gt;                    return false;&lt;br /&gt;                }&lt;br /&gt;                foreach (var fs in dir.GetFileSystemInfos().Where(fs =&amp;gt; Regex.IsMatch(fs.Name, match))) {&lt;br /&gt;                    Log.Debug(string.Format(&quot;Match successful with fileSystemInfo {0}&quot;, fs.FullName));&lt;br /&gt;                    return true;&lt;br /&gt;                }&lt;br /&gt;                Log.Debug(string.Format(&quot;No match for {0}&quot;, match));&lt;br /&gt;                return false;&lt;br /&gt;&lt;br /&gt;            }&lt;br /&gt;            bool isDirectory = fp.EndsWith(@&quot;\&quot;);//&quot;&lt;br /&gt;            bool exists = isDirectory ? Directory.Exists(fp) : File.Exists(fp);&lt;br /&gt;            Log.Debug(string.Format(&quot;Checking if {0} {1} exists: {2}&quot;, (isDirectory ? &quot;directory&quot; : &quot;file&quot;), fp, exists));&lt;br /&gt;            return exists;&lt;br /&gt;        }&lt;br /&gt;        /// &amp;lt;summary&amp;gt;&lt;br /&gt;        /// Translates the supplied value and expands all methods and variables&lt;br /&gt;        /// &amp;lt;/summary&amp;gt;&lt;br /&gt;        /// &amp;lt;param name=&quot;val&quot;&amp;gt;The value to translate&amp;lt;/param&amp;gt;&lt;br /&gt;        /// &amp;lt;returns&amp;gt;The translated value&amp;lt;/returns&amp;gt;&lt;br /&gt;        private string TranslateValue(string val) {&lt;br /&gt;            //handle replacements...&lt;br /&gt;            //like [Projects(MyProject).LastSuccessfulBuildLabel]&lt;br /&gt;            val = Regex.Replace(val, @&quot;\[Projects\((?&amp;lt;projectName&amp;gt;[^\)]+)\)\.(?&amp;lt;property&amp;gt;[^\]]+)\]&quot;, HandleProjectPropertyMatches, RegexOptions.IgnoreCase);&lt;br /&gt;            //handle Regexp&lt;br /&gt;            //like [Regex.Match(string,pattern)]&lt;br /&gt;            val = Regex.Replace(val, @&quot;\[Regex\.Match\((?&amp;lt;input&amp;gt;[^,]+),(?&amp;lt;pattern&amp;gt;[^\)]+)\)\]&quot;, HandleRegexMatchMatches, RegexOptions.IgnoreCase);&lt;br /&gt;&lt;br /&gt;            return val;&lt;br /&gt;        }&lt;br /&gt;        private ProjectStatus GetCurrentProjectStatus(string project) {&lt;br /&gt;            if (!ProjectStatus.ContainsKey(project)) {&lt;br /&gt;                throw new NoSuchProjectException(project);&lt;br /&gt;            }&lt;br /&gt;            return ProjectStatus[project];&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/2479008046459397958/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2010/05/writing-custom-trigger-for.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/2479008046459397958'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/2479008046459397958'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2010/05/writing-custom-trigger-for.html' title='Writing a custom trigger for CruiseControl.Net'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-6970722856974633778</id><published>2010-04-13T10:49:00.007+02:00</published><updated>2011-11-02T05:06:21.408+01:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="EPiServer"/><title type='text'>Adding buttons to the TinyMce editor in EPiServer 6 programmatically</title><content type='html'>While developing ImageVault we needed to add buttons to insert images in the TinyMce editor that ships with EPiServer CMS 6. The buttons are decorated with a TinyMCEPluginButtonAttribute and they can be added manually by entering admin mode and modifying the settings for the XHTML property (see &lt;a href=&quot;http://www.meridiumkalmar.se/sv/blogg/2010/03/23/add-functionality-to-the-rich-text-editor-in-episerver-cms-6&quot;&gt;Oskars post &quot;Add functionality to the rich text editor in EPiServer cms 6&quot;&lt;/a&gt;).&lt;br /&gt;To do this by code this was rather simple since EPiServer has a great Api (even that it could contain more examples)...&lt;br /&gt;&lt;br /&gt;The settings are stored in the EPiServer database and is accessed using the PropertySettingsRepository class.&lt;br /&gt;To get the current settings for the TinyMce editor, just create a repository and use the GetDefault method&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;&lt;br /&gt;PropertySettingsRepository p = new PropertySettingsRepository();&lt;br /&gt;PropertySettingsWrapper defaultSettings = p.GetDefault(typeof(TinyMCESettings));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The PropertySettingsWrapper contains metadata for the settings, like DisplayName, Id etc.&lt;br /&gt;It also contains the specific setting for the TinyMce editor in the property PropertySettings.&lt;br /&gt;&lt;br /&gt;To create a new settings for the TinyMce editor with some buttons added we can use the code below.&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;&lt;br /&gt;PropertySettingsRepository p = new PropertySettingsRepository();&lt;br /&gt;PropertySettingsWrapper defaultSettings = p.GetDefault(typeof(TinyMCESettings));&lt;br /&gt;&lt;br /&gt;//create a copy of the default settings&lt;br /&gt;PropertySettingsWrapper copy = defaultSettings.Copy();&lt;br /&gt;copy.DisplayName+=&quot;(My copy)&quot;;&lt;br /&gt;TinyMCESettings settings = copy.PropertySettings as TinyMCESettings;&lt;br /&gt;&lt;br /&gt;//create a new toolbar row for my buttons&lt;br /&gt;ToolbarRow row = new ToolbarRow();&lt;br /&gt;settings.Toolbars.Add(row);&lt;br /&gt;&lt;br /&gt;//the buttons are added using the name defined in the&lt;br /&gt;//ButtonName property of the TinyMCEPluginButtonAttribute.&lt;br /&gt;row.Buttons.Add(&quot;mybutton1&quot;);&lt;br /&gt;row.Buttons.Add(&quot;separator&quot;);&lt;br /&gt;row.Buttons.Add(&quot;mybutton2&quot;);&lt;br /&gt;&lt;br /&gt;//Save the copy as a global setting&lt;br /&gt;p.SaveGlobal(copy);&lt;br /&gt;&lt;br /&gt;//Set it as default&lt;br /&gt;p.SetDefault(copy.Id);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Quite easy. The separator button is included in EPiServer 6 the others need to be added using classes decorated with the TinyMCEPluginButtonAttribute.&lt;br /&gt;&lt;br /&gt;Note!&lt;br /&gt;To actually use this code in EPiServer you need to insert it somewhere :) The easiest way is to create a InitializationModule class (implement IInitializaionModule and mark it with the InitializationModuleAttribute). Then in the Initialize method, add your code to the InitializationEngine.InitComplete event.&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;&lt;br /&gt;public void Initialize(InitializationEngine context) {&lt;br /&gt;    context.InitComplete+=(sender,args)=&gt; MyInstallButtonsMethod();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a href=&quot;http://world.episerver.com/Documentation/Items/Tech-Notes/EPiServer-CMS-6/EPiServer-CMS-60/Initialization/&quot;&gt;More about EPiServer Initialization on episerver world&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/6970722856974633778/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2010/04/adding-buttons-to-tinymce-editor-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/6970722856974633778'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/6970722856974633778'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2010/04/adding-buttons-to-tinymce-editor-in.html' title='Adding buttons to the TinyMce editor in EPiServer 6 programmatically'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-3663095176049954080</id><published>2009-10-16T14:46:00.009+02:00</published><updated>2010-05-11T13:40:39.219+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="CCNET"/><title type='text'>Converting word doc to pdf in CruiseControl.Net</title><content type='html'>Today I needed to automate the process where we create PDF files out of our client documentation in Microsoft Word format. Since up to now we have done it manually by opening the document and printing to a installed PDF printer. Quite tedious work.&lt;br /&gt;After some searching I start to realize that there are &lt;span style=&quot;font-weight: bold;&quot;&gt;many &lt;/span&gt;PDF creators around; and most of them uses Ms word and prints using a pdf printer (just as we use to do, but by automation).&lt;br /&gt;The problem is that I don&#39;t want to install printers or the Ms Office package on our build server (if I can avoid it). Another wish is that the solution should be free and open source.&lt;br /&gt;So finally I stumbled upon &lt;a href=&quot;http://www.togaware.com/linux/survivor/Convert_MS_Word.html&quot;&gt;this article&lt;/a&gt; by Graham J. Williams that uses &lt;a href=&quot;http://www.openoffice.org/&quot;&gt;OpenOffice.org&lt;/a&gt; to do the conversion and a custom macro to allow the conversion to be called from the commandline.&lt;br /&gt;&lt;pre class=&quot;basic:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;REM  *****  BASIC  *****&lt;br /&gt;&lt;br /&gt;Sub ConvertWordToPDF(cFile)&lt;br /&gt;  cURL = ConvertToURL(cFile)&lt;br /&gt;&lt;br /&gt;  &#39; Open the document.&lt;br /&gt;  &#39; Just blindly assume that the document is of a type that OOo will&lt;br /&gt;  &#39;  correctly recognize and open -- without specifying an import filter.&lt;br /&gt;  oDoc = StarDesktop.loadComponentFromURL(cURL, &quot;_blank&quot;, 0, Array(MakePropertyValue(&quot;Hidden&quot;, True), ))&lt;br /&gt;&lt;br /&gt;  cFile = Left(cFile, LastIndexOf(cFile,&quot;.&quot;)) + &quot;pdf&quot;&lt;br /&gt;  cURL = ConvertToURL(cFile)&lt;br /&gt;&lt;br /&gt;  &#39; Save the document using a filter.&lt;br /&gt;  oDoc.storeToURL(cURL, Array(MakePropertyValue(&quot;FilterName&quot;, &quot;writer_pdf_Export&quot;), ))&lt;br /&gt;&lt;br /&gt;  oDoc.close(True)&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;Function LastIndexOf ( cText as String, cMatch as String) As Integer&lt;br /&gt;  lastIndex = 0&lt;br /&gt;  pos=0&lt;br /&gt;  Do&lt;br /&gt;    pos = InStr(pos+1,cText,cMatch)&#39;&lt;br /&gt;    If pos &gt;0 Then&lt;br /&gt;      lastIndex = pos&lt;br /&gt;    End If&lt;br /&gt;  Loop While pos &gt;0&lt;br /&gt;  LastIndexOf()=lastIndex&lt;br /&gt;End Function&lt;br /&gt;&lt;br /&gt;Function MakePropertyValue( Optional cName As String, Optional uValue ) As com.sun.star.beans.PropertyValue&lt;br /&gt;  Dim oPropertyValue As New com.sun.star.beans.PropertyValue&lt;br /&gt;  If Not IsMissing( cName ) Then&lt;br /&gt;    oPropertyValue.Name = cName&lt;br /&gt;  EndIf&lt;br /&gt;  If Not IsMissing( uValue ) Then&lt;br /&gt;    oPropertyValue.Value = uValue&lt;br /&gt;  EndIf&lt;br /&gt;  MakePropertyValue() = oPropertyValue&lt;br /&gt;End Function&lt;br /&gt;&lt;/pre&gt;I modified it a bit to allow correct handing of extensions.&lt;br /&gt;To call it from a windows command prompt just enter&lt;br /&gt;&lt;pre class=&quot;basic:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;c:\program files\OpenOffice.Org 3\Program\swriter.exe -invisible &quot;macro:///Standard.Module1.ConvertWordToPDF(c:\temp\My word document.doc)&quot;&lt;br /&gt;&lt;/pre&gt;I incorporated it in our cruisecontrol.net nant scripts so all solutions that needs pdf conversions in the build chain can have it.&lt;br /&gt;Great!&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-style:italic;&quot;&gt;2010-05-11 Update: Fixed a bug with filenames that contained more than one period (.), added the LastIndexOf function&lt;/span&gt;</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/3663095176049954080/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2009/10/converting-word-doc-to-pdf-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/3663095176049954080'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/3663095176049954080'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2009/10/converting-word-doc-to-pdf-in.html' title='Converting word doc to pdf in CruiseControl.Net'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-8454250546558814303</id><published>2009-08-06T00:10:00.006+02:00</published><updated>2009-08-06T00:20:04.881+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Macros"/><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio"/><title type='text'>Bugfix for Documentator Macros (VB)</title><content type='html'>&lt;div&gt;A small bug fix for the auto documentation feature for VB.Net.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Thanks for the report Sam; what has it been, nearly 2 years since you reported the bug finally I&#39;ve come about... ( You said it wasn&#39;t urgent... you&#39;ll probably reconsider next time ;)&lt;/div&gt;&lt;br /&gt;&lt;div&gt; &lt;/div&gt;&lt;br /&gt;&lt;div&gt;When autodocumenting a Sub or function that is connected to an event, an error occurs.&lt;/div&gt;&lt;br /&gt;&lt;div&gt; &lt;/div&gt;&lt;br /&gt;&lt;div&gt;The fix is included in version 2.5.3.0 that can be downloaded from &lt;a href=&quot;http://dan.meridium.se/DocumentatorMacros/v2.5.3.0/Meridium.rar&quot;&gt;http://dan.meridium.se/DocumentatorMacros/v2.5.3.0/Meridium.rar&lt;/a&gt;.&lt;/div&gt;&lt;br /&gt;&lt;div&gt; &lt;/div&gt;&lt;br /&gt;&lt;div&gt;(For more information about the Documentator Macros, see &lt;a href=&quot;http://www.codeproject.com/KB/cs/documentatormacros.aspx&quot;&gt;http://www.codeproject.com/KB/cs/documentatormacros.aspx&lt;/a&gt; )&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/8454250546558814303/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2009/08/bugfix-for-documentator-macros-vb.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/8454250546558814303'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/8454250546558814303'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2009/08/bugfix-for-documentator-macros-vb.html' title='Bugfix for Documentator Macros (VB)'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-2718870077747005393</id><published>2009-07-27T21:47:00.011+02:00</published><updated>2009-07-28T00:02:40.846+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="C#"/><category scheme="http://www.blogger.com/atom/ns#" term="MVC"/><title type='text'>Mvc for Winforms - Mapping the View event to the Controller action Part III</title><content type='html'>Long time since part I and II were posted, but I&#39;ve been quite busy lately so I haven&#39;t found time (or prioritized) to write the last part, but here we are :)&lt;br /&gt;&lt;br /&gt;To recap, in &lt;a href=&quot;http://dhvik.blogspot.com/2008/11/mvc-for-winforms-mapping-view-event-to.html&quot;&gt;part I&lt;/a&gt; I explained about the background of the issue, that I was writing a small winforms MVC framework. The goal of the framework was to be able to instruct the controller to listen to a component in the view and when the component called an event, a specific method would be called.&lt;br /&gt;For example, the call below would register the click event from the saveButton&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;Controller.RegisterAction(saveButton,&quot;Save&quot;);&lt;br /&gt;&lt;/pre&gt;(in later editions I have also added support for lamda expressions instead of method names, and thus almost abandoned the previous)&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;Controller.RegisterAction(rotateRightButton,() =&gt; Controller.Rotate(90));&lt;br /&gt;Controller.RegisterAction(rotateLeftButton,() =&gt; Controller.Rotate(-90));&lt;br /&gt;&lt;/pre&gt;In &lt;a href=&quot;http://dhvik.blogspot.com/2008/11/mvc-for-winforms-mapping-view-event-to_02.html&quot;&gt;part II&lt;/a&gt; I wrote about how find and listen to the event of the component.&lt;br /&gt;In this section I will explain how I call the method.&lt;br /&gt;&lt;br /&gt;What we ended with in the last post was that we had registered the controllers event to call the ExecuteAction method of the Controller class.&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;public void ExecuteAction(object source, object eventArgs, ActionData actionData)&lt;br /&gt;&lt;/pre&gt; or in the latter case using lambda expressions (or action)&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;public void ExecuteAction(object source, object eventArgs, Action action)&lt;br /&gt;&lt;/pre&gt; In this case the Action is the easiest to implement. Just call the Invoke method of the Action instance and you are done.&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;public void ExecuteAction(object source, object eventArgs, Action action) {&lt;br /&gt;   action.Invoke();&lt;br /&gt;}&lt;/pre&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;The no Action ExecuteAction method (almost abandoned way )&lt;/span&gt;&lt;br /&gt;The ActionData variant is somewhat more tedious but this is how ASP.NET MVC does it.&lt;br /&gt;First we need to find the method to call. The ActionData contains the name of the method. We now needs to find the method of the controller that matches that name. MVC for ASP.NET uses the ActionSelectorClass. The ActionSelectorClass is responsible for retrieving the MethodInfo for an action by passing in the name of the action. It support action aliases and other things so look it up for a great example.&lt;br /&gt;In short it uses reflection to get all methods, filters out irrelevant methods and returns the one that matches the name.&lt;br /&gt;Example (really shortened example, lookup ActionSelectorClass in the ASP.NET MVC framework for a complete example.)&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;//get all methods&lt;br /&gt;MethodInfo[] array = ControllerType.GetMethods(BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance);&lt;br /&gt;//convert to a dictionary&lt;br /&gt;ILookup&amp;lt;string,MethodInfo&amp;gt; methodLookup = array.ToLookup(method =&gt; method.Name, StringComparer.OrdinalIgnoreCase);&lt;br /&gt;//return the matching MethodInfo&lt;br /&gt;MethodInfo action = methodLookup[actionName];&lt;br /&gt;&lt;/pre&gt;Now we have the MethodInfo to invoke, only to calculate the parameters to pass to the method is left.&lt;br /&gt;&lt;br /&gt;The action is invoked using the Invoke method of the MethodInfo class and we need to pass the instance to invoke it on (the controller instance) and the object array containing the parameters to pass to the method/action.&lt;br /&gt;Example&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;action.Invoke(controller,parameterValues);&lt;/pre&gt;If the action don&#39;t have any parameters we just pass null, but if we do have parameters we need to assign values for them.&lt;br /&gt;Remember: we  wanted to be able to register the call&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;Controller.RegisterAction(createBoldTextButton, &quot;CreateText&quot;, new {name=&quot;Bold&quot;, type=4}); &lt;/pre&gt;To call the action method&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;public void CreateText(string name, int type) {...}  &lt;/pre&gt;using the &quot;Bold&quot; as value for the name parameter and 4 as value for the type parameter.&lt;br /&gt;&lt;br /&gt;To succeed with this we need to do some parsing.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Iterate all parameters of the method info.&lt;/li&gt;&lt;li&gt;Handle special cases (like if the parameter is named source and is of type object, then the source value from the event handler method should be passed).&lt;/li&gt;&lt;li&gt;Find a property with a matching name from the value object passed in the RegisterAction.&lt;/li&gt;&lt;/ol&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Iterate all parameters of the method info&lt;/span&gt;&lt;br /&gt;Using reflection it&#39;s easy to find the parameters.&lt;br /&gt;&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;List&amp;lt;object&amp;gt; objects = new List&amp;lt;object&amp;gt;();&lt;br /&gt;foreach (ParameterInfo info in method.GetParameters()) {              &lt;br /&gt;  objects.Add(GetParameterValue(info));&lt;br /&gt;}&lt;br /&gt;return objects.ToArray();&lt;/pre&gt;&lt;span style=&quot;font-weight: bold;&quot;&gt;Handle special cases&lt;/span&gt;&lt;br /&gt;The GetParameterValue method will return the value to pass to the supplied ParameterInfo. In some cases the value to be passed as parameter is not any of the values passed to the values parameter (in the RegisterAction method), like for example, if one would need to get the EventArgs or source parameter passed from the event component, then we would need to handle them. (other customizations can be done here, this is an example)&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;Type parameterType = parameterInfo.ParameterType;&lt;br /&gt;string parameterName = parameterInfo.Name;&lt;br /&gt;&lt;br /&gt;//handle eventArgs and source parameters&lt;br /&gt;if (string.Equals(parameterName, &quot;e&quot;, StringComparison.OrdinalIgnoreCase) &amp;amp;&amp;amp; _eventArgs != null &amp;amp;&amp;amp; parameterType.IsAssignableFrom(_eventArgs.GetType()))&lt;br /&gt;  return _eventArgs;&lt;br /&gt;if (string.Equals(parameterName, &quot;source&quot;, StringComparison.OrdinalIgnoreCase)&amp;amp;&amp;amp; parameterType.Equals(typeof(object)))&lt;br /&gt;  return _source;&lt;br /&gt;&lt;br /&gt;//parse the values object&lt;br /&gt;return GetValuesValue(parameterType, parameterName);&lt;/pre&gt;Find a matching property of the value object&lt;br /&gt;At last we would try to find a matching property of the value object that matches the parameter type and name. (First we check that the parameter don&#39;t matches the whole value object)&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;//if we don&#39;t have a value, return null&lt;br /&gt;if (ValueType == null)&lt;br /&gt;  return null;&lt;br /&gt;//if the value is matching, use it as value&lt;br /&gt;if (type.Equals(ValueType))&lt;br /&gt;  return _actionData.Values;&lt;br /&gt;PropertyInfo[] valueProperties= ValueType.GetProperties();&lt;br /&gt;//if the value don&#39;t have any properties, return null&lt;br /&gt;if (valueProperties.Length == 0)&lt;br /&gt;  return null;&lt;br /&gt;//find matching property&lt;br /&gt;PropertyInfo[] possibleMatches = valueProperties&lt;br /&gt;     .Where(p =&gt; string.Equals(name, p.Name, StringComparison.OrdinalIgnoreCase)&lt;br /&gt;            &amp;amp;&amp;amp; type.IsAssignableFrom(p.PropertyType))&lt;br /&gt;     .ToArray();&lt;br /&gt;if (possibleMatches == null || possibleMatches.Length == 0)&lt;br /&gt;  return null;&lt;br /&gt;if (possibleMatches.Length &gt; 1)&lt;br /&gt;  throw new AmbiguousMatchException(string.Format(&quot;The value collection contains ambigous match values for the parameter {0} {1}&quot;, type, name));&lt;br /&gt;return possibleMatches[0].GetValue(_actionData.Values, null);&lt;br /&gt;&lt;/pre&gt;Ok, so now we can invoke the method with our array of parameters. (as I said, a bit tedious).&lt;br /&gt;If we compare the two variants,&lt;br /&gt;&lt;pre class=&quot;c-sharp:nocontrols:nogutter&quot; name=&quot;code&quot;&gt;Controller.RegisterAction(createBoldTextButton, &quot;CreateText&quot;, new {name=&quot;Bold&quot;, type=4});&lt;br /&gt;Controller.RegisterAction(createBoldTextButton, ()=&gt; Controller.CreateText(&quot;Bold&quot;, 4);&lt;br /&gt;&lt;/pre&gt;you understand why I have abandoned this last variant and only uses Actions/lambda functions.&lt;br /&gt;The tedious method can still be used if you want to lift the connection between view and controller to a configuration layer. Then the Action method will be hard to implement.&lt;br /&gt;&lt;br /&gt;That sums up this last part of my WinForms MVC framework lessons and experiences. Hope that you learned something.</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/2718870077747005393/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2009/07/mvc-for-winforms-mapping-view-event-to.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/2718870077747005393'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/2718870077747005393'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2009/07/mvc-for-winforms-mapping-view-event-to.html' title='Mvc for Winforms - Mapping the View event to the Controller action Part III'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-4030078708965151168</id><published>2009-06-24T11:40:00.009+02:00</published><updated>2009-06-26T07:28:45.708+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="SharePoint"/><title type='text'>The start address &amp;lt;https://newhostname.domain.com&amp;gt; cannot be crawled</title><content type='html'>Have a SharePoint site on one of our servers and have lately added a new hostname to that server to access a new site collection. I started getting a lot of entries in the eventlog using event id 2436 stating:&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-size:85%;&quot;&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;The start address &amp;lt;https://newhostname.domain.com&amp;gt; cannot be crawled.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Context: Application &#39;Search index file on the search server&#39;, Catalog &#39;Search&#39;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Details:&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;Access is denied. Check that the Default Content Access Account has access to this content, or add a crawl rule to crawl this content. (0x80041205)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Another symptom on this error is that if you open a web browser on the server itself and try to navigate to the url, you&#39;ll only get an authentication dialog (symptom on a 401 error).&lt;br /&gt;&lt;br /&gt;After a bit of searching I found a solution that I recognized. The following kb article &lt;a href=&quot;http://support.microsoft.com/kb/971382&quot;&gt;http://support.microsoft.com/kb/971382&lt;/a&gt; provided the solution to add the new hostname to the registry key &lt;span style=&quot;font-size:85%;&quot;&gt;&lt;span style=&quot;font-family:courier new;&quot;&gt;HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\MSV1_0\BackConnectionHostNames &lt;/span&gt;&lt;/span&gt; .&lt;br /&gt;&lt;br /&gt;When I started regedit I found that the key already existed and a couple of hostnames already was present. It seems that I had implemented this fix before.&lt;br /&gt;Better to write a post about it so I don&#39;t forget it in the future :)&lt;br /&gt;&lt;br /&gt;Ps. Don&#39;t forget to restart the server after applying the registry change. Ds.</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/4030078708965151168/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2009/06/start-address-cannot-be-crawled.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/4030078708965151168'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/4030078708965151168'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2009/06/start-address-cannot-be-crawled.html' title='The start address &amp;lt;https://newhostname.domain.com&amp;gt; cannot be crawled'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-5960759059021408684</id><published>2009-06-24T07:26:00.005+02:00</published><updated>2009-09-28T06:10:43.354+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Tfs"/><category scheme="http://www.blogger.com/atom/ns#" term="WorkItemQueries"/><title type='text'>Export and Import Work item queries in TFS projects</title><content type='html'>I have lately been introducing new WorkItemTypes in our TFS project to handle tests and support cases. With those new types the need for modifying the queries in the current projects was necessary. To modify a query I could have opened them in visual studio and saved them to disk and then saved them individually to each project. Since we are getting a lot of projects (60+) this would be a quite tedious task to update 10+ queries in each project individually.&lt;br /&gt;To solve this administrative plague I  wrote a small commandline program that allows you to export and import queries from a project.&lt;br /&gt;&lt;br /&gt;Example&lt;br /&gt;To list all queries in a project&lt;br /&gt;&lt;span style=&quot;;font-family:courier new;font-size:85%;&quot;  &gt;TFSQueryUtil.exe /t https://tfsserver.domain.com:8143 /p &quot;My Tfs Project&quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This will list every Work Item Query by Scope, Name and description&lt;br /&gt;&lt;br /&gt;To export all queries to the current folder&lt;br /&gt;&lt;span style=&quot;;font-family:courier new;font-size:85%;&quot;  &gt;TFSQueryUtil.exe /t https://tfsserver.domain.com:8143 /p &quot;My Tfs Project&quot; /o Export /q *&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This will export each query using the name of the query (plus the .wiq extension) as name of the exported file. If you want to only export one query, use the example below.&lt;br /&gt;&lt;span style=&quot;;font-family:courier new;font-size:85%;&quot;  &gt;TFSQueryUtil.exe /t https://tfsserver.domain.com:8143 /p &quot;My Tfs Project&quot; /o Export /q &quot;My query&quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To import all *.wiq files to a project as Team queries&lt;br /&gt;&lt;span style=&quot;;font-family:courier new;font-size:85%;&quot;  &gt;TFSQueryUtil.exe /t https://tfsserver.domain.com:8143 /p &quot;My Tfs Project&quot; /o Import /f *.wiq /qs Public&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This command will use the filename (without the .wiq extension) as name for the imported query.&lt;br /&gt;You can also specify a Description of the query by using the /qd switch (best for use with one query imports)&lt;br /&gt;&lt;br /&gt;The format of the wiq files follows the &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/aa721763.aspx&quot;&gt;WorkItemQuery schema&lt;/a&gt; but since the schema only includes the query and no meta data (like name, scope or description) this has to be provided as parameter switches... (Perhaps something to fix in TFS 2010)&lt;br /&gt;&lt;br /&gt;If you need more help add the /? parameter.&lt;br /&gt;&lt;br /&gt;I haven&#39;t released the program as open source but feel free to use it if you have need for it.&lt;br /&gt;Can be downloaded from &lt;a href=&quot;http://dan.meridium.se/TfsQueryUtil.rar&quot;&gt;http://dan.meridium.se/TfsQueryUtil.rar&lt;/a&gt;&lt;br /&gt;To use it you need to have Team Foundation Explorer 2008 installed. (needs the tfs dlls in the GAC).&lt;br /&gt;&lt;br /&gt;2009-09-28 Update; small bugfix&lt;br /&gt;* Now writes xml files as utf-8 (was an in consequence between xml notation and file encoding)</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/5960759059021408684/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2009/06/handling-work-item-queries.html#comment-form' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/5960759059021408684'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/5960759059021408684'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2009/06/handling-work-item-queries.html' title='Export and Import Work item queries in TFS projects'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6908201609754153877.post-3598932492597013336</id><published>2009-05-05T14:27:00.005+02:00</published><updated>2009-05-05T15:50:36.346+02:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Macros"/><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio"/><title type='text'>New version of DocumentatorMacros</title><content type='html'>Long time since last version was released officially... (almost two years), time to make a change.&lt;br /&gt;New version can be downloaded from &lt;a href=&quot;http://dan.meridium.se/DocumentatorMacros/v2.5.2.0/Meridium.rar&quot;&gt;http://dan.meridium.se/DocumentatorMacros/v2.5.2.0/Meridium.rar&lt;/a&gt;&lt;br /&gt;(For more information about the Documentator Macros, see &lt;a href=&quot;http://www.codeproject.com/KB/cs/documentatormacros.aspx&quot;&gt;http://www.codeproject.com/KB/cs/documentatormacros.aspx&lt;/a&gt; )&lt;br /&gt;&lt;br /&gt;New version contains some news:&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Contains support for Resharper 4.5, Visual studio 2008 and some minor changes.&lt;/p&gt;&lt;p&gt;Enhancements /Bugfixes&lt;/p&gt;&lt;ul&gt;&lt;li&gt;No longer enters wrong type of linefeeds when applying some functions&lt;/li&gt;&lt;li&gt;PasteTemplate &lt;ul&gt;&lt;li&gt;now indents correctly&lt;/li&gt;&lt;li&gt;Handles static events&lt;/li&gt;&lt;li&gt;Handles new, virtual, override keywords when converting fields-&gt;property&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;DocumentThis - Now autodocuments thrown exceptions&lt;/li&gt;&lt;/ul&gt;</content><link rel='replies' type='application/atom+xml' href='https://dhvik.blogspot.com/feeds/3598932492597013336/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='https://dhvik.blogspot.com/2009/05/new-version-of-documentatormacros.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/3598932492597013336'/><link rel='self' type='application/atom+xml' href='https://www.blogger.com/feeds/6908201609754153877/posts/default/3598932492597013336'/><link rel='alternate' type='text/html' href='https://dhvik.blogspot.com/2009/05/new-version-of-documentatormacros.html' title='New version of DocumentatorMacros'/><author><name>Dan</name><uri>http://www.blogger.com/profile/14787263872119403136</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://2.bp.blogspot.com/_BktCNuR9VXg/S_W8rNV-5hI/AAAAAAAABC0/BP4nWl1EXzc/S220/2009+-+Dan+H%C3%A4ndevik+80_80.jpg'/></author><thr:total>0</thr:total></entry></feed>