Get source file location with DeterministicSourcePaths turned on - c#

Question:
Is there a way how to get the Caller or current frame source code location without using the CallerFilePath attribute?
Background:
I have this helper defined:
public class PathHelper
{
public static string GetThisFilePath([CallerFilePath] string path = null)
{
return path;
}
}
That can be called as follows to obtain the location of source code used to build the binary:
var currentSourceFilePath = PathHelper.GetThisFilePath();
This works fine, unless I have DeterministicSourcePaths turned on (typically via ContinuousIntegrationBuild msbuild property). In such a case the returned paths are trimmed to something like:
/_/MyRelativeSourcePath
So it seems that determinist paths are injected into the compiler functionality supporting CallerFilePath yielding this behavior.
I need the source code location in order to be able to unit test product specific functionality (that has to do with inspecting build process), while I'd still like to support fully determinisitc build on CI machines.

You may try something like following. Please note
this is just an idea, exact implementation depends on your environment
This would be working if you are using "default build output paths" only
I didn't tested it
public static string GetThisFilePath([CallerFilePath] string path = null)
{
const string determenisticRoot = "/_/";
if(!path.StartsWith(determenisticRoot))
{
return path;
}
// callerBinPath would be something like $(SolutionRoot)/.../MyProject.Tests/bin/Debug/net5.0
var callerBinPath = Path.GetDirectoryName(Assembly.GetCallingAssembly().Location);
// Traverse to $(TestProjectRoot) - testProjectRoot would be something like $(SolutionRoot)/.../MyProject.Tests
var testProjectRoot = Path.Combine(callerExecutablePath, "../../..");
// Combine projectRoot root with relative path from [CallerFilePath]
return Path.Combine(testProjectRoot, path.Substring(determenisticRoot.Length));
}

Related

Testing validations C#

Below I've pasted my code. I'm validating a measure. I've written code that will read a Linux file. But if I wanted to pass multiple file names here would this be possible? so for example instead of my test just validating one file could I do a loop so it could ready multiple files in one go.
Once the file is being read and proceeded I return actualItemData. In my next method, I want to make a call to this actualItemData so the data is published in my var actual
public string validateMeasurement
{
var processFilePath = **"/orabin/app/oracle/inputs/ff/ff/actuals/xx_ss_x.csv.ovr";**
var actualItemData = Common.LinuxCommandExecutor.
RunLinuxcommand("cat " + processFilePath);
**return actualItemData;**
}
public void validateInventoryMeasurementValue(string Data, string itemStatus)
{
var expected = '6677,6677_6677,3001,6';
**var actual = actualItemData);**
Assert.AreEqual(expected, actual);
}
It looks like you are using msunit. As far as I know it doesn't support test cases. If you were to use nunit you would be able to do this using the TestCase attribute.
[TestCase("myfile1.txt", "6677,6677_6677,3001,6")]
[TestCase("myfile2.txt", "1,2,3")]
public void mytest(string path, string expected)
{
var actual = Common.LinuxCommandExecutor.
RunLinuxcommand("cat " + path);
Assert.AreEqual(expected, actual);
}
Generally you don't want to write unit tests that cross code boundaries (read files, hit the database, etc) as these tests tend to be brittle and difficult to maintain. I am not sure of the aim of your code but it appears you may be trying to parse the data to check it's validity. If this is the case you could write a series of tests to ensure that when your production code (parser) is given a string input, you get an output that matches your expectation. e.g.
[Test()]
public void Parse_GivenValidDataFromXX_S_X_CSV_ShouldReturnTrue(string filename)
{
// Arrange
var parser = CreateParser(); // factory function that returns your parser
// Act
var result = parser.Parse("6677,6677_6677,3001,6");
// Arrage
Assert.IsTrue(result);
}

Replace method's Body with Body of another method using Mono.Cecil?

