How to wrap all calls to an assembly with PostSharp? - postsharp

My C# project refers to an external .NET assembly. I would like to insert locking statements around every call from my project to that assembly. I've been trying to establish this with PostSharp but can't find a way to do it. I have the source to the external assembly and I probably could achieve my goal the easiest by inserting the aspect there, but I prefer a non-intrusive solution where I can leave the external assembly untouched.
Approach 1
I have found out that I can wrap calls to the external assembly. Sadly, PostSharp is unable to wrap calls to abstract methods, and interface members are abstract methods. Therefore this approach doesn't cover calls through interface types.
[assembly: WrappingAspect(
AttributeTargetAssemblies = "Library",
AttributeTargetExternalMemberAttributes = MulticastAttributes.NonAbstract)]
[Serializable]
internal class WrappingAspect : OnMethodBoundaryAspect {
public override void OnEntry(MethodExecutionArgs args) {
Monitor.Enter(SyncRoot);
}
public override void OnExit(MethodExecutionArgs args) {
Monitor.Exit(SyncRoot);
}
}
Approach 2
Perhaps I could wrap all the methods in my project that refer to types in the external assembly. I'm thinking along the lines below. However, I cannot try this out because ReflectionSearch requires a PostSharp license that I don't currently have.
[assembly: WrappingAspect]
[Serializable]
internal class WrappingAspect : OnMethodBoundaryAspect {
public override void OnEntry(MethodExecutionArgs args) {
Monitor.Enter(SyncRoot);
}
public override void OnExit(MethodExecutionArgs args) {
Monitor.Exit(SyncRoot);
}
public override bool CompileTimeValidate(MethodBase method) {
return ReflectionSearch.GetDeclarationsUsedByMethod(method)
.Any(r => r.UsedType.Assembly.FullName.StartsWith("Library"));
}
}
Questions
Is there a non-intrusive way to wrap all calls to an external assembly, including calls to methods through an interface type?
Would my second approach work; to detect which methods refer to the external assembly and wrap them?
Are there other approaches to this problem?

Have you tried adding them via the XML approach? Straight from the PostSharp (slightly outdated) docs
Adding aspects through XML gives the advantage of applying aspects without modifying the source code, which could be an advantage in some legacy projects.

Answering my own question number 1; Yes, there is. Here's how I did it, using PostSharp configuration files as #Mikee suggested. I used PostSharp 3.1.39.
In short, you can run PostSharp to weave code into a DLL without changing the source code of that DLL. The command might look like this (split into multiple lines for readability)
postsharp.4.0-x64.exe temp\mylib.dll /P:Output=mylib.dll /NoLogo
/X:myconfig.psproj
"/P:ReferenceDirectory=$(ProjectDir) "
"/P:SearchPath=$(OutDir) "
"/P:Configuration=$(Configuration)"
"/P:Platform=$(Platform)"
"/P:MSBuildProjectFullPath=$(ProjectPath) "
/P:TargetFrameworkIdentifier=.NETFramework
The $(variables) in this command come straight out of Visual Studio, e.g. if you run this in your post-build event. Beware trailing backslashes in Visual Studio variables; adding an extra space before the closing quote is a necessary precaution.
mylib.dll is the target assembly where the weaving will be done. The input and output DLL must be two different files, hence the input is in a temp folder.
The configuration file myconfig.psproj looks like this in my case:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.postsharp.org/1.0/configuration">
<Multicast xmlns:my="clr-namespace:MyApp.Aspects;assembly:MyApp">
<my:MyAspect AttributeTargetMemberAttributes="Public"/>
</Multicast>
</Project>
This configuration will apply the aspect MyApp.Aspects.MyAspect from the MyApp assembly into all public members in the target assembly. More configuration attributes can be found from the documentation of MulticastAttribute
To run PostSharp in a more complex scenario, more configuration parameters may be required. Running postsharp.4.0-x64.exe /? gives you a somewhat unhelpful list of command line parameters. To find out what kind of parameters PostSharp really uses when it's run as part of a Visual Studio project, you can do this:
Add PostSharp to your C# project (as a NuGet).
Add some dummy aspect to a method.
Run the build with verbose output (in Visual Studio 2012: Tools -> Options -> Projects and Solutions -> Build and Run -> MSBuild project build output verbosity -> Diagnostic).
After the build, search the build output window for a line containing Task "PostSharp30" and then browse down to find a line starting with "Connected to the pipe after XXX ms. Requesting with XX arguments".
What follows on the line is a list of parameters to postsharp.4.0-x64.exe. Note that the parameters are separated by semicolons; remove the semicolons and quote the parameters to preserve meaningful spaces.
The list of parameters I got for my test project was much longer than the final command above. Many of the parameters weren't necessary.
Caveat: The free Express version of PostSharp doesn't support weaving iterator methods (ones that use return yield). You'll get warnings of them.

