Compile to module with Roslyn - roslyn

I need to compile any C# or VB.NET project to .NetModule. I have following sample code which emits DLLs, Need some help to modify following to get .NetModules out from .csproj
Thanks in advance.
// Required Microsoft.CodeAnalysis 1.3.0
class Program
{
static void Main(string[] args)
{
try
{
//Please copy provide a path to a .csproj
CompileProject(#"C:\WebGoat\WebGoat.NET.csproj", #"C:\tempout").Wait();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
async static Task<string> CompileProject(string projectFilePath, string outputFolderPath)
{
using (var workspace = MSBuildWorkspace.Create())
{
var project = workspace.OpenProjectAsync(projectFilePath).Result;
await Emit(project, outputFolderPath);
return Path.GetFileName(project.OutputFilePath);
}
}
async static Task Emit(Project project, string outputFolderPath)
{
Directory.CreateDirectory(outputFolderPath);
var compilation = await project.GetCompilationAsync();
var outputFilePath = Path.Combine(outputFolderPath, Path.GetFileName(project.OutputFilePath));
var pdbFilePath = Path.ChangeExtension(outputFilePath, "pdb");
var compilationStatus = compilation.Emit(outputFilePath, pdbPath: pdbFilePath);
if (!compilationStatus.Success)
{
Console.WriteLine("Failed.");
}
else
{
Console.WriteLine("Pass.");
}
}

I believe you're looking for CompilationOptions.OutputKind and specifically OutputKind.NetModule.
Something similar to the following should work:
var project = workspace.OpenProjectAsync(projectFilePath).Result;
var options = project.CompilationOptions;
var netModuleOptions = options.WithOutputKind(OutputKind.NetModule);
var projectWithOptions = project.WithCompilationOptions(netModuleOptions);
Now you should be able to get a compilation and emit it as you normally would.

Following fixed the issue
var project = workspace.OpenProjectAsync(projectFilePath).Result;
var options = project.CompilationOptions;
options = options.WithOutputKind(OutputKind.NetModule).WithPlatform(Platform.AnyCpu);
project = project.WithCompilationOptions(options);
var moduleCompilation = await project.GetCompilationAsync();

Related

How to unit test a repository and mock db with moq

I am onboarding alone on an existing project that do not have any unit test. My first goal before any refactoring is to cover 100% of the code. I would like to avoid any regression.
I have read how do I mock sqlconnection or should I refactor the code? but my case as you can see below is quite different cause I need to do more than stub simply the sqlConnection. Basically, I need to mock the db. I would like to know the best approach to achieve it.
(By the way, I do not want to use any ORM such as Entity).
Below the code of the repository :
public class HotelRepository : IHotelRepository
{
private readonly IDbConnection _dbConnection;
private readonly ILogService _loggerService;
public HotelRepository(IDbConnection dbConnection, ILogService loggerService)
{
_dbConnection = dbConnection;
_loggerService = loggerService;
}
public HotelDo GetByRid(string rid)
{
return Find(rid).FirstOrDefault();
}
public List<HotelDo> Find(string text)
{
try
{
_dbConnection.Open();
var items = new List<HotelDo>();
using (var command = _dbConnection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "dbo.HotelSearchByRidOrName";
command.Parameters.Add(new SqlParameter("#Text", text));
using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
while (reader.Read())
{
items.Add(new HotelDo()
{
Name = SqlExtension.ReaderToStringConverter(reader["Name"]),
Id = SqlExtension.ReaderToIntConverter(reader["Id"]),
Rid = SqlExtension.ReaderToStringConverter(reader["RIDHotel"]),
IdPms = SqlExtension.ReaderToNullableIntConverter(reader["IdPms"]),
LinkResaWeb = SqlExtension.ReaderToStringConverter(reader["LinkResaWeb"]),
LinkPms = SqlExtension.ReaderToStringConverter(reader["LinkPms"]),
IdBrand = SqlExtension.ReaderToNullableIntConverter(reader["IdBrand"]) ?? 0,
IsOnline = SqlExtension.ReaderToBoolConverter(reader["IsOnline"]) ?? false,
CodeCountry = SqlExtension.ReaderToStringConverter(reader["CodeCountry"])
});
}
}
}
return items;
}
catch (Exception e)
{
var errorMessage = $"HotelRepository Find, text {text} ";
_loggerService.Trace(LogSeverity.Error, errorMessage, e);
throw new DalException() { Source = errorMessage, };
}
finally
{
_dbConnection.Close();
}
}
public List<HotelDo> GetAll()
{
try
{
_dbConnection.Open();
var items = new List<HotelDo>();
using (var command = _dbConnection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "dbo.HotelGetAll";
using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
while (reader.Read())
{
bool.TryParse(reader["IsOnline"].ToString(), out var isOnline);
items.Add(new HotelDo()
{
Id = SqlExtension.ReaderToIntConverter(reader["Id"]),
Rid = SqlExtension.ReaderToStringConverter(reader["RIDHotel"]),
Name = SqlExtension.ReaderToStringConverter(reader["Name"]),
CodeCountry = SqlExtension.ReaderToStringConverter(reader["CodeCountry"]),
LinkPms = SqlExtension.ReaderToStringConverter(reader["LinkPms"]),
IdPms = SqlExtension.ReaderToNullableIntConverter(reader["IdPms"]),
IdBrand = SqlExtension.ReaderToNullableIntConverter(reader["IdBrand"]) ?? 0,
LinkResaWeb = SqlExtension.ReaderToStringConverter(reader["LinkResaWeb"]),
IsOnline = isOnline
});
}
}
}
return items;
}
catch (Exception e)
{
var errorMessage = $"HotelRepository GetAllHotels";
_loggerService.Trace(LogSeverity.Error, errorMessage, e);
throw new DalException() { Source = errorMessage, };
}
finally
{
_dbConnection.Close();
}
}
}
Thank you for your help
So based on what you've got I've set up a test frame for you to follow. I've removed less important components for breviety.
Before you you jump in I just want to give my 2 cents, if you don't know how to do this sort of thing you seem to be more or less in a junior position or less experiance with C# and also found your self in a more or less mature company that doesn't care about the development deparment, since an uncovered project just get's thrown your way says you don't have alot of resources to go about to imrpove the code base.
Test only the things that have business value (can you put a price on the piece of logic if it brakes)
Delivering stuff faster will make you look better as a programmer (noone in the business gives a damn about test covarage)
Study hard, get your experiance, good reputation and don't be afraid to get the hell out of there as soon as you start getting bored.
Main
void Main()
{
var idIndex = 0;
var ids = new string[] { "1", "2" };
var mockDataReader = new Mock<IDataReader>();
mockDataReader.SetupSequence(x => x.Read()).Returns(true).Returns(true).Returns(false);
mockDataReader.SetupGet(x => x["Id"]).Returns(() => ids[idIndex]).Callback(() => idIndex++);
var mockParameters = new Mock<IDataParameterCollection>();
var mockCommand = new Mock<IDbCommand>();
mockCommand.SetupGet(x => x.Parameters).Returns(mockParameters.Object);
mockCommand.Setup(x => x.ExecuteReader(CommandBehavior.CloseConnection)).Returns(mockDataReader.Object);
var mockConnection = new Mock<IDbConnection>();
mockConnection.Setup(x => x.CreateCommand()).Returns(mockCommand.Object);
var repo = new HotelRepository(mockConnection.Object);
var result = repo.Find("search");
Assert.Equal("1", result[0].Id);
Assert.Equal("2", result[1].Id);
}
Repository
public class HotelRepository
{
private readonly IDbConnection _dbConnection;
public HotelRepository(IDbConnection dbConnection)
{
_dbConnection = dbConnection;
}
public List<Pony> Find(string text)
{
_dbConnection.Open();
var items = new List<Pony>();
using (var command = _dbConnection.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "dbo.HotelSearchByRidOrName";
command.Parameters.Add(new SqlParameter("#Text", text));
using (var reader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
while (reader.Read())
{
items.Add(new Pony
{
Id = reader["Id"].ToString()
});
}
}
}
return items;
}
}
Just a dumb o'l pony
public class Pony {
public string Id { get; set; }
}

Roslyn - save edited document to physical solution

I'm trying to iterate through all documents, change some code and then rewrite analyzed document with new Syntax. The only problem I have it's that i don't know how to save changed document via workspace. After my code execution nothing happens.
var manager = new AnalyzerManager(slnPath);
foreach (var project in manager.Projects.Values)
{
var workspace = project.GetWorkspace();
var sln = workspace.CurrentSolution;
foreach (var msBuildProject in sln.Projects)
{
foreach (var document in msBuildProject.Documents)
{
var tree = await document.GetSyntaxTreeAsync();
var root = tree.GetRoot();
var compilation = await msBuildProject.GetCompilationAsync();
var walker = new CustomRewriter(compilation, tree);
var newRoot = walker.Visit(root);
if (!walker.Edited)
continue;
var editor = await DocumentEditor.CreateAsync(document);
editor.ReplaceNode(root, newRoot);
var newDocument = editor.GetChangedDocument();
workspace.TryApplyChanges(newDocument.Project.Solution);
break;
}
}
}
EDIT: Workspace is fetched with help of the Buildalyzer package.
Buildalyzer uses the adhoc workspace which does not persist changes. You want to use the MSBuildWorkspace.
You should have a project file that looks something like this
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="2.9.8" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="3.3.1" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="3.3.1" />
</ItemGroup>
</Project>
And then this is how I would load your solution and run your rewriter
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.MSBuild;
class Program {
static async Task Main(string[] args) {
// Attempt to set the version of MSBuild.
var visualStudioInstances = MSBuildLocator.QueryVisualStudioInstances().ToArray();
var instance = visualStudioInstances.Length == 1
// If there is only one instance of MSBuild on this machine, set that as the one to use.
? visualStudioInstances[0]
// Handle selecting the version of MSBuild you want to use.
: SelectVisualStudioInstance(visualStudioInstances);
Console.WriteLine($"Using MSBuild at '{instance.MSBuildPath}' to load projects.");
// NOTE: Be sure to register an instance with the MSBuildLocator
// before calling MSBuildWorkspace.Create()
// otherwise, MSBuildWorkspace won't MEF compose.
MSBuildLocator.RegisterInstance(instance);
using (var workspace = MSBuildWorkspace.Create()) {
// Print message for WorkspaceFailed event to help diagnosing project load failures.
workspace.WorkspaceFailed += (o, e) => Console.WriteLine(e.Diagnostic.Message);
var solutionPath = args[0];
Console.WriteLine($"Loading solution '{solutionPath}'");
// Attach progress reporter so we print projects as they are loaded.
var solution = await workspace.OpenSolutionAsync(solutionPath, new ConsoleProgressReporter());
Console.WriteLine($"Finished loading solution '{solutionPath}'");
// Run your custome re-writer on the loaded solution
foreach (var msBuildProject in solution.Projects) {
foreach (var document in msBuildProject.Documents) {
var tree = await document.GetSyntaxTreeAsync();
var root = tree.GetRoot();
var compilation = await msBuildProject.GetCompilationAsync();
var walker = new CustomRewriter(compilation, tree);
var newRoot = walker.Visit(root);
if (!walker.Edited)
continue;
var editor = await DocumentEditor.CreateAsync(document);
editor.ReplaceNode(root, newRoot);
var newDocument = editor.GetChangedDocument();
workspace.TryApplyChanges(newDocument.Project.Solution);
break;
}
}
}
}
private static VisualStudioInstance SelectVisualStudioInstance(VisualStudioInstance[] visualStudioInstances) {
Console.WriteLine("Multiple installs of MSBuild detected please select one:");
for (int i = 0; i < visualStudioInstances.Length; i++) {
Console.WriteLine($"Instance {i + 1}");
Console.WriteLine($" Name: {visualStudioInstances[i].Name}");
Console.WriteLine($" Version: {visualStudioInstances[i].Version}");
Console.WriteLine($" MSBuild Path: {visualStudioInstances[i].MSBuildPath}");
}
while (true) {
var userResponse = Console.ReadLine();
if (int.TryParse(userResponse, out int instanceNumber) &&
instanceNumber > 0 &&
instanceNumber <= visualStudioInstances.Length) {
return visualStudioInstances[instanceNumber - 1];
}
Console.WriteLine("Input not accepted, try again.");
}
}
private class ConsoleProgressReporter : IProgress<ProjectLoadProgress> {
public void Report(ProjectLoadProgress loadProgress) {
var projectDisplay = Path.GetFileName(loadProgress.FilePath);
if (loadProgress.TargetFramework != null) {
projectDisplay += $" ({loadProgress.TargetFramework})";
}
Console.WriteLine($"{loadProgress.Operation,-15} {loadProgress.ElapsedTime,-15:m\\:ss\\.fffffff} {projectDisplay}");
}
}
}

Initializing DefaultHttpContext.Response.Body to MemoryStream throws NullReferencePointer

I am working on implementation of global exception middleware and I would like to cover middleware with unit tests. Below you see how far I got.
This is a code of unit test.
[Fact]
public async Task MethodName_StateUnderTest_ExpectedBehavior()
{
//Arrange
IExceptionHandlerFeature exceptionHandlerFeature = new ExceptionHandlerFeature {Error = new NotFoundException()};
IFeatureCollection features = new FeatureCollection();
features.Set(exceptionHandlerFeature);
var context = new DefaultHttpContext(features);
context.Response.Body = new MemoryStream();
//Act
await ExceptionMiddleware.HandleException(context);
//Assert
context.Response.StatusCode.Should().Be((int) HttpStatusCode.NotFound);
}
This is a code of ExceptionMiddleware.Handle method
public static async Task HandleException(HttpContext context)
{
var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
if (contextFeature == null)
{
return;
}
if (contextFeature.Error is AppValidationException validationException)
{
context.Response.StatusCode = (int) HttpStatusCode.BadRequest;
var failures = JsonConvert.SerializeObject(validationException.Failures);
await context.Response.WriteAsync(
new ErrorDetails
{
StatusCode = context.Response.StatusCode,
Message = contextFeature.Error.Message,
StackTrace = contextFeature.Error.StackTrace,
Detail = failures
}.ToString());
return;
}
context.Response.StatusCode = (int) ResolveStatusCodeFromExceptionType(contextFeature.Error);
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(
new ErrorDetails
{
StatusCode = context.Response.StatusCode,
Message = contextFeature.Error.Message,
StackTrace = contextFeature.Error.StackTrace
}.ToString());
}
Test is crushing on line
context.Response.Body = new MemoryStream();
According to this question, everything should be fine but I still can't initialize property Body.
Test Projects target framework is .Net Core 3.0.
ExceptionMiddleware.cs is in project with set target framework .NET Standard 2.1 and it is Class Library.
StackTrace of exception is really short:
at
Microsoft.AspNetCore.Http.Internal.DefaultHttpResponse.set_Body(Stream
value) at
Itixis.Shared.API.Tests.ExceptionMiddlewareTests.d__0.MoveNext()
in
C:\Users\daniel.rusnok\source\repos\Itixis\Itixis.Shared\Tests\Itixis.Shared.API.Tests\ExceptionMiddlewareTests.cs:line
35
By manually setting the features you are removing the ones set by default, which causes your null error.
public DefaultHttpContext()
: this(new FeatureCollection())
{
Features.Set<IHttpRequestFeature>(new HttpRequestFeature());
Features.Set<IHttpResponseFeature>(new HttpResponseFeature());
Features.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(Stream.Null));
}
public DefaultHttpContext(IFeatureCollection features)
{
_features.Initalize(features);
_request = new DefaultHttpRequest(this);
_response = new DefaultHttpResponse(this);
}
Source
Note the request, response and response body features added in the default constructor.
Either try recreating what was done in the default constructor by also adding the required features,
IExceptionHandlerFeature exceptionHandlerFeature = new ExceptionHandlerFeature {Error = new NotFoundException()};
IFeatureCollection features = new FeatureCollection();
features.Set(exceptionHandlerFeature);
features.Set<IHttpRequestFeature>(new HttpRequestFeature());
features.Set<IHttpResponseFeature>(new HttpResponseFeature());
features.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(Stream.Null));
var context = new DefaultHttpContext(features);
//...
or remove the manual feature and use the default constructor.
[Fact]
public async Task MethodName_StateUnderTest_ExpectedBehavior() {
//Arrange
var context = new DefaultHttpContext();
context.Response.Body = new MemoryStream();
//Act
await ExceptionMiddleware.HandleException(context);
//Assert
context.Response.StatusCode.Should().Be((int) HttpStatusCode.NotFound);
}
and it should work as expected.

