Specflow step file doesn't appear to be recognised by feature file - c#

I created a Specflow project which has one feature file, EBIntegrationTest.feature :
Feature: EBIntegrationTest for MF
Initial test feature
#mytag
Scenario: Subscribe to Market Data EU Topic and write to SQL DB
Given MD messages are streaming to MF application
When MF enriches template 304
Then the enriched messages are then written to an SQL DB server
I then added a step file to my Steps folder:
using System;
using System.Collections.Generic;
using System.Text;
namespace eb_test_tool.Steps
{
class EBIntegrationTest
{
[Given(#"MD messages are streaming to MF application")]
public void GivenMDMessagesAreStreamingToMFApplication()
{
var processInfo = new ProcessStartInfo("C:\\ProgramData\\CME\\Java_SymLinks\\JDK8_x64\\jre\\bin\\java.exe",
"java -jar .\\Jar\\Injector\\MD-Injector-1.0.jar My.TOPIC")
{
CreateNoWindow = true,
UseShellExecute = false
};
Process proc;
if ((proc = Process.Start(processInfo)) == null)
{
throw new InvalidOperationException("Message injector failed to run");
}
proc.WaitForExit();
int exitCode = proc.ExitCode;
proc.Close();
}
[When(#"MF enriches template (.*)")]
public void WhenMFEnrichesTemplate(int p0)
{
ScenarioContext.Current.Pending();
}
[Then(#"The enriched messages are then written to an SQL DB server")]
public void ThenTheEnrichedMessagesAreThenWrittenToAnSQLDBServer()
{
ScenarioContext.Current.Pending();
}
}
}
When I run dotnet test it is skipped with this alert:
Am I doing something wrong or should I reference my steps file from the feature file in some way?

You need to add the [Binding] attribute to your EBIntegrationTest class and make it public.
[Binding]
public class EBIntegrationTest
{
//...
}

I noticed this in my .csproj file. When I removed it the issue was resolved.
<ItemGroup>
<Compile Remove="Steps\EBSIntegrationTestStepDefinitions.cs" />
</ItemGroup>

Related

Selenium C# NUnit how to take and save screenshot of failed test into the folder

I am new at Selenium and came up with an issue - how to take and save screenshot into the specific folder. I am using Selenium+C#+NUnit bond.
Have read many information on this but most of them is on - how to capture screenshot and add it to html file. But this is not what I need.
I need the screenshot file to be save into a folder so when I'm running Pipeline in AzureDevOps the "Tests results" block contain this screenshot as well and display it.
I was using this part of the code. The test runs and fails, but no screenshot was make
[OneTimeTearDown]
public void OneTimeTearDown()
{
if (TestContext.CurrentContext.Result.Outcome != ResultState.Failure)
{
var screenshot = ((ITakesScreenshot)driver).GetScreenshot();
var filePath = "pathToTheFolder\\Screenshots\\";
screenshot.SaveAsFile(filePath, Png);
}
}
Maybe someone can help on this and maybe share the knowledge and the code as well)
Thank you all!
Try
using System.Drawing; //add this using statement
[OneTimeTearDown]
public void OneTimeTearDown()
{
if (TestContext.CurrentContext.Result.Outcome != ResultState.Failure)
{
var screenshot = ((ITakesScreenshot)driver).GetScreenshot();
var filePath = "pathToTheFolder\\Screenshots\\";
screenshot.SaveAsFile(filePath + System.Drawing.Imaging.ImageFormat.Png);
}
}
This tutorial looks like what you need
Found a way how to reach out my goal - to save screenshots for failed tests in a bin folder of the project and attach them to the Pipeline "Test Result" section in further.
This is the final workable code. I have removed it from [OneTimeTearDown] and set it to [TearDown]. And also have added attachment with the last row, that allows to sent screenshots to Pipelines "Test result" block.
This is my Base.cs file where I am locating IWebDriver, all Setup and TearDown methods
using E2E_Tests.TestData;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium;
using WebDriverManager.DriverConfigs.Impl;
using OpenQA.Selenium.Chrome;
using System.Configuration;
using NUnit.Framework.Interfaces;
[TearDown]
public void TearDown()
{
TakeScreenshotDefaultImageFormat();
driver.Close();
}
public void TakeScreenshotDefaultImageFormat()
{
if (TestContext.CurrentContext.Result.Outcome == ResultState.Error)
{
var screenshot = ((ITakesScreenshot)driver).GetScreenshot();
var screenshotDirectoryPath = Path.Combine(TestContext.CurrentContext.TestDirectory, "Screenshots\\");
if (!Directory.Exists(screenshotDirectoryPath))
{
Directory.CreateDirectory(screenshotDirectoryPath);
}
var currentDate = DateTime.Now;
var filePath = $"{screenshotDirectoryPath}{TestContext.CurrentContext.Test.Name}_{currentDate.ToString("yyyy.MM.dd-HH.mm.ss")}.png";
screenshot.SaveAsFile(filePath);
TestContext.AddTestAttachment(filePath);
}
}

Wixsharp how to reference custom action project

I currently have two projects in my solution, a deployment project which builds the msi and another project which contains my custom actions. I am having trouble referencing my custom actions the same two errros keep appearing:
..\WixSharp Setup\bin\Debug\WixSharpSetup.exe" "/MSBUILD:WixSharp Setup" "/WIXBIN:"" exited with code -532462766. WixSharp Setup ..\WixSharp Setup\packages\WixSharp.1.9.2\build\WixSharp.targets 6
No CA or UI entry points found in module: ..\WixSharp Setup\WixSharp Setup\WixSharpSetup.exe WixSharp Setup ..\WixSharp Setup\WixSharp Setup\EXEC
Deployment project
using System;
using System.Windows.Forms;
using Deploy.CustomAction;
using WixSharp;
using WixSharp.Forms;
namespace WixSharp_Setup
{
class Program
{
static void Main()
{
var project = new ManagedProject("MyProduct",
new Dir(#"%ProgramFiles%\My Company\My Product",
new File("Program.cs")),
new ManagedAction(SearchAPIActions.SearchAPIInstall));
project.GUID = new Guid("6fe30b47-2577-43ad-9095-1861ba25889b");
project.ManagedUI = ManagedUI.Default; //all standard UI dialogs
project.BuildMsi();
}
CustomAction project
public class SearchAPIActions
{
[CustomAction]
public static ActionResult SearchAPIInstall(Session session)
{
session.Log("Begin CustomAction1");
return ActionResult.Success;
}
In case anyone is interested i found the solution to my problem, as the Custom action was compiling to a .dll you need to give a direct reference to it when you declare a managedAction.
new ManagedAction(CustomActions.IISReset, #"Your full Path\Customs.dll"));

How to use the gRPC tools to generate code

I've read the tutorial and I'm able to generate the .cs file but it doesn't include any of my service or rpc definitions.
I've added protoc to my PATH and from inside the project directory.
protoc project1.proto --csharp_out="C:\output" --plugin=protoc-gen-grpc="c:\Users\me\.nuget\packages\grpc.tools\1.8.0\tools\windows_x64\grpc_csharp_plugin.exe"
No errors output in console
You need to add the --grpc_out command line option, e.g. add
--grpc_out="C:\output\"
Note that it won't write any files if you don't have any services.
Here's a complete example. From a root directory, create:
An empty output directory
A tools directory with protoc.exe and grpc_csharp_plugin.exe
A protos directory with test.proto as shown below:
test.proto:
syntax = "proto3";
service StackOverflowService {
rpc GetAnswer(Question) returns (Answer);
}
message Question {
string text = 1;
string user = 2;
repeated string tags = 3;
}
message Answer {
string text = 1;
string user = 2;
}
Then run (all on one line; I've broken it just for readability here):
tools\protoc.exe -I protos protos\test.proto --csharp_out=output
--grpc_out=output --plugin=protoc-gen-grpc=tools\grpc_csharp_plugin.exe
In the output directory, you'll find Test.cs and TestGrpc.cs
Just an idle comment here for other that find this, the documentation about this is terribly out of date and just flat out wrong.
Installing Grpc.Tools does not install anything in a packages folder; that is legacy behaviour which is no longer true even on windows.
When you install Grpc.Tools it will be hidden away in your local package cache, which you can see by calling:
$ dotnet nuget locals all --list
info : http-cache: /Users/doug/.local/share/NuGet/v3-cache
info : global-packages: /Users/doug/.nuget/packages/
info : temp: /var/folders/xx/s2hnzbrj3yn4hp1bg8q9gb_m0000gn/T/NuGetScratch
The binaries you want will be in one of these folders.
The easiest way to do this is to download the Grpc.Tools package directly from nuget, and install it locally.
I've hacked up this little helper script to do that, which works on windows/mac/linux, which may ease the difficulty of getting starting with this for others:
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Mono.Unix;
namespace BuildProtocol
{
public class Program
{
private const string ToolsUrl = "https://www.nuget.org/api/v2/package/Grpc.Tools/";
private const string Service = "Greeter";
private static string ProtocolPath = Path.Combine("..", "protos");
private static string Protocol = Path.Combine(ProtocolPath, "helloworld.proto");
private static string Output = Path.Combine("..", "Greeter");
public static void Main(string[] args)
{
RequireTools().Wait();
var protoc = ProtocPath();
var plugin = ProtocPluginPath();
Console.WriteLine($"Using: {protoc}");
Console.WriteLine($"Using: {plugin}");
var command = new string[]
{
$"-I{ProtocolPath}",
$"--csharp_out={Output}",
$"--grpc_out={Output}",
$"--plugin=protoc-gen-grpc=\"{plugin}\"",
Protocol,
};
Console.WriteLine($"Exec: {protoc} {string.Join(' ', command)}");
var process = new Process
{
StartInfo = new ProcessStartInfo
{
UseShellExecute = false,
FileName = protoc,
Arguments = string.Join(' ', command)
}
};
process.Start();
process.WaitForExit();
Console.WriteLine($"Completed status: {process.ExitCode}");
}
public static async Task RequireTools()
{
if (!Directory.Exists("Tools"))
{
Console.WriteLine("No local tools found, downloading binaries from nuget...");
Directory.CreateDirectory("Tools");
await DownloadTools();
ExtractTools();
}
}
private static void ExtractTools()
{
ZipFile.ExtractToDirectory(Path.Combine("Tools", "tools.zip"), Path.Combine("Tools", "bin"));
}
private static async Task DownloadTools()
{
using (var client = new HttpClient())
{
Console.WriteLine($"Fetching: {ToolsUrl}");
using (var result = await client.GetAsync(ToolsUrl))
{
if (!result.IsSuccessStatusCode) throw new Exception($"Unable to download tools ({result.StatusCode}), check URL");
var localArchive = Path.Combine("Tools", "tools.zip");
Console.WriteLine($"Saving to: {localArchive}");
File.WriteAllBytes(localArchive, await result.Content.ReadAsByteArrayAsync());
}
}
}
private static string ProtocPath()
{
var path = Path.Combine("Tools", "bin", "tools", DetermineArch(), "protoc");
RequireExecutablePermission(path);
return WithExeExtensionIfRequired(path);
}
private static string ProtocPluginPath()
{
var path = Path.Combine("Tools", "bin", "tools", DetermineArch(), "grpc_csharp_plugin");
RequireExecutablePermission(path);
return WithExeExtensionIfRequired(path);
}
private static void RequireExecutablePermission(string path)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return;
Console.WriteLine($"Ensuring +x on {path}");
var unixFileInfo = new UnixFileInfo(path);
unixFileInfo.FileAccessPermissions = FileAccessPermissions.UserRead | FileAccessPermissions.UserWrite | FileAccessPermissions.UserExecute;
}
private static string WithExeExtensionIfRequired(string path)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
path += ".exe";
}
return path;
}
private static string DetermineArch()
{
var arch = RuntimeInformation.OSArchitecture;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return WithArch("windows_", arch);
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return WithArch("macosx_", arch);
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return WithArch("linux_", arch);
}
throw new Exception("Unable to determine runtime");
}
private static string WithArch(string platform, Architecture arch)
{
switch (arch)
{
case Architecture.X64:
return $"{platform}x86";
case Architecture.X86:
return $"{platform}x64";
default:
throw new ArgumentOutOfRangeException(nameof(arch), arch, null);
}
}
}
}
the following approach helped me :
Create a gRPC client and server in ASP.NET Core
in project, where .proto file located, edit the .csproj file
<ItemGroup>
....
<Protobuf Include="Shipping.proto" GrpcServices="Server" />
</ItemGroup>
rebuild the project, the all necessary .cs files will be added automaticaly
\obj\Debug\[TARGET_FRAMEWORK]\Shipping.cs
\obj\Debug\[TARGET_FRAMEWORK]\ShippingGrpc.cs

How to start Azure Storage Emulator from within a program

I have some unit tests that use Azure Storage. When running these locally, I want them to use the Azure Storage emulator which is part of the Azure SDK v1.5. If the emulator isn't running, I want it to be started.
To start the emulator from the command line, I can use this:
"C:\Program Files\Windows Azure SDK\v1.5\bin\csrun" /devstore
This works fine.
When I try to start it using this C# code, it crashes:
using System.IO;
using System.Diagnostics;
...
ProcessStartInfo processToStart = new ProcessStartInfo()
{
FileName = Path.Combine(SDKDirectory, "csrun"),
Arguments = "/devstore"
};
Process.Start(processToStart);
I've tried fiddling with a number of ProcessStartInfo settings, but nothing seems to work. Is anybody else having this problem?
I've checked the Application Event Log and found the following two entries:
Event ID: 1023
.NET Runtime version 2.0.50727.5446 - Fatal Execution Engine Error (000007FEF46B40D2) (80131506)
Event ID: 1000
Faulting application name: DSService.exe, version: 6.0.6002.18312, time stamp: 0x4e5d8cf3
Faulting module name: mscorwks.dll, version: 2.0.50727.5446, time stamp: 0x4d8cdb54
Exception code: 0xc0000005
Fault offset: 0x00000000001de8d4
Faulting process id: 0x%9
Faulting application start time: 0x%10
Faulting application path: %11
Faulting module path: %12
Report Id: %13
Updated 7/12/2022:
If you are running Visual Studio 2022, azurite.exe is the replacement for the now-deprecated AzureStorageEmulator.exe which can be found here:
C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\Extensions\Microsoft\Azure Storage Emulator\azurite.exe
NB: if you are running Professional (or another) Edition, you'll need to replace Community with Professional (or the appropriate edition name) in the path.
Updated 1/19/2015:
After doing more testing (i.e., running several builds), I've discovered that WAStorageEmulator.exe's status API is actually broken in a couple of significant ways (which may or may not have impact on how you use it).
The status reports False even when an existing process is running if the user differs between the existing running process and the user used to launch the status process. This incorrect status report will lead to a failure to launch the process that looks like this:
C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator>WAStorageEmulator.exe status
Windows Azure Storage Emulator 3.4.0.0 command line tool
IsRunning: False
BlobEndpoint: http://127.0.0.1:10000/
QueueEndpoint: http://127.0.0.1:10001/
TableEndpoint: http://127.0.0.1:10002/
C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator>WAStorageEmulator.exe start
Windows Azure Storage Emulator 3.4.0.0 command line tool
Error: Port conflict with existing application.
Additionally, the status command appears only to report the endpoints specified in WAStorageEmulator.exe.config, not those of the existing running process. I.e., if you start the emulator, then make a change to the config file, and then call status, it will report the endpoints listed in the config.
Given all of these caveats, it may, in fact, simply be better to use the original implementation as it appears to be more reliable.
I will leave both so others can choose whichever solution works for them.
Updated 1/18/2015:
I have fully rewritten this code to properly leverage WAStorageEmulator.exe's status API per #RobertKoritnik's request.
public static class AzureStorageEmulatorManager
{
public static bool IsProcessRunning()
{
bool status;
using (Process process = Process.Start(StorageEmulatorProcessFactory.Create(ProcessCommand.Status)))
{
if (process == null)
{
throw new InvalidOperationException("Unable to start process.");
}
status = GetStatus(process);
process.WaitForExit();
}
return status;
}
public static void StartStorageEmulator()
{
if (!IsProcessRunning())
{
ExecuteProcess(ProcessCommand.Start);
}
}
public static void StopStorageEmulator()
{
if (IsProcessRunning())
{
ExecuteProcess(ProcessCommand.Stop);
}
}
private static void ExecuteProcess(ProcessCommand command)
{
string error;
using (Process process = Process.Start(StorageEmulatorProcessFactory.Create(command)))
{
if (process == null)
{
throw new InvalidOperationException("Unable to start process.");
}
error = GetError(process);
process.WaitForExit();
}
if (!String.IsNullOrEmpty(error))
{
throw new InvalidOperationException(error);
}
}
private static class StorageEmulatorProcessFactory
{
public static ProcessStartInfo Create(ProcessCommand command)
{
return new ProcessStartInfo
{
FileName = #"C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator\WAStorageEmulator.exe",
Arguments = command.ToString().ToLower(),
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
}
}
private enum ProcessCommand
{
Start,
Stop,
Status
}
private static bool GetStatus(Process process)
{
string output = process.StandardOutput.ReadToEnd();
string isRunningLine = output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).SingleOrDefault(line => line.StartsWith("IsRunning"));
if (isRunningLine == null)
{
return false;
}
return Boolean.Parse(isRunningLine.Split(':').Select(part => part.Trim()).Last());
}
private static string GetError(Process process)
{
string output = process.StandardError.ReadToEnd();
return output.Split(':').Select(part => part.Trim()).Last();
}
}
And the corresponding tests:
[TestFixture]
public class When_starting_process
{
[Test]
public void Should_return_started_status()
{
if (AzureStorageEmulatorManager.IsProcessRunning())
{
AzureStorageEmulatorManager.StopStorageEmulator();
Assert.That(AzureStorageEmulatorManager.IsProcessRunning(), Is.False);
}
AzureStorageEmulatorManager.StartStorageEmulator();
Assert.That(AzureStorageEmulatorManager.IsProcessRunning(), Is.True);
}
}
[TestFixture]
public class When_stopping_process
{
[Test]
public void Should_return_stopped_status()
{
if (!AzureStorageEmulatorManager.IsProcessRunning())
{
AzureStorageEmulatorManager.StartStorageEmulator();
Assert.That(AzureStorageEmulatorManager.IsProcessRunning(), Is.True);
}
AzureStorageEmulatorManager.StopStorageEmulator();
Assert.That(AzureStorageEmulatorManager.IsProcessRunning(), Is.False);
}
}
Original post:
I took Doug Clutter's and Smarx's code one step further and created a utility class:
The code below has been updated to work on both Windows 7 and 8 and now points at the new storage emulator path as of SDK 2.4.**
public static class AzureStorageEmulatorManager
{
private const string _windowsAzureStorageEmulatorPath = #"C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator\WAStorageEmulator.exe";
private const string _win7ProcessName = "WAStorageEmulator";
private const string _win8ProcessName = "WASTOR~1";
private static readonly ProcessStartInfo startStorageEmulator = new ProcessStartInfo
{
FileName = _windowsAzureStorageEmulatorPath,
Arguments = "start",
};
private static readonly ProcessStartInfo stopStorageEmulator = new ProcessStartInfo
{
FileName = _windowsAzureStorageEmulatorPath,
Arguments = "stop",
};
private static Process GetProcess()
{
return Process.GetProcessesByName(_win7ProcessName).FirstOrDefault() ?? Process.GetProcessesByName(_win8ProcessName).FirstOrDefault();
}
public static bool IsProcessStarted()
{
return GetProcess() != null;
}
public static void StartStorageEmulator()
{
if (!IsProcessStarted())
{
using (Process process = Process.Start(startStorageEmulator))
{
process.WaitForExit();
}
}
}
public static void StopStorageEmulator()
{
using (Process process = Process.Start(stopStorageEmulator))
{
process.WaitForExit();
}
}
}
This program worked fine for me. Give it a try, and if it works for you too, work backwards from there. (What about your app is different from this?)
using System.Diagnostics;
public class Program
{
public static void Main() {
Process.Start(#"c:\program files\windows azure sdk\v1.5\bin\csrun", "/devstore").WaitForExit();
}
}
The file name in v4.6 is "AzureStorageEmulator.exe". The full path is: "C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator\AzureStorageEmulator.exe"
For Windows Azure Storage Emulator v5.2, the following helper class can be used to start the emulator:
using System.Diagnostics;
public static class StorageEmulatorHelper {
/* Usage:
* ======
AzureStorageEmulator.exe init : Initialize the emulator database and configuration.
AzureStorageEmulator.exe start : Start the emulator.
AzureStorageEmulator.exe stop : Stop the emulator.
AzureStorageEmulator.exe status : Get current emulator status.
AzureStorageEmulator.exe clear : Delete all data in the emulator.
AzureStorageEmulator.exe help [command] : Show general or command-specific help.
*/
public enum StorageEmulatorCommand {
Init,
Start,
Stop,
Status,
Clear
}
public static int StartStorageEmulator() {
return ExecuteStorageEmulatorCommand(StorageEmulatorCommand.Start);
}
public static int StopStorageEmulator() {
return ExecuteStorageEmulatorCommand(StorageEmulatorCommand.Stop);
}
public static int ExecuteStorageEmulatorCommand(StorageEmulatorCommand command) {
var start = new ProcessStartInfo {
Arguments = command.ToString(),
FileName = #"C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator\AzureStorageEmulator.exe"
};
var exitCode = executeProcess(start);
return exitCode;
}
private static int executeProcess(ProcessStartInfo startInfo) {
int exitCode = -1;
try {
using (var proc = new Process {StartInfo = startInfo}) {
proc.Start();
proc.WaitForExit();
exitCode = proc.ExitCode;
}
}
catch {
//
}
return exitCode;
}
}
[Thanks to huha for the boilerplate code to execute a shell command.]
FYI - The 1.6 default location is C:\Program Files\Windows Azure Emulator\emulator as stated on the MSDN docs.
We are running into the same issue. We have the concept of a "smoke test" which runs between groups of tests, and which ensure the environment is in a good state before the next group starts. We have a .cmd file that kicks off the smoke tests, and it works just fine starting the devfabric emulator, but the devstore emulator only runs as long as the .cmd process runs.
Apparently the implementation of the DSServiceSQL.exe is different than DFService.exe. DFService seems to run like a windows service - kick it off, and it keeps running. DSServiceSQL dies as soon as the process that started it dies.
I uninstalled all of the Windows Azure bits:
WA SDK v1.5.20830.1814
WA Tools for Visual Studio: v1.5.40909.1602
WA AppFabric: v1.5.37
WA AppFabric: v2.0.224
Then, I downloaded and installed everything using the unified installer. Everything came back except the AppFabric v2. All the version numbers are the same. Reran my tests and still having a problem.
And then...(this is weird)...it would work every now and then. Rebooted the machine and now it works. Have shutdown and rebooted a number of times now...and it just works. (sigh)
Thanks to everyone who provided feedback and/or ideas!
The final code is:
static void StartAzureStorageEmulator()
{
ProcessStartInfo processStartInfo = new ProcessStartInfo()
{
FileName = Path.Combine(SDKDirectory, "csrun.exe"),
Arguments = "/devstore",
};
using (Process process = Process.Start(processStartInfo))
{
process.WaitForExit();
}
}
maybe caused by file not found?
try this
FileName = Path.Combine(SDKDirectory, "csrun.exe")
Here we go: Pass the string "start" to the method ExecuteWAStorageEmulator().
The NUnit.Framework is used for the Assert only.
using System.Diagnostics;
using NUnit.Framework;
private static void ExecuteWAStorageEmulator(string argument)
{
var start = new ProcessStartInfo
{
Arguments = argument,
FileName = #"c:\Program Files (x86)\Microsoft SDKs\Windows Azure\Storage Emulator\WAStorageEmulator.exe"
};
var exitCode = ExecuteProcess(start);
Assert.AreEqual(exitCode, 0, "Error {0} executing {1} {2}", exitCode, start.FileName, start.Arguments);
}
private static int ExecuteProcess(ProcessStartInfo start)
{
int exitCode;
using (var proc = new Process { StartInfo = start })
{
proc.Start();
proc.WaitForExit();
exitCode = proc.ExitCode;
}
return exitCode;
}
See also my new self-answered question
There's now a neat little NuGet package to assist with starting/stopping the Azure Storage Emulator programmatically: RimDev.Automation.StorageEmulator.
The source code is available in this GitHub repository, but you can essentially do things like this:
if(!AzureStorageEmulatorAutomation.IsEmulatorRunning())
{
AzureStorageEmulatorAutomation emulator = new AzureStorageEmulatorAutomation();
emulator.Start();
// Even clear some things
emulator.ClearBlobs();
emulator.ClearTables();
emulator.ClearQueues();
emulator.Stop();
}
It feels like the cleanest option to me.

C# IO Reading and Writing file in use error

I have a library that handles reading and writing a cache file. This library is used by a Windows Service and several instances of a console application on the same machine. The console application runs when a user logs in.
I am getting occasional IO errors saying the cache file is in use by another process. I assume that collisions are occurring between the different application instances and service trying to read and write at the same time.
Is there a way to lock the file when it is in use and force all other requests to "wait in line" to access the file?
private void SaveCacheToDisk(WindowsUser user) {
string serializedCache = SerializeCache(_cache);
//encryt
serializedCache = AES.Encrypt(serializedCache);
string path = user == null ? ApplicationHelper.CacheDiskPath() :
_registry.GetCachePath(user);
string appdata = user == null ? ApplicationHelper.ClientApplicationDataFolder() :
_registry.GetApplicationDataPath(user);
if (Directory.Exists(appdata) == false) {
Directory.CreateDirectory(appdata);
}
if (File.Exists(path) == false) {
using (FileStream stream = File.Create(path)) { }
}
using (FileStream stream = File.Open(path, FileMode.Truncate)) {
using (StreamWriter writer = new StreamWriter(stream)) {
writer.Write(serializedCache);
}
}
}
private string ReadCacheFromDisk(WindowsUser user) {
//cache file path
string path = user == null ? ApplicationHelper.CacheDiskPath() :
_registry.GetCachePath(user);
using (FileStream stream = File.Open(path, FileMode.Open)) {
using (StreamReader reader = new StreamReader(stream)) {
string serializedCache = reader.ReadToEnd();
//decrypt
serializedCache = AES.Decrypt(serializedCache);
return serializedCache;
}
}
}
Sure, you could use a mutex and permit access only when holding the mutex.
You could use a cross-process EventWaitHandle. This lets you create and use a WaitHandle that's identified across processes by name. A thread is notified when it's its turn, does some work, and then indicates it's done allowing another thread to proceed.
Note that this only works if every process/thread is referring to the same named WaitHandle.
The EventWaitHandle constructors with strings in their signature create named system synchronization events.
One option you could consider is having the console applications route their file access through the service, that way there's only one process accessing the file and you can synchronise access to it there.
One way of implementing this is by remoting across an IPC channel (and here's another example from weblogs.asp.net). We used this technique in a project for the company I work for and it works well, with our specific case providing a way for a .net WebService to talk to a Windows Service running on the same machine.
Sample based on the weblogs.asp.net example
Basically what you need to do with the code below is create a Solution, add two Console Apps (one called "Server" and the other called "Client" and one Library to it. Add a reference to the Library to both console apps, paste the code below in and add a reference to System.Runtime.Remoting to both Server & Console.
Run the Server app, then run the client app. Observe the fact that the server app has a message passed to it by the client. You can extend this to any number of messages/tasks
// Server:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
namespace RemotingSample
{
public class Server
{
public Server()
{
}
public static int Main(string[] args)
{
IpcChannel chan = new IpcChannel("Server");
//register channel
ChannelServices.RegisterChannel(chan, false);
//register remote object
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(RemotingSample.RemoteObject),
"RemotingServer",
WellKnownObjectMode.SingleCall);
Console.WriteLine("Server Activated");
Console.ReadLine();
return 0;
}
}
}
// Client:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using RemotingSample;
namespace RemotingSample
{
public class Client
{
public Client()
{
}
public static int Main(string[] args)
{
IpcChannel chan = new IpcChannel("Client");
ChannelServices.RegisterChannel(chan);
RemoteObject remObject = (RemoteObject)Activator.GetObject(
typeof(RemotingSample.RemoteObject),
"ipc://Server/RemotingServer");
if (remObject == null)
{
Console.WriteLine("cannot locate server");
}
else
{
remObject.ReplyMessage("You there?");
}
return 0;
}
}
}
// Shared Library:
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
namespace RemotingSample
{
public class RemoteObject : MarshalByRefObject
{
public RemoteObject()
{
Console.WriteLine("Remote object activated");
}
public String ReplyMessage(String msg)
{
Console.WriteLine("Client : " + msg);//print given message on console
return "Server : I'm alive !";
}
}
}
Check out the TextWriter.Synchronized method.
http://msdn.microsoft.com/en-us/library/system.io.textwriter.synchronized.aspx
This should let you do this:
TextWriter.Synchronized(writer).Write(serializedCache);

Categories

Resources