Related

Can telosys generation invoke external program prior to writing to target location and based on the return value continue or cancel writing to target

I want to generate c# asp.net core razor pages with many projects: Application, Domain, Infrastructure, Core etc.
I would like to know if Telosys is extensible to allow a custom console program to be called with arguments: , so this program can do some code merges in order to preserve possible additions/changes to the code a developer did since the previous code generation?
C# can have some directives called #region to partition the code and these will be "regenerate only regions" and outside these regions the developer can put his own code. We need to preserver his code.
C# Partial classes and methods can also help.
I want to have capability to merge newly re-generated code (e.g. entity class - I can add some properties or change some property name, type, annotations, tags etc.) into previously generated entity class. This could be done by creating #region .... #endregion where the code can be regenerated into and all the code that is outside these regions should be preserved. See: efg.loresoft.com/en/latest/regeneration So I would like to know if there would be a way to temporarily generate files in a TMPGEN folder and allow some "merge program to run" that will mege new code with previously generated code (with some code added by the developer) previously determining if merge is needed (compare hashes)
This is the code from EntityFrameworkCore.Generator tool that merges existing regions https://github.com/loresoft/EntityFrameworkCore.Generator..EntityFrameworkCore.Generator.Core/Templates/CodeTemplateBase.cs has the protected virtual method:
void MergeOutput(string fullPath, string outputContent) show that fullPath is an existing file and outputContent is the result of parsing the template (new content). RegionParser uses these namespaces: Microsoft.CodeAnalysis.CSharp; and Microsoft.CodeAnalysis.CSharp.Syntax;
so I need to user c# console application to user this preserver region code.
Thanks,
Rad
Yes, Telosys is extensible by using specific Java classes loaded in the template context with $loader
See : Is it possible in a Telosys template to call a function created specifically?
So, you just have to create a Java class to launch any Shell command
For example, see : How to get java getRuntime().exec() to run a command-line program with arguments?

Roslyn Diagnostic Analyzer RegisterSymbolAction not being called

I created a C# diagnostic project for VB, and am having trouble with it.
public override void Initialize(AnalysisContext context)
{
context.RegisterSymbolAction(CheckExpression,
SyntaxKind.InvocationExpression);
}
But the breakpoint on the first line of my CheckExpression method never gets hit.
There were two problems. The first problem was that nuget manager didn’t want to install the Visual Basic portion of the code analysis packages into the c# project, the second was at least partially caused by the first — the syntaxkind namespace is split between VisualBasic and CSharp, and all of course I was referencing the C# namespace. Once I managed to get the nuget packages installed (did this from the command line instead of the UI), I was able to reference the right namespaces for the vb syntax nodes and the anaylyzer portion is now working.
Hope this helps someone.

What happended to nunit extensions/rowtest?

In NUnit 2.4.7, nunit.framework.extensions.dll was included which made it possible to do RowTests.
When downloading the newest version (2.5.8) I can't find it. What happened to it?
Instead of using RowTest, you can use TestCase. A previous testing using RowTest would look like:
[RowTest]
[Row("foo", false)]
[Row("", true)]
public void Some_test(string value, bool expected)
{
// test
}
And the same thing with TestCase looks like this:
[TestCase("foo", false)]
[TestCase("", true)]
public void Some_test(string value, bool expected)
{
// test
}
RowTest was an extension that was merged in temporarily, and was removed in 2.5 Alpha 2
Quote from the Release Notes for 2.4.8:
NUnit now includes the RowTest
extension, written by Andreas
Schlapsi, in it's extension
assemblies. This extension allows you
to write test methods that take
arguments and to provide multiple sets
of argument values using the
RowAttribute. To use RowTest, your
test must reference the
nunit.framework.extensions assembly.
Note: Merging extensions into NUnit's
own extension assembly is an
experiment we are trying for this
release. The approach may change in
future releases.future releases.
Quote from the 2.5 alpha 2 Release Notes:
The RowTestExtension, which was merged
into the nunit extension dlls in
Alpha-1, is now provided as a separate
addin. This is the general approach we
plan to take with regard to any
bundled addins, since it permits the
creator of an addin to provide updates
separately from the NUnit release.
You can now download the RowTest extension from here.