Unity PerRequestLifetimeManager UnitTest

I have following Unity configuration:
public static void RegisterTypes(IUnityContainer container)
{
...
container.RegisterType<IRootDatabaseContext, RootEntities>(new PerRequestLifetimeManager());
...
}
And everything works fine. But when I also want to test this method:
[TestMethod]
public void AssertUnityConfigAreValid()
{
using (var container = new UnityContainer())
{
UnityConfig.RegisterTypes(container);
foreach (var registration in container.Registrations)
{
container.Resolve(registration.RegisteredType, registration.Name);
}
}
}
And when I run this test I get an error:
InvalidOperationException - Operation is not valid due to the current state of the object.
How can I replace LifeTimeManager into Unit test from PerRequestLifetimeManager to another one?
I've just found the solution for this issue:
Just need to add following code before this line in test
UnityConfig.RegisterTypes(container);
var request = new HttpRequest("fake", "https://127.0.0.1", null);
var respons = new HttpResponse(new StringWriter());
var context = new HttpContext(request, respons);
HttpContext.Current = context;

Iron Python throws exception on testing environment

I'm having a code which runs Iron Python scripts on VS 2010. Every time a test completes I get an exception of type ObjectDisposedException, with the description: Cannot write to a closed TextWriter. I can't see the stack trace. I'm accessing the scripts via this wrapper:
public static class PythonWrapper
{
public static dynamic GetClient(string clientName, string clientType)
{
var file = string.Format(#"{0}\Python\webcore.eas", Directory.GetCurrentDirectory());
dynamic result = null;
var ipy = GetRuntime();
var engine = ipy.GetEngine("py");
ScriptScope clientScope = engine.CreateScope();
if(File.Exists(file))
{
clientScope.SetVariable("asm", Assembly.Load(ServiceManager.Get<FileEncryptionSevice>().Decrypt(file)));
string dllWrapper = string.Format("import clr\n" +
"clr.AddReference(asm)\n" +
"from Clients.{0} import {1}\n" +
"del clr", clientName, clientType);
var src = engine.CreateScriptSourceFromString(dllWrapper);
var compiled = src.Compile();
compiled.Execute(clientScope);
result = clientScope.GetVariable(clientType);
}
else
{
var scope = ipy.UseFile(string.Format(#"{0}\Python\Clients\{1}.py", Directory.GetCurrentDirectory(),clientName));
result = scope.GetVariable(clientType);
}
return result;
}
private static ScriptRuntime GetRuntime()
{
var result = Python.CreateRuntime();
var engine = Python.GetEngine(result);
var baseFolder = string.Format(#"{0}\Python\", Directory.GetCurrentDirectory());
engine.SetSearchPaths(new[] {
string.Format("{0}", baseFolder),
string.Format(#"{0}\Lib\", Directory.GetCurrentDirectory())
});
return result;
}
}
I guess I'm attempting to access a disposed object but none of the scripting objects is IDisposable. I've also tried calling ScriptRuntime.ShutDown at the end of each test, but it only has the test stuck.
Please help me.
Kind regards,
Izhar
Turns out it was a simple bug of setting an output stream for the iron python. The following code solved the error
private static void SetOutputStream(ScriptEngine engine)
{
ScriptScope sys = engine.GetSysModule();
sys.SetVariable("stdout", new PythonStreamWrapper(LogLevel.Debug));
sys.SetVariable("stderr", new PythonStreamWrapper(LogLevel.Debug));
}
public class PythonStreamWrapper
{
private readonly ILogger _logger = LoggerService.GetLogger("Obj.Gen");
private LogLevel _logLevel;
public PythonStreamWrapper(LogLevel logLevel)
{
_logLevel = logLevel;
}
public void write(string text)
{
if (text.Trim() == "") return;
_logger.Write(_logLevel, text);
}
public int softspace
{
get;
set;
}
}