With Mono.Cecil it looks quite simple when we can just set the Body of the target MethodDefinition to the Body of the source MethodDefinition. For simple methods, that works OK. But for some methods whereas a custom type is used (such as to init a new object), it won't work (with an exception thrown at the time writing the assembly back).
Here is my code:
//in current app
public class Form1 {
public string Test(){
return "Modified Test";
}
}
//in another assembly
public class Target {
public string Test(){
return "Test";
}
}
//the copying code, this works for the above pair of methods
//the context here is of course in the current app
var targetAsm = AssemblyDefinition.ReadAssembly("target_path");
var mr1 = targetAsm.MainModule.Import(typeof(Form1).GetMethod("Test"));
var targetType = targetAsm.MainModule.Types.FirstOrDefault(e => e.Name == "Target");
var m2 = targetType.Methods.FirstOrDefault(e => e.Name == "Test");
var m1 = mr1.Resolve();
var m1IL = m1.Body.GetILProcessor();
foreach(var i in m1.Body.Instructions.ToList()){
var ci = i;
if(i.Operand is MethodReference){
var mref = i.Operand as MethodReference;
ci = m1IL.Create(i.OpCode, targetType.Module.Import(mref));
}
else if(i.Operand is TypeReference){
var tref = i.Operand as TypeReference;
ci = m1IL.Create(i.OpCode, targetType.Module.Import(tref));
}
if(ci != i){
m1IL.Replace(i, ci);
}
}
//here the source Body should have its Instructions set imported fine
//so we just need to set its Body to the target's Body
m2.Body = m1.Body;
//finally write to another output assembly
targetAsm.Write("modified_target_path");
The code above was not referenced from anywhere, I just tried it myself and found out it works for simple cases (such as for the 2 methods Test I posted above). But if the source method (defined in the current app) contains some Type reference (such as some constructor init ...), like this:
public class Form1 {
public string Test(){
var u = new Uri("SomeUri");
return u.AbsolutePath;
}
}
Then it will fail at the time writing the assembly back. The exception thrown is ArgumentException with the following message:
"Member 'System.Uri' is declared in another module and needs to be imported"
In fact I've encountered a similar message before but it's for method calls like (string.Concat). And that's why I've tried importing the MethodReference (you can see the if inside the foreach loop in the code I posted). And really that worked for that case.
But this case is different, I don't know how to import the used/referenced types (in this case it is System.Uri) correctly. As I know the result of Import should be used, for MethodReference you can see that the result is used to replace the Operand for each Instruction. But for Type reference in this case I totally have no idea on how.
All my code posted in my question is fine BUT not enough. Actually the exception message:
"Member 'System.Uri' is declared in another module and needs to be imported"
complains about the VariableDefinition's VariableType. I just import the instructions but not the Variables (which are just referenced exactly from the source MethodBody). So the solution is we need to import the variables in the same way as well (and maybe import the ExceptionHandlers as well because an ExceptionHandler has CatchType which should be imported).
Here is just the similar code to import VariableDefinition:
var vars = m1.Body.Variables.ToList();
m1.Body.Variables.Clear();
foreach(var v in vars){
var nv = new VariableDefinition(v.Name, targetType.Module.Import(v.VariableType));
m1.Body.Variables.Add(nv);
}

How to read data from multiple (multi language) resource files?