Can CodeDom add Source Code Files to a Project?

I have been using CodeDom to do some code generation. It works great, but I haven't found a way to include the generated source code files in a project. I started using T4 and the T4Toolbox to generate code because it supports integration with project files.
Does anyone know if CodeDom supports this functionality too? I'd consider taking a second look at CodeDom if it only supported this one feature.
Here is an example of how I make a source code file with CodeDom:
protected void CreateSourceFile(CodeCompileUnit codeCompileUnit,
string fileName,
out string fileNameWithExtension)
{
fileNameWithExtension = string.Format("{0}.{1}",
fileName,
CodeProvider.FileExtension);
var indentedTextWriter =
new IndentedTextWriter(new StreamWriter(fileNameWithExtension,
false),
TabString);
CodeProvider.GenerateCodeFromCompileUnit(codeCompileUnit,
indentedTextWriter,
new CodeGeneratorOptions());
indentedTextWriter.Close();
}
That works fine but it just outputs the file to the hard drive somewhere (probably bin folder).
Here is a second example of some code I use with T4, this one specifies the output as part of the project the template is transformed in:
public class RDFSClassGenerator : Generator
{
private readonly string rootNamespace;
private readonly string ontologyLocation;
public RDFSClassGenerator(
string rootNamespace,
string ontologyLocation)
{
this.rootNamespace = rootNamespace;
this.ontologyLocation = ontologyLocation;
}
protected override void RunCore()
{
XElement ontology = XElement.Load(ontologyLocation);
var service = new RDFSGeneratorService(ontology);
foreach (MetaClass metaClass in service.MetaClasses)
{
var rdfsClassTemplate = new RDFSClassTemplate(rootNamespace, metaClass);
rdfsClassTemplate.Output.File = "Domain/" + metaClass.Name + ".cs";
rdfsClassTemplate.Render();
}
}
}
So the T4 code will output the file into the "Domain" folder of my project. But the CodeGen stuff just outputs the file on disk and doesn't update the project file.
Here is a visual:
Yes, it can. Here is how: http://www.olegsych.com/2009/09/t4-and-codedom-better-together/
Short answer is no, but I could be wrong (ever try to prove a negative?)
Your question was a little confusing as CodeDom isn't exactly equitable with T4. T4 templates are a convenient way of generating code files the same way, for example, asp.net generates HTML files, mixing text and code that gets executed to generate a file that is then interpreted by something else (such as a compiler or a browser). CodeDom is usually used to generate assemblies at runtime rather than files, although you can do it (as you have discovered).
While T4 makes it easy to add files to the solution, you can do this with CodeDom as well. I don't believe it supports interaction with the solution directly, but you can manage this using EnvDTE, or the automation model for Visual Studio.
The problem with this is that the automation model isn't easy to work with. EnvDTE is a wrapper around COM classes, which is always fun to code against. Also, you have to be careful when attempting to get the object. The naive implementation will get the object from the first instance of Visual Studio loaded. You have to poll the Running Object Table to find the current instance. Once you have it, you must deal with searching through the dte for the location you're looking for, deal with source control, locked files, etc etc.
Working with it, you start to learn why T4 was created in the first place.
The question you have to ask yourself is, "Does CodeDom give me enough that T4 doesn't to make up for all its shortcomings?"

CA: Suppress results from generated code not working in VS2010 beta 2

I'm trying to run codeanalysis on an assembly that contains an entity model (edmx file). In project properties I have checked the "Suppress results from generated code" option, but I am still getting a lot of CA errors pertaining to the auto-generated EF code.
Has anyone experienced this? And is there a work-around?
Just put the attribute on your class definition.
But how to do it, since your file can get overridden any time. Use a separate file, since all generated classes are partial classes. Open a separate file, and write something like:
[GeneratedCode("EntityModelCodeGenerator", "4.0.0.0")]
public partial class YourEntitiesContextName : ObjectContext
{
}
This will skip code analysis on your particular generated class. StyleCop for instance is more smart and doesn't touch files that have .designer/.generated part in their name or regions that have generated word in their name.
Well, "Suppress results from generated code" really means "Don't look at types with GeneratedCodeAttribute". EF's code generator hasn't added this, historically (though I've suggested it to the team). But you can add it if you use custom T4.