Sometimes I need to convert GUIDs without dashes to GUIDs with dashes or vice versa, so I've created this small page to help me with the conversion.
or
or
<?xml version="1.0" encoding="UTF-8"?> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <Product Id="*" Name="WixWithCustomAction" Language="1033" Version="1.0.0.0" Manufacturer="Trifonov" UpgradeCode="60468a7d-6485-4e7e-bf82-503213bc43a8"> <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" /> <Media Id='1' Cabinet='Dummy.cab' EmbedCab='yes' /> <Directory Id='TARGETDIR' Name='SourceDir'> <Directory Id='ProgramFilesFolder'> <Directory Id='WixWithCustomAction' Name='WixWithCustomAction'> <Component Id="DummyContent" Guid="ba9028ae-0d3b-4b66-8560-f53330736265"> <!-- Add the dummy file as content. --> <File Id="DummyFile" KeyPath="yes" Source="Dummy.txt" Vital="yes" /> </Component> </Directory> </Directory> </Directory> <Feature Id="Complete" Title="WixWithCustomAction" Level="1"> <ComponentRef Id='DummyContent' /> </Feature> </Product> </Wix>That's how our solution looks like:
using Microsoft.Deployment.WindowsInstaller; using System.IO; namespace MyCustomActionProject { public class CustomActions { [CustomAction] public static ActionResult MyCustomAcion(Session session) { session.Log("Executing MyCustomAcion"); File.CreateText(@"c:\temp\installed.txt"); return ActionResult.Success; } } }Now we just need to call this custom action from the installer. To do this we will add a reference to this project in the setup project.
<!--The source file should be the MyCustomActionProject.CA.dll file, that's why it's constructed this way--> <Binary Id='CustomActionBinary' SourceFile='$(var.MyCustomActionProject.TargetDir)$(var.MyCustomActionProject.TargetName).CA.dll' /> <!--The DllEntry must be the name of the method to be called from the custom action project, in our case - MyCustomActionMethod http://wix.tramontana.co.hu/tutorial/events-and-actions/at-a-later-stage The Execute attribute will specify the deferred status of our custom action. And finally, HideTarget will allow us to disable logging the parameteres passed to this custom action if security considerations so dictate.--> <CustomAction Id='CustomActionId' BinaryKey='CustomActionBinary' DllEntry='MyCustomActionMethod' Execute="deferred" HideTarget="yes"/> <InstallExecuteSequence> <!--We want to call the custom action before the install finalizes--> <Custom Action='CustomActionId' Before='InstallFinalize'/> </InstallExecuteSequence>And that's it. Now if we build the setup project and run the created msi installer, c:\temp\installed.txt will be created as a part of the installation process.
PM> Install-Package ServiceStackThis will add the dlls needed for ServiceStack. Now let's create the DTO:
using ServiceStack.ServiceHost; namespace ServiceStackStreaming.Service { [Route("/upload/{FileName}", "POST")] public class UploadPackage : IRequiresRequestStream { public System.IO.Stream RequestStream { get; set; } public string FileName { get; set; } } }To enable Streaming support we need to implement IRequiresRequestStream which needs a RequestStream property of type System.IO.Stream. We'll add a FileName property and include it in the Route so that we would be able to pass the uploaded file name.
using ServiceStack.Common.Web; using ServiceStackStreaming.Utility; using System; using System.IO; namespace ServiceStackStreaming.Service { public class UploadService : ServiceStack.ServiceInterface.Service { public object Post(UploadPackage request) { // hack - get the properties from the request if (string.IsNullOrEmpty(request.FileName)) { var segments = base.Request.PathInfo.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); request.FileName = segments[1]; } string resultFile = Path.Combine(@"C:\Temp", request.FileName); if (File.Exists(resultFile)) { File.Delete(resultFile); } using (FileStream file = File.Create(resultFile)) { request.RequestStream.Copy(file); } return new HttpResult(System.Net.HttpStatusCode.OK); } } }
using System.IO; namespace ServiceStackStreaming.Utility { public static class StreamExtender { public static void Copy(this Stream instance, Stream target) { int bytesRead = 0; int bufSize = copyBuf.Length; while ((bytesRead = instance.Read(copyBuf, 0, bufSize)) > 0) { target.Write(copyBuf, 0, bytesRead); } } private static readonly byte[] copyBuf = new byte[0x1000]; } }this simply copes the instance stream to the target stream. Another option is to use ServiceStack StreamExtensions WriteTo method instead of creating this utility method.
using ServiceStack.WebHost.Endpoints; namespace ServiceStackStreaming.Service { public class AppHost : AppHostHttpListenerBase { public AppHost() : base("Agent", typeof(UploadService).Assembly) { } public override void Configure(Funq.Container container) { // we can add the routing here instead of adding it as attribute to the DTO //Routes // .AddWe can configure the route here, but I prefer doing this with attribute.("/upload/{FileName}", "POST"); } } }
using ServiceStackStreaming.Service; using System; namespace ServiceStackStreaming.Server { class Program { static void Main(string[] args) { var appHost = new AppHost(); appHost.Init(); appHost.Start("http://*:1999/"); Console.WriteLine("Service listening on port 1999!"); Console.ReadKey(); } } }
using ServiceStackStreaming.Utility; using System.IO; using System.Net; namespace ServiceStackStreaming.Client { class Program { static void Main(string[] args) { string filePath = @"c:\temp\upload.zip"; HttpWebRequest client = (HttpWebRequest)WebRequest.Create("http://localhost:1999/upload/upload-copy.zip"); client.Method = WebRequestMethods.Http.Post; // the following 4 rows enable streaming client.AllowWriteStreamBuffering = false; client.SendChunked = true; client.ContentType = "multipart/form-data;"; client.Timeout = int.MaxValue; using (FileStream fileStream = File.OpenRead(filePath)) { fileStream.Copy(client.GetRequestStream()); } var response = new StreamReader(client.GetResponse().GetResponseStream()).ReadToEnd(); } } }And that's it. We create WebRequest, set the needed properties to enable streaming on the client and copy the file stream to the request stream. This will call the service and will upload the "C:\Temp\upload.zip" file as upload-copy.zip file.
using System; namespace ConsoleLogger { public class DummyClass { public void WriteToConsole(string text) { Console.Write(text); } private void PrivateWriteToConsole(string text) { Console.Write("Private: " + text); } } }Now we can add a unit test to test the method using reflection:
[TestMethod] public void PrivateWriteToConsoleReflection() { var currentConsoleOut = Console.Out; DummyClass target = new DummyClass(); Type type = typeof(DummyClass); string text = "Hello"; using (var consoleOutput = new ConsoleOutput()) { var method = type.GetMethod("PrivateWriteToConsole", BindingFlags.NonPublic | BindingFlags.Instance); method.Invoke(target, new object[1] { text }); Assert.AreEqual(string.Format("Private: {0}", text), consoleOutput.GetOuput()); } Assert.AreEqual(currentConsoleOut, Console.Out); }We get and invoke the method using reflection.
[TestMethod] public void PrivateWriteToConsolePrivateObject() { var currentConsoleOut = Console.Out; PrivateObject target = new PrivateObject(typeof(DummyClass)); string text = "Hello"; using (var consoleOutput = new ConsoleOutput()) { target.Invoke("PrivateWriteToConsole", text); Assert.AreEqual(string.Format("Private: {0}", text), consoleOutput.GetOuput()); } Assert.AreEqual(currentConsoleOut, Console.Out); }
///we will modify it for our needs:///A test for PrivateWriteToConsole /// [TestMethod] [DeploymentItem("ConsoleLogger.exe")] public void PrivateWriteToConsoleTest() { DummyClass_Accessor target = new DummyClass_Accessor(); // TODO: Initialize to an appropriate value string text = string.Empty; // TODO: Initialize to an appropriate value target.PrivateWriteToConsole(text); Assert.Inconclusive("A method that does not return a value cannot be verified."); }
///When Visual Studio builds the project it will generate ConsoleLogger_Accessor.exe assembly, containing DummyClass_Accessor class with public methods only.///A test for PrivateWriteToConsole /// [TestMethod] [DeploymentItem("ConsoleLogger.exe")] public void PrivateWriteToConsoleTest() { var currentConsoleOut = Console.Out; DummyClass_Accessor target = new DummyClass_Accessor(); string text = "Hello"; using (var consoleOutput = new ConsoleOutput()) { target.PrivateWriteToConsole(text); Assert.AreEqual(string.Format("Private: {0}", text), consoleOutput.GetOuput()); } Assert.AreEqual(currentConsoleOut, Console.Out); }
[assembly: InternalsVisibleTo("ConsoleLogger.Tests")]now we will add an internal method to the same class:
using System; namespace ConsoleLogger { public class DummyClass { public void WriteToConsole(string text) { Console.Write(text); } private void PrivateWriteToConsole(string text) { Console.Write("Private: " + text); } internal void InternalWriteToConsole(string text) { Console.Write("Internal: " + text); } } }and here is the working test method:
[TestMethod] public void InternalWriteToConsoleTest() { var currentConsoleOut = Console.Out; DummyClass target = new DummyClass(); string text = "Hello"; using (var consoleOutput = new ConsoleOutput()) { target.InternalWriteToConsole(text); Assert.AreEqual(string.Format("Internal: {0}", text), consoleOutput.GetOuput()); } Assert.AreEqual(currentConsoleOut, Console.Out); }
using System; namespace ConsoleLogger { public class DummyClass { public void WriteToConsole(string text) { Console.Write(text); } } }I have created a small helper class to redirect the output to a StringWriter:
using System; using System.IO; namespace ConsoleLogger.Tests { public class ConsoleOutput : IDisposable { private StringWriter stringWriter; private TextWriter originalOutput; public ConsoleOutput() { stringWriter = new StringWriter(); originalOutput = Console.Out; Console.SetOut(stringWriter); } public string GetOuput() { return stringWriter.ToString(); } public void Dispose() { Console.SetOut(originalOutput); stringWriter.Dispose(); } } }Now let's write the unit test:
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace ConsoleLogger.Tests { [TestClass] public class DummyClassTest { [TestMethod] public void WriteToConsoleTest() { var currentConsoleOut = Console.Out; DummyClass target = new DummyClass(); string text = "Hello"; using (var consoleOutput = new ConsoleOutput()) { target.WriteToConsole(text); Assert.AreEqual(text, consoleOutput.GetOuput()); } Assert.AreEqual(currentConsoleOut, Console.Out); } } }This way we are sure that the original output will be restored and it's easy to get the output from the console.
<ItemGroup Condition=" '$(PackageSources)' == '' "> <!-- Package sources used to restore packages. By default will used the registered sources under %APPDATA%\NuGet\NuGet.Config --> <!-- <PackageSource Include="https://nuget.org/api/v2/" /> <PackageSource Include="https://my-nuget-source/nuget/" /> --> </ItemGroup>and adding s new PackageSource location
<ItemGroup Condition=" '$(PackageSources)' == '' "> <!-- Package sources used to restore packages. By default will used the registered sources under %APPDATA%\NuGet\NuGet.Config --> <!-- <PackageSource Include="https://nuget.org/api/v2/" /> <PackageSource Include="https://my-nuget-source/nuget/" /> --> <PackageSource Include="C:\NugetConfig\Repo" /> </ItemGroup>And that's it. This will make the solution search for the used packages in the given folder and you will get а meaningful error if the package could not be found.