I am trying the multi language features in an application. I have created the resource files GlobalTexts.en-EN.resx GlobalTexts.fr-FR.resx and a class that sets the culture and returns the texts like (I will not go in all the details, just show the structure):
public class Multilanguage
{
...
_res_man_global = new ResourceManager("GlobalResources.Resources.GlobalTexts", System.Reflection.Assembly.GetExecutingAssembly());
...
public virtual string GetText(string _key)
{
return = _res_man_global.GetString(_key, _culture);
}
}
...
Multilanguage _translations = new Multilanguage();
...
someText = _translations.GetText(_someKey);
...
This works just fine.
Now, I would like to use this application in another solution that basically extends it (more windows etc.) which also has resource files ExtendedTexts.en-En.resx ExtendedTexts.fr-FR.resx and a new class like:
public class ExtendedMultilanguage : Multilanguage
{
...
_res_man_local = new ResourceManager("ExtendedResources.Resources.ExtendedTexts", System.Reflection.Assembly.GetExecutingAssembly());
...
public override string GetText(string _key)
{
string _result;
try
{
_result = _res_man_local.GetString(_key, _culture);
}
catch (Exception ex)
{
_result = base.GetText(_key);
}
}
...
ExtendedMultilanguage _translations = new Multilanguage();
...
someText = _translations.GetText(_someKey);
...
the idea being that if the key is not found in ExtendedTexts the method will call the base class which is looking into GlobalTexts. I did this in order to use the call GetText(wantedKey) everywhere in the code without having to care about the location of the resource (I do not want to include the translations from the extensions in the GlobalTexts files); it is juts the used class that is different from project to project.
The problem I am facing is that the try/catch is very slow when exceptions raise- I wait seconds for one window to populate. I tested with direct call and works much faster, but then I need to care all the time where the resource is located...
The question is: is there an alternative way of doing this (having resources spread in various files and have only one method that gives the desired resource without throwing an error)?
In the end I took a workaround solution and loaded all the content of the resource files in dictionaries. This way I can use ContainsKey and see if the key exists or not.

Is it possible to execute C# code represented as string?

On my form I have a button click
private void button1_Click(object sender, EventArgs e)
{
do something
}
How on the click would I load my do something from a text file, for example my text file looks like this:
MessageBox.Show("hello");
label1.Text = "Hello";
on click it does everything in my text file, if possible.
Here is a very simple example, just to prove this is possible. Basically, you use CodeDomProvider to compile source at runtime, then execute using reflection.
var provider = CodeDomProvider.CreateProvider("C#");
string src=#"
namespace x
{
using System;
public class y
{
public void z()
{
Console.WriteLine(""hello world"");
}
}
}
";
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
type.GetMethod("z").Invoke(instance, null);
}
Edit
As #Agat points out, the OP seems to require a sort of scripting framework (it makes use of label1, a property of the current object), whereas my answer above obviously does not provide that. The best I can think of is a limited solution, which would be to require dependencies to be specified explicitly as parameters in the "script". Eg, write the scripted code like this:
string src = #"
namespace x
{
using System.Windows;
public class y
{
public void z(Label label1)
{
MessageBox.Show(""hello"");
label1.Text = ""Hello"";
}
}
}
";
Now you can have the caller examine the parameters, and pass them in from the current context, again using reflection:
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
var method = type.GetMethod("z");
var args = new List<object>();
// assume any parameters are properties/fields of the current object
foreach (var p in method.GetParameters())
{
var prop = this.GetType().GetProperty(p.Name);
var field = this.GetType().GetField(p.Name);
if (prop != null)
args.Add(prop.GetValue(this, null));
else if (field != null);
args.Add(field.GetValue(this));
else
throw new InvalidOperationException("Parameter " + p.Name + " is not found");
}
method.Invoke(instance, args.ToArray());
}
Like the other answers have stated, it isn't an easy thing to implement and can possibly be done through reflection depending on how advanced your scripts are.
But no one #BrankoDimitrijevic mentioned Roslyn and it is a great tool. http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx
It hasn't been updated in quite awhile (Sept.2012) and doesn't have all of the features of C# implemented, however, it did have a lot of it implemented when I played around with this release.
By adding your assembly as a reference to the scripting session, you're able to gain access to all of your assembly's types and script against them. It also supports return values so you can return any data that a scripted method generates.
You can find what isn't implemented here.
Below is a quick and dirty example of Roslyn that I just wrote and tested. Should work right out of box after installing Roslyn from NuGet. The small bloat at the initialization of the script engine can easily be wrapped up in a helper class or method.
The key is passing in a HostObject. It can be anything. Once you do, your script will have full access to the properties. Notice that you just call the properties and not the host object in the script.
Basically, your host object will contain properties of the data you need for your script. Don't necessarily think of your host object as just a single data object, but rather a configuration.
public class MyHostObject
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
public class RoslynTest
{
public void Test()
{
var myHostObject = new MyHostObject
{
Value1 = "Testing Value 1",
Value2 = "This is Value 2"
};
var engine = new ScriptEngine();
var session = engine.CreateSession(myHostObject);
session.AddReference(myHostObject.GetType().Assembly.Location);
session.AddReference("System");
session.AddReference("System.Core");
session.ImportNamespace("System");
// "Execute" our method so we can call it.
session.Execute("public string UpdateHostObject() { Value1 = \"V1\"; Value2 = \"V2\"; return Value1 + Value2;}");
var s = session.Execute<string>("UpdateHostObject()");
//s will return "V1V2" and your instance of myHostObject was also changed.
}
}
No. You can not.
At least in any simple way.
The thing you want is something like eval('do something') from javascript.
That's not possible to do with C#. C# is a language which needs compilation before execution unlike javascript (for instance).
The only way to implement that is to build your own (pretty complicated as for beginner) parser and execute it in such way.
UPDATED:
Actually, as JDB fairly noticed, that's really not the only way. I love programming! There are so many ways to make a freakky (or even sometimes that really can be necessary for some custom interesting tasks (or even learning)!) code. he he
Another approach I've got in my mind is building some .cs file, then compiling it on-the-fly and working with it as some assembly or some other module. Right.

How to check if file exist in ASP.NET MVC 4

I am working on an ASP.NET MVC 4 application. I allow users to upload files but I want to save them with different name on the server so I created helper method which should return GUID to be used. Even though it probably will never happen still I want to check if I have a file with the same GUID name so I have this as code :
public static string GetUniqueName(string pathToFile)
{
bool IsUnique = false;
string guid = null;
while (!IsUnique)
{
guid = Guid.NewGuid().ToString("N");
var path = System.IO.Path.Combine(pathToFile, "login.jpg");
if (!System.IO.File.Exists(path))
{
IsUnique = true;
}
}
return guid;
}
as you can see the name of the file is hard coded just for testing purposes, because I know there is such file.
To save the file I use this:
var path = System.IO.Path.Combine(Server.MapPath("~/Content/NewsImages"), fileName);
and it's working properly. So when I tried to call my static method I pass the arbument like this:
string test = Helper.GetUniqueName("~/Content/NewsImages");
but then in debug I saw that
System.IO.Path.Combine(pathToFile, "login.jpg");
returns ~/Content/NewsImages\\login.jpg so I decided to change the argument that I pass to:
string test = Helper.GetUniqueName("~\\Content\\NewsImages");
which now results in ~\\Content\\NewsImages\\login.jpg which seems fine but then in:
if (!System.IO.File.Exists(path))
{
IsUnique = true;
}
I pass the check, even though I know that such a file exist in the directory that I want to check. How can I fix this?
When calling the helper method you should use Server.MapPath, this will convert from a virtual path to a physical path e.g.
string test = Helper.GetUniqueName(Server.MapPath("~/Content/NewsImages"));

Categories

Resources