Trying to analyze this code with Roslyn.
public class MyClass
{
#region FirstRegion
public void MyMethod()
{
}
#endregion
#region SecondRegion
public void MyMethod1()
{
}
private static void MyMethod3()
{
}
#endregion
}
At first I want to break it on regions and then to look what function is declared in each region.
var syntaxRoot = tree.GetRoot();
var regions = syntaxRoot.GetRegions();
foreach (var item in regions)
{
var publicFunctions = syntaxRoot.GetFunctions(item.FullSpan, Modificators.Public);
}
I thought that it can be done by specifying TextSpan
Region region = new Region();
region.Identifier = rawRegions[i].GetText().ToString();
region.FullSpan = new TextSpan(rawRegions[i].FullSpan.Start, rawRegions[i + 1].FullSpan.End - rawRegions[i].FullSpan.Start);
regions.Add(region);
But at the end i get that first regions contains two public methods, second region only one public method. It seems that in my case TextSpan is wrong, but why? How to fix this?
Program.cs
public class Modificators
{
public const string Public = "public";
public const string Private = "private";
public const string Protected = "protected";
}
public class Region
{
public string Identifier { get; set; }
public TextSpan FullSpan { get; set; }
}
public static class SyntaxExtensions
{
public static List<MethodDeclarationSyntax> GetFunctions(this SyntaxNode syntaxNode, TextSpan textSpan, string modifiers)
{
return syntaxNode.DescendantNodes(textSpan, null, false)
.OfType<MethodDeclarationSyntax>()
.Where(modificator => modificator.Modifiers.Any(x => x.Text.Contains(modifiers.ToString())))
.ToList();
}
public static List<Region> GetRegions(this SyntaxNode syntaxNode)
{
List<Region> regions = new List<Region>();
var rawRegions = syntaxNode.DescendantNodes(null, true).Where(x => x.RawKind == 8552 || x.RawKind == 8553).ToList();
for (int i = 0; i < rawRegions.Count; i = i + 2)
{
Region region = new Region();
region.Identifier = rawRegions[i].GetText().ToString();
region.FullSpan = new TextSpan(rawRegions[i].FullSpan.Start, rawRegions[i + 1].FullSpan.End - rawRegions[i].FullSpan.Start);
regions.Add(region);
}
return regions;
}
}
class Program
{
static void Main(string[] args)
{
var tree = CSharpSyntaxTree.ParseText(#"
public class MyClass
{
#region FirstRegion
public void MyMethod()
{
}
#endregion
#region SecondRegion
public void MyMethod1()
{
}
private static void MyMethod3()
{
}
#endregion
}");
var syntaxRoot = tree.GetRoot();
var regions = syntaxRoot.GetRegions();
foreach (var item in regions)
{
var publicFunctions = syntaxRoot.GetFunctions(item.FullSpan, Modificators.Public);
}
}
}
Related
I want to add the "Current" property to my list class VolumeList.
Here's my code:
class Volume
{
private string name;
private string color;
private bool has_segment;
public Volume(string Name, string color, bool has_segment)
this.name = name;
this.color = color;
this.has_segment = has_segment;
public string Name
{
get { return name; }
set { name = value; }
}
public string Color
{
get { return color; }
set { color = value; }
}
public bool HasSegment
{
get { return has_segment; }
set { has_segment = value; }
}
public static ROOTList<Volume> VolumeList{ get; set; } = new ROOTList<Volume>();
}
public class ROOTList<T> : List<T>
{
public T Last
{
get
{
return this[this.Count - 1];
}
set
{
this[this.Count - 1] = value;
}
}
public T First
{
get
{
return this[0];
}
set
{
this[0] = value;
}
}
public T Current
{
}
}
I would like to add the "Current" property/method in order to use it like this:
Volume.VolumeList.Current.Name (I get the name of the current volume)
Volume.VolumeList.Current.Name = "test" (I set the name "test" to the current volume).
First and "Last` methods are working.
Please, do you have any solutions?
Best regards
There's a few options on how to do this. As you're just starting out, I'd say use option 1 as this is a lot simpler.
Option 1: Basic using LINQ
Below is a Volume class and a Main method which shows examples of usage. You can use Linq to get the first/last items in the list of volumes and then you can set properties on individual items in the list by using volumes[i] where i is the index of the item in the list.
You can also use Linq to find particular volumes based on Color, Name, HasVolume and then change properties on each of those using a foreach loop.
More info on LINQ is here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/.
Take a look at the example below:
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
internal class Program
{
private static void Main(string[] args)
{
var volumes = new List<Volume>();
var volume1 = new Volume("vol1", "red", true);
var volume2 = new Volume("vol2", "red", true);
volumes.Add(volume1); //adds the first volume
volumes.Add(volume2); //adds the second volume
var lastVolume = volumes.First(); //gets the first volume
var firstVolume = volumes.Last(); //gets the last volume
volumes[0].Color = "blue"; //sets the first volume color to blue
volumes[1].Color = "green"; //sets the second volume color to green
// set all green volumes to purple
var greenVolumes = volumes.Where(v => v.Color == "green");
foreach (var greenVolume in greenVolumes)
{
greenVolume.Color = "purple";
}
}
}
public class Volume
{
public string Name { get; set; }
public string Color { get; set; }
public bool HasSegment { get; set; }
public Volume(string name, string color, bool hasSegment)
{
Name = name;
Color = color;
HasSegment = hasSegment;
}
}
}
Option 2: More advanced using the Iterator pattern
Take a look at the iterator pattern here: https://www.dofactory.com/net/iterator-design-pattern.
Basically, it will allow you to step through the list by selecting MoveNext(), Last, First, and Current. I've added a _current field which keeps track of which item you are working with. It relates to the index of the collection/list.
using System.Collections.Generic;
namespace WebApplication1.Sandbox
{
public class Volume
{
public string Name { get; set; }
public string Color { get; set; }
public bool HasSegment { get; set; }
public Volume(string name, string color, bool hasSegment)
{
Name = name;
Color = color;
HasSegment = hasSegment;
}
}
public class Iterator<T>
{
private readonly List<T> _items;
private int _index;
public T Last
{
get => _items[_items.Count - 1];
set => _items[_items.Count - 1] = value;
}
public T First
{
get => _items[0];
set => _items[0] = value;
}
public T Current
{
get => _items[_index];
set => _items[_index] = value;
}
public Iterator(List<T> items)
{
_items = items;
}
public bool MoveNext()
{
if (_index == _items.Count - 1)
{
return false;
}
else
{
_index++;
return true;
}
}
}
}
Now you can use it like in this unit test:
[Fact]
public void VolumeIteratorGetsNextThenSetsCurrentByProperty()
{
var sut = new Iterator<Volume>(_volumes);
sut.MoveNext();
sut.Current.Name = "vol2modifiedAgain";
Assert.Equal("vol2modifiedAgain", sut.Current.Name);
}
Unit tests are here:
using System.Collections.Generic;
using WebApplication1.Sandbox;
using Xunit;
namespace WebApplication1.Tests
{
public class VolumeIteratorTests
{
private List<Volume> _volumes => new List<Volume>()
{
new Volume("vol1", "green", true),
new Volume("vol2", "green", true)
};
[Fact]
public void VolumeIteratorGetsCurrent()
{
var sut = new Iterator<Volume>(_volumes);
Assert.Equal("vol1", sut.Current.Name);
}
[Fact]
public void VolumeIteratorGetsNext()
{
var sut = new Iterator<Volume>(_volumes);
sut.MoveNext();
Assert.Equal("vol2", sut.Current.Name);
}
[Fact]
public void VolumeIteratorGetsNextThenSetsCurrent()
{
var sut = new Iterator<Volume>(_volumes);
sut.MoveNext();
sut.Current = new Volume("vol2modified", "green", false);
Assert.Equal("vol2modified", sut.Current.Name);
}
[Fact]
public void VolumeIteratorGetsNextThenSetsCurrentByProperty()
{
var sut = new Iterator<Volume>(_volumes);
sut.MoveNext();
sut.Current.Name = "vol2modifiedAgain";
Assert.Equal("vol2modifiedAgain", sut.Current.Name);
}
[Fact]
public void VolumeIteratorGetsLast()
{
var sut = new Iterator<Volume>(_volumes);
Assert.Equal("vol2", sut.Last.Name);
}
[Fact]
public void VolumeIteratorSetsLast()
{
var sut = new Iterator<Volume>(_volumes);
sut.Last = new Volume("last", "red", true);
Assert.Equal("last", sut.Last.Name);
}
[Fact]
public void VolumeIteratorGetsFirst()
{
var sut = new Iterator<Volume>(_volumes);
Assert.Equal("vol1", sut.First.Name);
}
[Fact]
public void VolumeIteratorSetsFirst()
{
var sut = new Iterator<Volume>(_volumes);
sut.First = new Volume("first", "red", true);
Assert.Equal("first", sut.First.Name);
}
[Fact]
public void MoveNextReturnsTrueIfNotLastItem()
{
var sut = new Iterator<Volume>(_volumes);
Assert.True(sut.MoveNext());
}
[Fact]
public void MoveNextReturnsFalseIfLastItem()
{
var sut = new Iterator<Volume>(_volumes);
sut.MoveNext();
Assert.False(sut.MoveNext());
}
}
}
How do I ensure that when someone makes the following call, that the private constructor is executed?
var rc = CrmSecureConfiguration.RestCRMClientConfiguration;
Here's the implementation:
public class CrmSecureConfiguration
{
private CrmSecureConfiguration()
{
var configurationPackage = FabricRuntime.GetActivationContext().GetConfigurationPackageObject("Config");
RestCRMClientConfiguration.CRMOrganizationName = configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMOrganizationName"].Value;
RestCRMClientConfiguration.UserName = configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMWebServiceUserName"].Value;
RestCRMClientConfiguration.Password = configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMWebServicePassword"].Value;
RestCRMClientConfiguration.Domain = configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMWebServiceDomain"].Value;
RestCRMClientConfiguration.CRMWebServiceBaseUrl = $"{configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMWebServiceBaseUrl"].Value}/{RestCRMClientConfiguration.CRMOrganizationName}/api/data/v8.1/";
}
public static RestCRMClientConfiguration RestCRMClientConfiguration { get; private set; }
}
For some reason, the current behavior is such that the private constructor is not executed at all.
You can't do that without creating an instance. Perhaps you want a static constructor instead?
static CrmSecureConfiguration()
{
var configurationPackage = FabricRuntime.GetActivationContext().GetConfigurationPackageObject("Config");
RestCRMClientConfiguration.CRMOrganizationName = configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMOrganizationName"].Value;
RestCRMClientConfiguration.UserName = configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMWebServiceUserName"].Value;
RestCRMClientConfiguration.Password = configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMWebServicePassword"].Value;
RestCRMClientConfiguration.Domain = configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMWebServiceDomain"].Value;
RestCRMClientConfiguration.CRMWebServiceBaseUrl = $"{configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMWebServiceBaseUrl"].Value}/{RestCRMClientConfiguration.CRMOrganizationName}/api/data/v8.1/";
}
Singleton pattern:
public class CrmSecureConfiguration
{
private static CrmSecureConfigurationinstance;
private CrmSecureConfiguration() {
var configurationPackage = FabricRuntime.GetActivationContext().GetConfigurationPackageObject("Config");
RestCRMClientConfiguration.CRMOrganizationName = configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMOrganizationName"].Value;
RestCRMClientConfiguration.UserName = configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMWebServiceUserName"].Value;
RestCRMClientConfiguration.Password = configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMWebServicePassword"].Value;
RestCRMClientConfiguration.Domain = configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMWebServiceDomain"].Value;
RestCRMClientConfiguration.CRMWebServiceBaseUrl = $"{configurationPackage.Settings.Sections["CRMWebService"].Parameters["CRMWebServiceBaseUrl"].Value}/{RestCRMClientConfiguration.CRMOrganizationName}/api/data/v8.1/";
}
public static CrmSecureConfiguration Instance
{
get
{
if (instance == null)
{
instance = new CrmSecureConfiguration();
}
return instance;
}
}
public RestCRMClientConfiguration RestCRMClientConfiguration { get; private set; }
}
Then client:
var rc = CrmSecureConfiguration.Instance.RestCRMClientConfiguration;
Thanks to NHMountainGoat for an answer!
Implementing Interface looks a good choice so we have only the 'needed' method instanciated.
It looks like this now:
EDIT
class Machine
{
//REM: MachineConnexion is a link to the main server where asking the data
internal linkToPLC LinkToPLC;
public IlinkToPLC ILinkPLC;
public interface IlinkToPLC//Interface to linkPLC
{
Int16 MachineNumIS { get; set; }
}
internal class linkToPLC : IlinkToPLC
{
private Int16 Act_MachineNum;
private List<string> genlnkPLCCanvas;
private List<string> genlnkPLCworkingwith;
static private List<string> ListSymbolNoExist;
private string[] ListToPLClnk = {
"GlobalFolder.PMachine[{0}].",
"GlobalFolder.PMachine[{0}].STATE.",
"GlobalFolder.Machine[{0}].",
"GlobalFolder.Machine[{0}].STATE.",
};
public linkToPLC()//ctor
{
genlnkPLCCanvas = new List<string>(ListToPLClnk);
genlnkPLCworkingwith = new List<string>(ListToPLClnk);
ListSymbolNoExist = new List<string>();
Act_MachineNum = MachineNumIS;
}
public Int16 MachineNumIS { get { return (Int16)ReadWriteMachine("data"); } set { ReadWriteMachine("data", value); } }
public string ValueExist(string ValueToreach, bool WorkingDATA = false)
{
if (!WorkingDATA)
{
for (int inc = 0; inc < genlnkPLCworkingwith.Count; inc++)
{
string StrValueToReach = genlnkPLCworkingwith[inc] + ValueToreach;
if (MachineConnexion.SymbolExists(StrValueToReach))
{
ListSymbolNoExist.Clear();
return StrValueToReach;
}
else ListSymbolNoExist.Add(genlnkPLCworkingwith[inc] + ValueToreach);
}
}
else if (WorkingDATA)
{
string StrValueToReach = genlnkPLCworkingwith[10] + ValueToreach;
if (MachineConnexion.SymbolExists(StrValueToReach))
{
ListSymbolNoExist.Clear();
return StrValueToReach;
}
else ListSymbolNoExist.Add(genlnkPLCworkingwith[10] + ValueToreach);
}
if (ListSymbolNoExist.Count != 0)
{
string ErrorList = "";
for (int inc = 0; inc < ListSymbolNoExist.Count; inc++)
{
ErrorList = string.Concat(ErrorList + "Num: " + inc.ToString() + " " + ListSymbolNoExist[inc].ToString() + "\n");
}
Console.WriteLine("Error" + ErrorList);
}
return null;
}
public object ReadWriteMachine(string VariableName, object DataToWrite = null, bool WorkingDATA = false)
{
string valueToFind = "";
if (ValueExist(VariableName) != "FALSE")
{
if (DataToWrite != null) { MachineConnexion.WriteSymbol(valueToFind, DataToWrite); }
return MachineConnexion.ReadSymbol(valueToFind);
}
return VariableName;
}
}
public Machine() //constructor
{
LinkToPLC = new linkToPLC();
}
}
And It doesn't work telling me that the reference object is not defined to an instance of the object..... in the line : Machine() LinkToPLC = new linkToPLC();//REM I found the bug, it was me ;o)) 24112016
//REM 24112016
What are the main differences between those two concept: static Instance and Interface?
Example:
class Program
{
static void Main(string[] args)
{
ITestInterface InterInstance = new TestInterface();
//test Interface
bool value1 = true;
value1 = InterInstance.invert(value1);
InterInstance.print(value1);
//test Instance static
TestStaticInstance staticInstance = new TestStaticInstance();
staticInstance.Instance.invert(value1);
staticInstance.Instance.print(value1);
Console.ReadKey();
}
}
class TestInterface : ITestInterface
{
public bool invert(bool value)
{
return !value;
}
public void print(bool value)
{
Console.WriteLine(value.ToString()+"\n");
}
private void methodX()
{ }
}
interface ITestInterface
{
bool invert(bool value);
void print(bool value);
}
public class TestStaticInstance
{
public TestStaticInstance Instance;
public TestStaticInstance()
{
Instance = this;
}
internal bool invert(bool value)
{
return !value;
}
internal void print(bool value)
{
Console.WriteLine(value.ToString());
}
}
Thanks
Can you structure your other classes to take an instance of the link class? See:
/// <summary>
/// just a stub to demonstrate the model
/// </summary>
internal class Machine
{
public string ReadData() { return "this is data"; }
public void WriteData(string data) { Console.WriteLine(data); }
}
internal interface IMachineDataAccessor
{
string Read();
void Write(string data);
}
class LinkClass : IMachineDataAccessor
{
protected Machine _machine;
public LinkClass(Machine machine)
{
_machine = machine;
}
public void DoMyWork()
{
// insert work somewhere in here.
string dataFromMachine = Read();
Write("outbound data");
}
public string Read()
{
return _machine.ReadData();
}
public void Write(string data)
{
_machine.WriteData(data);
}
}
class PersistentClass
{
IMachineDataAccessor _machineImpl;
public PersistentClass(IMachineDataAccessor machineAccessImplementation)
{
_machineImpl = machineAccessImplementation;
}
public void DoMyWork()
{
string dataFromMachine = _machineImpl.Read();
// insert work here. Or anywhere, actually..
_machineImpl.Write("outbound data");
}
}
class StateClass
{
IMachineDataAccessor _machineImpl;
public StateClass(IMachineDataAccessor machineAccessImplementation)
{
_machineImpl = machineAccessImplementation;
}
public void DoMyWork()
{
string dataFromMachine = _machineImpl.Read();
// insert work here. Or anywhere, actually..
_machineImpl.Write("outbound data");
}
}
static void Main(string[] args)
{
LinkClass link = new LinkClass(new Machine());
PersistentClass persistent = new PersistentClass(link as IMachineDataAccessor);
StateClass state = new StateClass(link as IMachineDataAccessor);
persistent.DoMyWork();
state.DoMyWork();
link.DoMyWork();
}
fans of beautiful code.
I would like to ask my question by two ways. May be it will be useful to understand me.
1) There is code of 2 classes. One of them is nested. Nested class is used to get access to private fields of other one. I would like to get inherit class B:A{class BUnit:AUnit{}} which has the same functional but else has some more methods and fields in B and BUnits classes. How it can be done?
class Program
{
static void Main(string[] args)
{
A a = new A();
a.Add();
a.Add();
a.Add();
bool res=a[0].Rename("1");//res=true;
res = a[1].Rename("1");//res= false;
Console.ReadKey();
}
}
class A
{
private List<AUnit> AUnits;
public AUnit this[int index] {get {return AUnits[index];}}
public A()//ctor
{
AUnits = new List<AUnit>();
}
public void Add()
{
this.AUnits.Add(new AUnit(this));
}
public class AUnit
{
private string NamePr;
private A Container;
public AUnit(A container)//ctor
{
NamePr = "Default";
this.Container = container;
}
public string Name { get { return this.NamePr; } }
public Boolean Rename(String newName)
{
Boolean res = true;
foreach (AUnit unt in this.Container.AUnits)
{
if (unt.Name == newName) res = false;
}
if (res) this.NamePr = String.Copy(newName);
return res;
}
}
}
2) There is two very similar “things” – Class A and Class B. Is it possible to separate their common part, and then “inherit” this two “things” from it ? For example, I would like add some methods like GetUnitsCount() or RemoveUnit() and this methods are common for both. So I should “CopyPaste” this method to A and B but it is not good idea. It will be better to change their common part one time in one place. There is no important how it can be done – inheriting or interfaces or anything else. Important - how?
class Program
{
static void Main(string[] args)
{
A a = new A();
a.Add();
a[0].objB.Add();
a[0].objB.Add();
a[0].objB[0].Val1 = 1;
int res = a[0].objB[0].Val1 + a[0].objB[0].Val2;
Console.ReadKey();
}
}
class A
{
private List<AUnit> Units;
public AUnit this[int index] {get {return Units[index];}}
public A()//ctor
{
Units = new List<AUnit>();
}
public void Add()
{
this.Units.Add(new AUnit(this));
}
public class AUnit
{
private string NamePr;
private A Container;
public B objB;
public AUnit(A container)//ctor
{
NamePr = "Default";
this.Container = container;
this.objB = new B();
}
public string Name { get { return this.NamePr; } }
public Boolean Rename(String newName)
{
Boolean res = true;
foreach (AUnit unt in this.Container.Units)
{
if (unt.Name == newName) res = false;
}
if (res) this.NamePr = String.Copy(newName);
return res;
}
}
}
class B
{
private List<BUnit> Units;
public BUnit this[int index] { get { return Units[index]; } }
public B()//ctor
{
Units = new List<BUnit>();
}
public void Add()
{
this.Units.Add(new BUnit(this));
}
public class BUnit
{
private string NamePr;
private B Container;
public int Val1{get;set;}
public int Val2{get;set;}
public BUnit(B container)//ctor
{
NamePr = "Default";
this.Container = container;
this.Val1 = 10;
this.Val2 = 17;
}
public string Name { get { return this.NamePr; } }
public Boolean Rename(String newName)
{
Boolean res = true;
foreach (BUnit unt in this.Container.Units)
{
if (unt.Name == newName) res = false;
}
if (res) this.NamePr = String.Copy(newName);
return res;
}
}
}
Thank you for your attentions.
To answer your first question, the only thing you need to to to have BUnit inherit from AUnit is to qualify AUnit:
public class BUnit : A.AUnit
{
....
}
from there I believe your question is about basic inheritance which works no differently for nested classes. Nested classes are purely for organization - they are not inherited when you inherit the "containing" class.
I'm working on a project where I have some recursive data structure and I want to create a fixture for it.
The data structure is XmlCommandElement, it has a single method ToCommand that converts XmlCommandElement to Command.
Each node on the tree can be a XmlCommandElement and/or XmlCommandPropertyElement.
Now, in order to test the behaviour of the method ToCommand I want to fetch XmlCommandElement with some arbitrary data.
I want to control the depth of the tree and the amount of instances of XmlCommandElement and/or XmlCommandPropertyElement per node.
So here is the code I'm using for the fixture:
public class XmlCommandElementFixture : ICustomization
{
private static readonly Fixture _fixture = new Fixture();
private XmlCommandElement _xmlCommandElement;
public int MaxCommandsPerDepth { get; set; }
public int MaxDepth { get; set; }
public int MaxPropertiesPerCommand { get; set; }
public XmlCommandElementFixture BuildCommandTree()
{
_xmlCommandElement = new XmlCommandElement();
var tree = new Stack<XmlCommandElementNode>();
tree.Push(new XmlCommandElementNode(0, _xmlCommandElement));
while (tree.Count > 0) {
var node = tree.Pop();
node.Command.Key = CreateRandomString();
node.Command.Properties = CreateProperties();
if (MaxDepth > node.Depth) {
var commands = new List<XmlCommandElement>();
for (var i = 0; i < MaxCommandsPerDepth; i++) {
var command = new XmlCommandElement();
tree.Push(new XmlCommandElementNode(node.Depth + 1, command));
commands.Add(command);
}
node.Command.Commands = commands.ToArray();
}
}
return this;
}
public void Customize(IFixture fixture)
{
fixture.Customize<XmlCommandElement>(c => c.FromFactory(() => _xmlCommandElement)
.OmitAutoProperties());
}
private static string CreateRandomString()
{
return _fixture.Create<Generator<string>>().First();
}
private XmlCommandPropertyElement[] CreateProperties()
{
var properties = new List<XmlCommandPropertyElement>();
for (var i = 0; i < MaxPropertiesPerCommand; i++) {
properties.Add(new XmlCommandPropertyElement {
Key = CreateRandomString(),
Value = CreateRandomString()
});
}
return properties.ToArray();
}
private struct XmlCommandElementNode
{
public XmlCommandElementNode(int depth, XmlCommandElement xmlCommandElement)
{
Depth = depth;
Command = xmlCommandElement;
}
public XmlCommandElement Command { get; }
public int Depth { get; }
}
}
And this is how I'm using it:
xmlCommandElement = new Fixture().Customize(new XmlCommandElementFixture {
MaxDepth = 2,
MaxCommandsPerDepth = 3,
MaxPropertiesPerCommand = 4
}.BuildCommandTree()).Create<XmlCommandElement>();
This works perfectly fine! but the issue I have with it is it isn't generic, the whole point of AutoFixture at least as far as I know is to avoid making specific fixtures.
So what I would really like to do is something like this (found it here but it doesn't work for me.):
var fixture = new Fixture();
fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new DepthThrowingRecursionBehavior(2));
fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(XmlCommandElement), 3));
fixture.Behaviors.Add(new OmitOnRecursionForRequestBehavior(typeof(XmlCommandPropertyElement), 4));
xmlCommandElement = fixture.Create<XmlCommandElement>();
Here is all the code for reference:
Interfaces:
public interface ICommandCollection : IEnumerable<ICommand>
{
ICommand this[string commandName] { get; }
void Add(ICommand command);
}
public interface ICommandPropertyCollection : IEnumerable<ICommandProperty>
{
string this[string key] { get; }
void Add(ICommandProperty property);
}
public interface ICommandProperty
{
string Key { get; }
string Value { get; }
}
public interface ICommand
{
ICommandCollection Children { get; set; }
string Key { get; }
ICommandPropertyCollection Properties { get; }
}
public interface ICommandConvertible
{
ICommand ToCommand();
}
Classes:
public sealed class CommandPropertyCollection : ICommandPropertyCollection
{
private readonly IDictionary<string, ICommandProperty> _properties;
public CommandPropertyCollection()
{
_properties = new ConcurrentDictionary<string, ICommandProperty>();
}
public string this[string key]
{
get
{
ICommandProperty property = null;
_properties.TryGetValue(key, out property);
return property.Value;
}
}
public void Add(ICommandProperty property)
{
_properties.Add(property.Key, property);
}
public IEnumerator<ICommandProperty> GetEnumerator()
{
return _properties.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public sealed class CommandProperty : ICommandProperty
{
public CommandProperty(string key, string value)
{
Key = key;
Value = value;
}
public string Key { get; }
public string Value { get; }
}
public sealed class Command : ICommand
{
public Command(string key, ICommandPropertyCollection properties)
{
Key = key;
Properties = properties;
}
public ICommandCollection Children { get; set; }
public string Key { get; }
public ICommandPropertyCollection Properties { get; }
}
public class XmlCommandPropertyElement : ICommandPropertyConvertible
{
[XmlAttribute("key")]
public string Key { get; set; }
[XmlAttribute("value")]
public string Value { get; set; }
public ICommandProperty ToCommandProperty()
{
return new CommandProperty(Key, Value);
}
}
Finally, the class I'm trying to test is as follow:
public class XmlCommandElement : ICommandConvertible
{
[XmlArray]
[XmlArrayItem("Command", typeof(XmlCommandElement))]
public XmlCommandElement[] Commands { get; set; }
[XmlAttribute("key")]
public string Key { get; set; }
[XmlArray]
[XmlArrayItem("Property", typeof(XmlCommandPropertyElement))]
public XmlCommandPropertyElement[] Properties { get; set; }
public ICommand ToCommand()
{
ICommandPropertyCollection properties = new CommandPropertyCollection();
foreach (var property in Properties) {
properties.Add(property.ToCommandProperty());
}
ICommand command = new Command(Key, properties);
return command;
}
}
The test itself looks like this:
namespace Yalla.Tests.Commands
{
using Fixtures;
using FluentAssertions;
using Ploeh.AutoFixture;
using Xbehave;
using Yalla.Commands;
using Yalla.Commands.Xml;
public class XmlCommandElementTests
{
[Scenario]
public void ConvertToCommand(XmlCommandElement xmlCommandElement, ICommand command)
{
$"Given an {nameof(XmlCommandElement)}"
.x(() =>
{
xmlCommandElement = new Fixture().Customize(new XmlCommandElementFixture {
MaxDepth = 2,
MaxCommandsPerDepth = 3,
MaxPropertiesPerCommand = 4
}.BuildCommandTree()).Create<XmlCommandElement>();
});
$"When the object is converted into {nameof(ICommand)}"
.x(() => command = xmlCommandElement.ToCommand());
"Then we need to have a root object with a key"
.x(() => command.Key.Should().NotBeNullOrEmpty());
"And 4 properties as its children"
.x(() => command.Properties.Should().HaveCount(4));
}
}
}
Thanks to Mark Seemann! the final solution looks like this:
public class RecursiveCustomization : ICustomization
{
public int MaxDepth { get; set; }
public int MaxElements { get; set; }
public void Customize(IFixture fixture)
{
fixture.Behaviors
.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior(MaxDepth));
fixture.RepeatCount = MaxElements;
}
}
And can be used like this:
xmlCommandElement = new Fixture().Customize(new RecursiveCustomization {
MaxDepth = 2,
MaxElements = 3
}).Create<XmlCommandElement>();
You can fairly easily create a small tree by changing the Fixture's recursion behaviour:
[Fact]
public void CreateSmallTree()
{
var fixture = new Fixture();
fixture.Behaviors
.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior(recursionDepth: 2));
var xce = fixture.Create<XmlCommandElement>();
Assert.NotEmpty(xce.Commands);
}
The above test passes.