I want to populate this type of using statement through Roslyn.
Using(var logger = new MethodLogger("someparam"))
{
}
How can I generate it..
I am trying this SyntaxFactory.UsingStatement
For stuff like this I use roslynquoter. It generates roslyn calls out of C# code. For your case it returns something like this:
SyntaxFactory
.UsingStatement(SyntaxFactory.Block()/* the code inside the using block */)
.WithDeclaration(SyntaxFactory
.VariableDeclaration(SyntaxFactory.IdentifierName("var"))
.WithVariables(SyntaxFactory.SingletonSeparatedList(SyntaxFactory
.VariableDeclarator(SyntaxFactory.Identifier("logger"))
.WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory
.ObjectCreationExpression(SyntaxFactory.IdentifierName(#"MethodLogger"))
.WithArgumentList(/* arguments for MethodLogger ctor */)))
Related
I writed a SourceGenerator, but how do I test it?
Main issue is how to imitate GeneratorExecutionContext (or just Compilation inside it) which generator gets into Execute method. I think there is a proper way to make fake SyntaxTrees for unit testing, but I cant find it. There are many articles about source generators itself, but none of them explain how to test generators.
You should look at official Source Generators Cookbook
There is example from it:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
namespace GeneratorTests.Tests
{
[TestClass]
public class GeneratorTests
{
[TestMethod]
public void SimpleGeneratorTest()
{
// Create the 'input' compilation that the generator will act on
Compilation inputCompilation = CreateCompilation(#"
namespace MyCode
{
public class Program
{
public static void Main(string[] args)
{
}
}
}
");
// directly create an instance of the generator
// (Note: in the compiler this is loaded from an assembly, and created via reflection at runtime)
CustomGenerator generator = new CustomGenerator();
// Create the driver that will control the generation, passing in our generator
GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
// Run the generation pass
// (Note: the generator driver itself is immutable, and all calls return an updated version of the driver that you should use for subsequent calls)
driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);
// We can now assert things about the resulting compilation:
Debug.Assert(diagnostics.IsEmpty); // there were no diagnostics created by the generators
Debug.Assert(outputCompilation.SyntaxTrees.Count() == 2); // we have two syntax trees, the original 'user' provided one, and the one added by the generator
Debug.Assert(outputCompilation.GetDiagnostics().IsEmpty); // verify the compilation with the added source has no diagnostics
// Or we can look at the results directly:
GeneratorDriverRunResult runResult = driver.GetRunResult();
// The runResult contains the combined results of all generators passed to the driver
Debug.Assert(runResult.GeneratedTrees.Length == 1);
Debug.Assert(runResult.Diagnostics.IsEmpty);
// Or you can access the individual results on a by-generator basis
GeneratorRunResult generatorResult = runResult.Results[0];
Debug.Assert(generatorResult.Generator == generator);
Debug.Assert(generatorResult.Diagnostics.IsEmpty);
Debug.Assert(generatorResult.GeneratedSources.Length == 1);
Debug.Assert(generatorResult.Exception is null);
}
private static Compilation CreateCompilation(string source)
=> CSharpCompilation.Create("compilation",
new[] { CSharpSyntaxTree.ParseText(source) },
new[] { MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location) },
new CSharpCompilationOptions(OutputKind.ConsoleApplication));
}
}
In addition to the Source Generators Cookbook mentioned in the other answer:
The cookbook solution allows you to generate some code and then compare your results to expected, also check for warnings and compilation exceptions etc.
Now, you can additionally EXECUTE the generated code to make sure it's running correctly. For that change the project reference in the test-project like this:
<ProjectReference Include="..\MyGenerator\MyGenerator.csproj"
ReferenceOutputAssembly="true"
OutputItemType="Analyzer" />
And then simply call the generated code from your unit tests, like you would in the consumer project.
I got a CS0426 compiler error when trying to open an Excel using SpreadsheetDocument class from DocumentFormat.OpenXml.Packaging namespace.
I realized that this was because I was using new and, for some reason, the compiler didn't like it.
Why can't I create an instance of the object using new?
//Error CS0426
using (SpreadsheetDocument goldenFile = new SpreadsheetDocument.Open(goldenPath, true));
//Ok code
using (SpreadsheetDocument goldenFile = SpreadsheetDocument.Open(goldenPath, true));
Judging by its name and context, the SpreadsheetDocument.Open method opens a new spreadsheet file for you to read/write from/to.
This should be the correct way to use this API:
using (SpreadsheetDocument goldenFile = SpreadsheetDocument.Open(goldenPath, true)) {
...
}
You need to understand that not every class needs to be created by you writing the word new and directly calling the constructor. Sometimes, in this case for example, the instance of SpreadsheetDocument is probably created somewhere inside the Open method. The Open method simply returns the new instance, so that you can assign it to a variable (goldenFile in this case).
You can write a class that gets created with a static method too:
class Foo {
// properties...
// private constructor
private Foo() { ... }
public static GiveMeAFoo() {
return new Foo();
}
}
I can now create an instance of Foo without directly using new:
var foo = Foo.GiveMeAFoo();
Something similar is happening inside Open.
The compiler gives off the error CS0426 because it thinks like this:
I see that you are using the new operator, so you are creating a new instance of a type. What type is it that you are creating? Let's see... It's SpreadsheetDocument.Open! But wait a minute! That's not a type! I can't find a type called Open in SpreadsheetDocument!
Hence the error:
The type name 'Open' does not exist in the type 'SpreadsheetDocument'.
It's not working because as is, when you're using new you essentially telling your code - "Create an object of a nested class 'Open'".
Either get rid of new or implement public constructor, and then call static Open method.
The Open method is a static method which uses new in its implementation and returns an instance of SpreadsheetDocument. This is why you don't need to use new. Refer to documentation.
In the DotNetYaml sample code I'm looking at, there's a C# construct:
var deserializer = new Deserializer(namingConvention: new CamelCaseNamingConvention());
var order = deserializer.Deserialize<Order>(input);
What is the equivalent F# code? I've tried
let deserializer = new Deserializer(namingConvention=new CamelCaseNamingConvention())
deserializer.Deserialize<Meta>(input)
If you have a C# library that defines optional parameters, then you can use the syntax you are using in your question. To quickly show that's the case, I compiled the following C# code as a library:
using System;
namespace Demo {
public class MyClass {
public static void Foo(int first, string second = "foo", string third = "bar") { }
}
}
You can reference this and use it from F# as follows:
open Demo
MyClass.Foo(1, third="hi")
I tried to do this with YamlDotNet which, I guess, is the library that you were using, but I get an error that the Deserializer class does not have namingConvention as an argument, so my guess would be that you are probably using a different version of the library than you are thinking (or perhaps, my guess of what library you're using was wrong...).
I am using the following code under ASP.NET 4.0 framework to obtain the version of MSI file from a web app:
string strVersion = "";
try
{
Type InstallerType;
WindowsInstaller.Installer installer;
InstallerType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
installer = (WindowsInstaller.Installer)Activator.CreateInstance(InstallerType);
WindowsInstaller.Database db = installer.OpenDatabase(strMSIFilePath, 0);
WindowsInstaller.View dv = db.OpenView("SELECT `Value` FROM `Property` WHERE `Property`='ProductVersion'");
WindowsInstaller.Record record = null;
dv.Execute(record);
record = dv.Fetch();
strVersion = record.get_StringData(1).ToString();
dv.Close();
//db.Commit();
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(dv);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(db);
}
catch
{
//Failed
strVersion = "";
}
It works fine except that when the code finishes running it holds an internal MSI file handle so when I try to move or rename the MSI file I get the error that the file is still in use. This continues until I actually navigate away from the ASPX page that calls the method above.
My question is, I obviously didn't close some handle or object in the code above. But what could that be?
PS. I'm testing it in a development IDE from VS2010.
EDIT: Edited the code like it should be after Adriano's suggestion. Thanks!
The COM object has not been released (it should be auto-released when it goes out of scope but in .NET this doesn't work really well). Because it does not implement the IDisposable interface you can't call its Dispose() method and you can't use it inside an using statement. You have to explicitly call Marshal.FinalReleaseComObject. For example:
try
{
// Your stuffs
}
finally
{
dv.Close();
Marshal.FinalReleaseComObject(dv);
Marshal.FinalReleaseComObject(db);
}
Moreover note that you do not really need a call to the Commit() method because you didn't make any change but just a query.
FWIW, you should be using Windows Installer XML (WiX) Deployment Tools Foundation (DTF). It's an FOSS project from Microsoft that can be found on CodePlex. It has MSI interop libraries with classes that are very similar to the COM classes but implement IDisosable and use P/Invoke instead of COM behind the scenes. There is even support for Linq to MSI if you want. And the full source code is available.
DTF is the gold standard for MSI interop in a .NET world. Here are two examples:
using System;
using System.Linq;
using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Deployment.WindowsInstaller.Linq;
namespace ConsoleApplication3
{
class Program
{
const string DATABASE_PATH = #"C:\FOO..MSI";
const string SQL_SELECT_PRODUCTVERSION = "SELECT `Value` FROM `Property` WHERE `Property`='ProductVersion'";
static void Main(string[] args)
{
using (Database database = new Database(DATABASE_PATH, DatabaseOpenMode.ReadOnly))
{
Console.WriteLine(database.ExecuteScalar(SQL_SELECT_PRODUCTVERSION).ToString());
}
using (QDatabase database = new QDatabase(DATABASE_PATH, DatabaseOpenMode.ReadOnly))
{
var results = from property in database.Properties where property.Property == "ProductVersion" select property.Value;
Console.WriteLine(results.AsEnumerable<string>().First());
}
}
}
}
try to Dispose the Objects.
dv.Dispose();
db.Dispose();
I see this:
using (StreamWriter sw = new StreamWriter("file.txt"))
{
// d0 w0rk s0n
}
Everything I try to find info on is does not explain what this doing, and instead gives me stuff about namespaces.
You want to check out documentation for the using statement (instead of the using directive which is about namespaces).
Basically it means that the block is transformed into a try/finally block, and sw.Dispose() gets called in the finally block (with a suitable nullity check).
You can use a using statement wherever you deal with a type implementing IDisposable - and usually you should use it for any disposable object you take responsibility for.
A few interesting bits about the syntax:
You can acquire multiple resources in one statement:
using (Stream input = File.OpenRead("input.txt"),
output = File.OpenWrite("output.txt"))
{
// Stuff
}
You don't have to assign to a variable:
// For some suitable type returning a lock token etc
using (padlock.Acquire())
{
// Stuff
}
You can nest them without braces; handy for avoiding indentation
using (TextReader reader = File.OpenText("input.txt"))
using (TextWriter writer = File.CreateText("output.txt"))
{
// Stuff
}
The using construct is essentially a syntactic wrapper around automatically calling dispose on the object within the using. For example your above code roughly translates into the following
StreamWriter sw = new StreamWriter("file.text");
try {
// do work
} finally {
if ( sw != null ) {
sw.Dispose();
}
}
Your question is answered by section 8.13 of the specification.
Here you go: http://msdn.microsoft.com/en-us/library/yh598w02.aspx
Basically, it automatically calls the Dispose member of an IDisposable interface at the end of the using scope.
check this Using statement