I am writing some unit test cases using fakes framework. I am using an object ShimFileCreationInformation from Microsoft.SharePoint.Client.Fakes namespace. Now, I pass this object to a function. Inside the function, I am trying to assign a value to the Url property.
fileCreationInformation.Url = value;
But even though the value is present, nothing gets assigned to Url properly and it remains null. Is there any workaround for this problem? To make things worse, there is not documentation available on ShimFileCreationInformation object.
Code sample:
ShimFileCreationInformation fileCreationInformation = new ShimFileCreationInformation();
SomeFunction(fileCreationInformation);
SomeFunction :
public void SomeFunction(FileCreationInformation fileCreationInformation)
{
fileCreationInformation.Url = value; // This statement had so effect on fileCreationInformation.Url
}
fileCreationInformation.Url = value;
Setting the value directly as above will not work since you are setting the value of the Shim and not the actual object. You need to use ShimFileCreationInformation.AllInstances.UrlGet so thay whenever the Url Get is called it will return the value you specify.
Your code should look something like below:
[TestMethod]
public void derived_test()
{
using (ShimsContext.Create())
{
ShimFileCreationInformation fileCreationInformation = new ShimFileCreationInformation();
ShimFileCreationInformation.AllInstances.UrlGet = (instance) => value;
SomeFunction(fileCreationInformation);
}
}
public void SomeFunction(FileCreationInformation fileCreationInformation)
{
var url = fileCreationInformation.Url;
// Check url variable above. It should be set to value
fileCreationInformation.Url = value; // This statement will not work since you are trying to set the value of the Shim and you need to use `ShimFileCreationInformation.AllInstances.UrlGet` to set property value for Shims
}
Related
I'm using log4net with C# to logging my app.
I know I can do it like this:
GlobalContext.Properties["PropertyName"] = "NewValue";
XmlConfigurator.Configure();
And it works.
But it's not thaaaat dynamic, as I have to call Configure again to set a new value.
Is there a way to set a property value before call ILog.Info?
Something like that:
//here I set a new value for %property{PropertyName}
log.Info("Value to log");
//here I set a another one for %property{PropertyName}
log.Info("Value to log 2");
You can use %property{PropertyName} in the conversionPattern of your PatternLayout, and you will get a new value logged each time you change the property value.
If you use a property for appender configuration properties, such as a filename or directory for a FileAppender, you will of course need to reconfigure after changing the property value.
Log4Net supports various contexts. GlobalContext, as you've found, is one of them. ThreadContext is another, and I think is more appropriate in your scenario:
log4net.ThreadContext["PropertyName"] = "NewValue";
There's no need to call Configure. Properties set in the ThreadContext are available for any calls to the logger from the current thread. Reference the property within the appender config in the normal way:
%property{PropertyName}
Based on #joe comment I've wrote my own appender as follow:
public class MyCustomAppender : RollingFileAppender
{
bool firstRun = true;
string fileNamePattern = null;
protected override void Append(LoggingEvent loggingEvent)
{
CloseFile();
File = fileNamePattern.Replace("__filename__", ThreadContext.Properties["PropertyName"].ToString());
LockingModel.OpenFile(File, true, Encoding.UTF8);
LockingModel.AcquireLock();
OpenFile(File, true);
base.Append(loggingEvent);
DoAppend(loggingEvent);
}
public override string File
{
get
{
if (firstRun)
{
firstRun = false;
fileNamePattern = base.File;
}
return base.File;
}
set
{
base.File = value;
}
}
}
And it works, which doesn't mean is correct. I don't know if it's a good thing inside the overrode Append method close, acquire lock and open file everytime I log something.
Any thoughts?
I've been trying to create a new base class for a Windows Forms form. I want to have this base class go through all the tableadapters it has on it and update their connection strings without anyone adding any code to the form. They just put the tableadapters on the form and don't worry about the connection string settings as it's all handled in the base class.
The problem is my reflection code can find the property fine, but it can't set it. How can I fix it?
Below is the code:
public class cFormWS : Form
{
public string ConnectionStringToUse { get; set; }
public cFormWS()
{
Load += cFormWS_Load;
}
void cFormWS_Load(object sender, EventArgs e)
{
InitiliseTableAdapters();
}
private void InitiliseTableAdapters()
{
var ListOfComponents = EnumerateComponents();
foreach (var ItemComp in ListOfComponents)
{
if (ItemComp.ToString().ToLower().EndsWith("tableadapter"))
{
var ItemCompProps = ItemComp.GetType().GetRuntimeProperties();
var TASQLConnection =
ItemCompProps.FirstOrDefault(
w => w.PropertyType == typeof(System.Data.SqlClient.SqlConnection));
if (TASQLConnection != null)
{
var property = typeof(System.Data.SqlClient.SqlConnection).GetProperty("ConnectionString");
// How do I set the value?
string value = "some new connection string";
var ConvertedProperty = Convert.ChangeType(value, property.PropertyType);
// I tried seting a value. It is not working:
// "object does not match target type"
property.SetValue(TASQLConnection, ConvertedProperty, null);
//// I tried using a method. It is not working:
//// "object does not match target type"
//var m = property.SetMethod;
//ParameterInfo[] parameters = m.GetParameters();
//m.Invoke(m, parameters); // m.Invoke(this, parameters); // m.Invoke(ItemComp, parameters);
}
}
}
}
private IEnumerable<Component> EnumerateComponents()
{
return from field in GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
where typeof(Component).IsAssignableFrom(field.FieldType)
let component = (Component)field.GetValue(this)
where component != null
select component;
}
When you do SetValue, you need to pass in the object that you wish to set the property on.
In your first example code, you passed in ItemComp: This is incorrect, since the ConnectionString is a property of the SqlConnection which is a property of ItemComp
In your edited question (and my original answer) you pass in the TASqlConnection. However, this is not the object, but a PropertyInfobased of the object
The correct way is to get the value from the ItemComp object and pass that in:
property.SetValue(TASQLConnection.GetValue(ItemComp), ConvertedProperty, null);
ORIGINAL (INCORRECT) ANSWER:
You're trying to set a ConnectionString property of ItemComp. The ConnectionString is not a property of the TableAdapter, but of the SqlConnection (which is a property of the TableAdapter).
The correct way of setting the property would be this:
property.SetValue(TASQLConnection, ConvertedProperty, null);
I am working on writing a unit test for the following function..
public virtual FacadeClass InitNewAMSObject()
{
FacadeClass output = null;
if (ClassMapInfo != null)
{
// Override the metadata caching option
// sets property in external dll
Avectra.netForum.Common.Config.CacheMetaData = false;
if (!String.IsNullOrEmpty(ClassMapInfo.AMSClassName))
{
output = DataUtils.InstantiateFacadeObject(ClassMapInfo.AMSClassName);
}
}
else
{
throw new System.ApplicationException("Need to add the attribute");
}
return output;
}
I cannot get past the line with the comment "sets property in external dll". My shim never seems to 'make it over' to the function being tested. It always throws an error, and its actually trying to use the dll instead of my shim. It may look like i'm setting a property to false only, but the dll uses the property like a method in it's Setter. The .Config is always in an errored state because its constructors (in the dll) are attempting to set up database connections. I expect it to be in at least a null state, and also have the .Fakes. in the class name. I just want to skip over it because I do not write this dll, its 3rd party.
I've researched for a day now and cannot find an example anywhere of how to shim a property that is set on a referenced dll.
Here is my current test for what its worth
[TestMethod]
public void InitNewAMSObjectTEST()
{
using (ShimsContext.Create())
{
Address amsc = new Address();
bool test = true;
ShimConfig.CacheMetaDataGet = () => test;
ShimConfig.CacheMetaDataSetBoolean = value => test = value;
amsc.InitNewAMSObject();
}
}
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.
I'm creating a custom workflow activity in VS2010 targeting .NET 3.5. The DLL is actually being used in a Microsoft System Center Service Manager custom workflow, but I don't think that is my issue.
I have a public string property, that the user types in the string of what the activity should use. However, when the WF runs, it errors out 'value cannot be null'. I want to target if it is my code or something else.
When we drag my custom activity onto the designer, I'm able to type in the text of the string on the designer for that property.
public static DependencyProperty ChangeRequestStageProperty = DependencyProperty.Register("ChangeRequestStage", typeof(String), typeof(UpdateChangeRequestStage));
[DescriptionAttribute("The value to set the ChangeRequestStage Property in the ChangeRequest Extension class.")]
[CategoryAttribute("Change Request Extension")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public String Stage
{
get { return ((String)(base.GetValue(UpdateChangeRequestStage.ChangeRequestStageProperty))); }
set { base.SetValue(UpdateChangeRequestStage.ChangeRequestStageProperty, value); }
}
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
EnterpriseManagementGroup emg = CreateEMG();
//System.WorkItem.ChangeRequest Extension - ClassExtension_928bec0a_cac4_4a0a_bd89_7146c9052fbe
ManagementPackClass mpcChangeRequest = emg.EntityTypes.GetClass(new Guid("8c6c6057-56ad-3862-47ec-dc0dde80a071"));
//System.WorkItemContainsActivity Relationship Class
ManagementPackRelationship workItemContainsActivityRelationship = emg.EntityTypes.GetRelationshipClass(new Guid("2DA498BE-0485-B2B2-D520-6EBD1698E61B"));
EnterpriseManagementObject changeRequest = null;
//Loop thru each emo (Change Request in this case), and assign it. There will never be more than 1 emo returned
foreach (EnterpriseManagementObject obj in emg.EntityObjects.GetRelatedObjects<EnterpriseManagementObject>(executionContext.ContextGuid, workItemContainsActivityRelationship, TraversalDepth.OneLevel, ObjectQueryOptions.Default))
{ changeRequest = obj; }
EnterpriseManagementObjectProjection emop = new EnterpriseManagementObjectProjection(changeRequest);
if (emop != null)
{ emop.Object[mpcChangeRequest, "ChangeRequestStage"].Value = Stage; }
emop.Commit();
return base.Execute(executionContext);
}
Since it is getting a 'value cannot be null' error, I'm guessing it's on this line:
emop.Object[mpcChangeRequest, "ChangeRequestStage"].Value = Stage;
I'm going to test and see if hardcoding a value works or not. Any ideas?
enter code here
try this
if (emop != null && emop.Object[mpcChangeRequest, "ChangeRequestStage"] != null)
emop.Object[mpcChangeRequest, "ChangeRequestStage"].Value = Stage
I didn't want to leave this question wide open, so I'm updating it as to how I resolved this (a long time ago).
Rather than working with an EnterpriseManagementObjectProjection (emop), I worked with a standard EnterpriseManagementObject (emo). From there, I was able to follow a similar format from above:
ManagementPackClass mpcChangeRequest = emg.EntityTypes.GetClass(new Guid("8c246fc5-4e5e-0605-dc23-91f7a362615b"));
changeRequest[mpcChangeRequest, "ChangeRequestStage"].Value = this.Stage;
changeRequest.Commit();