Class and static variable - c#

OutputFormatsBase is base class for managing Output Formats. OutputFormats1 and OutputFormats2 classes are inherited from OutputFormatsBase class.
I have the problem with static variable _listOfObjects which is in OutputFormatsBase class and can't find a way to solve it.
If I make _listOfObjects static everything works fine except that OutputFormats1 and OutputFormats2 classes static instances are shared, not good.
Could anyone suggest how to solve this? I completely lost.
public class OutputFormats1 : OutputFormatsBase
{
public static readonly OutputFormats1 Bmp = new OutputFormats1 { Value = "BMP", FileExtension = "bmp", Id = 1 };
public static readonly OutputFormats1 Jpg = new OutputFormats1 { Value = "JPG", FileExtension = "jpg", Id = 2 };
public static readonly OutputFormats1 Png = new OutputFormats1 { Value = "PNG", FileExtension = "png", Id = 3 };
public static readonly OutputFormats1 Tiff = new OutputFormats1 { Value = "TIFF", FileExtension = "tif", Id = 4 };
public override OutputFormatsBase Selected { get; set; }
public override OutputFormatsBase Default
{
get { return Png; }
}
}
public class OutputFormats2 : OutputFormatsBase
{
public static readonly OutputFormats2 Pdf = new OutputFormats2 { Value = "PDF", FileExtension = "pdf", Id = 1 };
public override OutputFormatsBase Selected { get; set; }
public override OutputFormatsBase Default
{
get { return Pdf; }
}
}
public abstract class OutputFormatsBase
{
private static readonly List<OutputFormatsBase> _listOfObjects = new List<OutputFormatsBase>();
public string Value { get; protected internal set; }
public string FileExtension { get; protected internal set; }
public int Id { get; protected internal set; }
public abstract OutputFormatsBase Selected { get; set; }
public abstract OutputFormatsBase Default { get; }
protected OutputFormatsBase()
{
_listOfObjects.Add(this);
}
public bool Validate(string format)
{
for (var i = 0; i < _listOfObjects.Count - 1; i++)
{
var outputFormats = _listOfObjects[i];
if (format.ToLower() == outputFormats.Value.ToLower())
{
Selected = outputFormats;
return true;
}
}
return false;
}
}

You could try something like that :
public abstract class OutputFormatsBase<T> where T : OutputFormatsBase
{
private static readonly List<T> _listOfObjects = new List<T>();
protected OutputFormatsBase()
{
_listOfObjects.Add((T)this);
}
}
You'll have one instance of _listOfObjects per template instanciation.

If you use generics then the static values will be unique to each generic type
public class OutputFormats1 : OutputFormatsBase<OutputFormats1>
{
public static readonly OutputFormats1 Bmp = new OutputFormats1 { Value = "BMP", FileExtension = "bmp", Id = 1 };
public static readonly OutputFormats1 Jpg = new OutputFormats1 { Value = "JPG", FileExtension = "jpg", Id = 2 };
public static readonly OutputFormats1 Png = new OutputFormats1 { Value = "PNG", FileExtension = "png", Id = 3 };
public static readonly OutputFormats1 Tiff = new OutputFormats1 { Value = "TIFF", FileExtension = "tif", Id = 4 };
public override OutputFormatsBase Selected { get; set; }
public override OutputFormatsBase Default
{
get { return Png; }
}
}
public class OutputFormats2 : OutputFormatsBase<OutputFormats2>
{
public static readonly OutputFormats2 Pdf = new OutputFormats2 { Value = "PDF", FileExtension = "pdf", Id = 1 };
public override OutputFormatsBase Selected { get; set; }
public override OutputFormatsBase Default
{
get { return Pdf; }
}
}
public abstract class OutputFormatsBase<T> where T:OutputFormatsBase
{
private static readonly List<T> _listOfObjects = new List<T>();
public string Value { get; protected internal set; }
public string FileExtension { get; protected internal set; }
public int Id { get; protected internal set; }
public abstract OutputFormatsBase Selected { get; set; }
public abstract OutputFormatsBase Default { get; }
protected OutputFormatsBase()
{
_listOfObjects.Add((T)this);
}
public bool Validate(string format)
{
for (var i = 0; i < _listOfObjects.Count - 1; i++)
{
var outputFormats = _listOfObjects[i];
if (format.ToLower() == outputFormats.Value.ToLower())
{
Selected = outputFormats;
return true;
}
}
return false;
}
}

Related

Mapping the external api request based on a pattern from a general request

I have an application with a general search request which has an identification number that could be anything like product number or customer number along with additional search criteria. I want to display all the results. I am writing a middleware to call the search api end points.
public class GeneralRequest
{
public string IdentificationNumber { get; set; }
public string Location { get; set; }
public bool IsActive { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime Start { get; set; }
public DateTime Stop { get; set; }
}
public class AdditionalSearch
{
public RangeSearch Range { get; set; }
public string Location { get; set; }
public bool IsActive { get; set; }
}
public class RangeSearch
{
public DateTime Start { get; set; }
public DateTime Stop { get; set; }
}
public class GetProductRequest : AdditionalSearch, ISearchRequest
{
public string ProductId { get; set; }
}
public class GetCustomerRequst : AdditionalSearch, ISearchRequest
{
public string CustomerNumber { get; set; }
}
public class GetManufacturerRequest : AdditionalSearch, ISearchRequest
{
public string ManufacturerNumber { get; set; }
}
// this is a dummy interface to make all the requests general
public interface ISearchRequest
{
}
This is the searchprocessor where I am creating the correct request based on the identification number pattern. But I am failing to assign the AdditonalSearch to the request that I get after invoking the func. Of course it is an interface that has nothing in it. How can I achieve this by not repeating(I mean I don't want to repeat the initialization logic in the dictionary)
Please suggest me what is the best practice here.
public class SearchProcessor
{
private readonly Dictionary<Regex, Func<GeneralRequest, ISearchRequest>> _pattern;
private readonly IAppClient _appClient;
public SearchProcessor(IAppClient appClient)
{
_appClient = appClient;
_pattern = new Dictionary<Regex, Func<GeneralRequest, ISearchRequest>>
{
{new Regex("/\b([0-9]|10)\b /"), p=> new GetProductRequest(){ProductId = p.IdentificationNumber} },
{new Regex("^\\d{}1,9}$"), p=> new GetCustomerRequst(){CustomerNumber = p.IdentificationNumber} },
{new Regex("^\\d{}1,11}$"), p=> new GetManufacturerRequest(){ManufacturerNumber = p.IdentificationNumber} }
};
}
public List<SearchResult> GetAllSearchResults(GeneralRequest request)
{
var requests = _pattern.Where(r => r.Key.IsMatch(request.IdentificationNumber)).Select(v => v.Value);
var responses = new List<SearchResult>();
foreach (var req in requests)
{
var appRequest = req.Invoke(request);
appRequest.AdditionalSearch = new AdditionalSearch // this is where I am not able to assign the additional seach from the general request
{
Range = new RangeSearch { Start = request.Start, Stop = request.Stop}
IsActive = request.IsActive,
Location = request.Location
};
//This calls another api to get the response.
responses.Add(_appclient.FindResult(appRequest));
}
return responses;
}
}
---UPDATE--
Here is the appclient that calls the external api..
sample request for the getproduct route is
public class AppClient : IAppClient
{
private readonly string _baseurl;
private readonly string _getProductRoute;
private readonly string _getCustomerRoute;
private readonly string _getManufacturerRoute;
public AppClient()
{
_getProductRoute = $"{_baseurl}/api/get-product";
_getCustomerRoute = $"{_baseurl}/api/get-customer";
_getManufacturerRoute = $"{_baseurl}/api/get-Manufacturer";
}
public SearchResult FindResult(ISearchRequest searchRequest)
{
var routes = new Dictionary<Type, string>
{
{typeof(GetProductRequest), _getProductRoute },
{typeof(GetCustomerRequst), _getCustomerRoute },
{typeof(GetManufacturerRequest), _getManufacturerRoute }
};
// Here it is going to be http implementation to call above routes.
return new SearchResult();
}
}
The request for get product route is
{
"ProductId":"",
"RangeSearch":{
"Start":"",
"Stop":""
},
"Location":"",
"IsActive":true
}
For get-customer request is
{
"CustomerNumber":"",
"RangeSearch":{
"Start":"",
"Stop":""
},
"Location":"",
"IsActive":true
}
You can simplify your work by doing this :
public class SearchRequest
{
public string IdentificationNumber { get; set; }
public string Location { get; set; }
public bool IsActive { get; set; }
public SearchRequestRange Range { get; set; }
public SearchRequest(string identificationNumber)
{
IdentificationNumber = identificationNumber;
}
}
public class SearchRequestRange
{
public DateTime Start { get; set; }
public DateTime Stop { get; set; }
}
now the process you are doing is not needed, you only need to adjust your ApiClient to something like this :
public class AppClient : IAppClient
{
private static readonly IReadOnlyDictionary<string, string> _endPointsSearchPatterns = new Dictionary<string, string>
{
{"get-product", "/\b([0-9]|10)\b /"},
{"get-customer", "^\\d{}1,9}$"},
{"get-Manufacturer", "^\\d{}1,11}$"}
};
private readonly string _baseUrl;
public AppClient(string baseUrl)
{
if(string.IsNotNullOrWhiteSpace(baseUrl))
throw new ArgumentNullException(nameof(baseUrl));
_baseurl = baseUrl;
}
public IEnumerable<SearchResult> FindResult(SearchRequest searchRequest)
{
var endPoints = _endPointsSearchPatterns.Where(x=> Regex.IsMatch(request.IdentificationNumber , x.Value))?.ToList();
if(endPoints?.Count == 0)
{
yield break;
}
var responses = new List<SearchResult>();
foreach(var endpoint in endPoints)
{
ISearchBy search = null;
switch(endPoint.Key)
{
case "get-product":
action = new ProductApiClient(this);
break;
case "get-customer":
action = new ProductApiClient(this);
break;
case "get-Manufacturer":
action = new ProductApiClient(this);
break;
}
yield return action.SearchBy(searchRequest);
}
return searchResult;
}
}
Regarding GetProductRequest, GetCustomerRequst, and GetManufacturerRequest these should be refactored, and instead you can create a class for each entity like this :
public interface ISearchBy
{
SearchResult ISearchBy(SearchRequest request);
}
public class ProductApiClient : ISearchBy
{
private readonly IAppClient _appClient;
public ProductApiClient(IAppClient appClient)
{
_appClient = appClient;
}
public SearchResult SearchBy(SearchRequest request)
{
// do stuff
}
// other related endpoints
}
public class CustomerApiClient : ISearchBy
{
private readonly IAppClient _appClient;
public CustomerApiClient(IAppClient appClient)
{
_appClient = appClient;
}
public SearchResult SearchBy(SearchRequest request)
{
// do stuff
}
// other related endpoints
}
public class ManufacturerApiClient : ISearchBy
{
private readonly IAppClient _appClient;
public ManufacturerApiClient(IAppClient appClient)
{
_appClient = appClient;
}
public SearchResult SearchBy(SearchRequest request)
{
// do stuff
}
// other related endpoints
}

Serialize multiple object of class but list value are the same

I would like to serialize this class by creating multiple objects. the method works and the objects are saved in the xml file but the values of the List variable are always the same for all objects. how can i do to correct.?
[Serializable()]
public class TextureValues
{
[XmlAttribute("Texture_Info")]
public string Name { get; set; }
public string Date { get; set; }
public List<Point> Points { get; set; }
public int Pensize { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public TextureValues() { }
public TextureValues(string name, string date ,List<Point> points,int pensize,int width,int heigth)
{
this.Name = name;
this.Date = date;
this.Points = points;
this.Pensize = pensize;
this.Width = width;
this.Height = heigth;
}
}
Below the method I used in the form to enter object parameters:
public partial class TextureMaker : Form
{
List<TextureValues> textureInfo = new List<TextureValues>();
List<Point> p = new List<Point>();
public TextureMaker()
{
InitializeComponent();
}
private void panelText_Click(object sender, EventArgs e)
{
p.Add(panelText.PointToClient(Cursor.Position));
}
private void buttonSave_Click(object sender, EventArgs e)
{
textureInfo.AddRange(new TextureValues[]
{
new TextureValues(textBoxName.Text,DateTime.Now.ToString(),p,(int)numericPenSize.Value,(int)numTextWidth.Value,(int)numTextHeight.Value)
});
var path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\textures.xml";
FileStream stream = File.Create(path);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<TextureValues>));
xmlSerializer.Serialize(stream, textureInfo);
stream.Close();
p.Clear();
}
}

What is the difference between the builder design pattern and using classes that inherit from other?

Recently I'm studying design patterns and I've been seeing a pattern called builder that in theory is supposed to encapsulate the complexity of a class, and this is the example I've been practicing:
class ExampleBuilder
{
static void Main(string[] args)
{
Kitchen kit = new Kitchen();
// One pizza
kit.NextPizza(new FourCheeseBuilder("Family"));
kit.PreparePizza();
var FourCheesePizza = kit.PizzaComplete;
// Another pizza
kit.NextPizza(new HawaiianBuilder("Small"));
kit.PreparePizza();
var HawaiianPizza = kit.PizzaComplete;
}
}
// Final Product
public class Pizza
{
public string Dough { get; set; }
public string Sauce { get; set; }
public string Stuff { get; set; }
public string Size { get; set; }
public bool BeenFolded { get; set; }
public Pizza()
{
}
public Pizza(string Size, string Dough, string Sauce, string Stuff, bool BeenFolded) : this()
{
this.Size = Size;
this.Dough = Dough;
this.Sauce = Sauce;
this.Stuff = Stuff;
this.BeenFolded = BeenFolded;
}
}
// Builder
public abstract class PizzaBuilder
{
// Protected para que las clases que implementen puedan acceder
protected Pizza _pizza;
public string Size { get; set; }
public Pizza ObtainPizza() { return _pizza; }
// Un paso para cada una de las propiedades
public virtual void PrepareDough()
{
}
public virtual void PrepareSauce()
{
}
public virtual void PrepareStuff()
{
}
public virtual void FoldPizza()
{
}
}
// BuilderConcrete
public class HawaiianBuilder : PizzaBuilder
{
public HawaiianBuilder(string size)
{
_pizza = new Pizza
{
Size = size
};
}
public override void PrepareDough()
{
_pizza.Dough = "Soft";
}
public override void PrepareSauce()
{
_pizza.Sauce = "Sweet";
}
public override void PrepareStuff()
{
_pizza.Stuff = "pineapple, tomato, ham";
}
}
// Another BuilderConcrete
public class FourCheeseBuilder : PizzaBuilder
{
public FourCheeseBuilder(string size)
{
_pizza = new Pizza
{
Size = size
};
}
public override void PrepareDough()
{
_pizza.Dough = "Coocked";
}
public override void PrepareSauce()
{
_pizza.Sauce = "Roquefort";
}
public override void PrepareStuff()
{
_pizza.Stuff = "mozzarela, gorgonzola, parmesano, ricotta";
}
}
// Director
public class Kitchen
{
private PizzaBuilder _pizzaBuilder;
public void NextPizza(PizzaBuilder pizzaBuilder)
{
_pizzaBuilder = pizzaBuilder;
}
public void PreparePizza()
{
_pizzaBuilder.PrepareDough();
_pizzaBuilder.PrepareSauce();
_pizzaBuilder.PrepareStuff();
}
public Pizza PizzaComplete
{
get { return _pizzaBuilder.ObtainPizza(); }
}
}
I had to do all that stuff to define two types of different classes, and I don't understand why is these better than do this:
class ExampleBuilder
{
static void Main(string[] args)
{
//One Pizza
var FourCheesePizza2 = new FourCheese2("Family");
//Another Pizza
var HawaiianPizza2 = new HawaiianPizza2("Small");
}
}
// Final product
public abstract class Pizza2
{
public string Dough { get; set; }
public string Sauce { get; set; }
public string Stuff { get; set; }
public string Size { get; set; }
public bool BeenFolded { get; set; }
public Pizza2()
{
}
public Pizza2(string Size, string Dough, string Sauce, string Stuff, bool BeenFolded) : this()
{
this.Size = Size;
this.Dough = Dough;
this.Sauce = Sauce;
this.Stuff = Stuff;
this.BeenFolded = BeenFolded;
}
}
public class HawaiianPizza2 : Pizza2
{
public HawaiianPizza2(string size)
{
Size = size;
Dough = "Soft";
Sauce = "Sweet";
Stuff = "pineapple, tomato, ham";
BeenFolded = false;
}
}
public class FourCheese2 : Pizza2
{
public FourCheese2(string size)
{
Size = size;
Dough = "Coocked";
Sauce = "Roquefort";
Stuff = "mozzarela, gorgonzola, parmesano, ricotta";
BeenFolded = true;
}
}
I get the same result but occupying much less lines of code, and that's why I don't know what the pattern builder is really for.

Using a static class as an input parameter on another class

I have no idea if this is possible but it feels like it should be from using C# up to this point.
I want to have a bunch of static classes that contain 'set values' for users of the library to send into another class as parameter.
So this is where I was headed but I can't figure it out. This below is just an example of what I was thinking so don't try and work out 'why' :-)
First - The Class that will be called
public class myClass
{
public bool isError { private set; get; }
public DataTable output { private set; get; }
public String filename { set; private get; }
public settingModule settings { set; private get; }
public static void execute()
{
//Call Private 'getTheData'
//set isError accordingly
//Load output
}
private static DataTable getTheData()
{
//Open and read file for 'fileName'
//Use settings.startingRow
//Use settings.fileType
//User settings.skipEmpty
//Do some stuff
return Datatable from workings
}
}
Second - The Class I want to user to pass
public static class settingMobule
{
public static class fileTypeA
{
public static int startingRow = 1;
public static String fileType = "txt";
public static bool skipEmpty = true;
}
public static class fileTypeB
{
public static int startingRow = 10;
public static String fileType = "csv";
public static bool skipEmpty = false;
}
public static class fileTypeC
{
public static int startingRow = 3;
public static String fileType = "hex";
public static bool skipEmpty = true;
}
}
Lastly the way I want to be able to call it
myClass test = new myClass();
test.filename = "c:\\temp\\test.txt;
test.settings = settingModule.fileTypeA;
test.execute();
if(test.isError == false
{
DataTable myTable = test.output;
test.dispose()
}
Thanks in advance... and yes, "your nuts there is a much better way" is a perfectly valid answer :-)
I would also LOVE to know how to add a .dispose() to my code, it's not something i have got to yet but while I am here... :-D
No, basically; but you could do this:
public sealed class SettingMobule
{
public int StartingRow {get; private set;}
public string FileType {get; private set;}
public bool SkipEmpty {get; private set;}
private SettingMobule(int startingRow, string fileType, bool skipEmpty)
{
StartingRow = startingRow;
FileType = fileType;
SkipEmpty = skipEmpty;
}
public static SettingMobule FileTypeA {get;}
= new SettingMobule(1, "txt", true);
public static SettingMobule FileTypeB {get;}
= new SettingMobule(10, "csv", false);
public static SettingMobule FileTypeC {get;}
= new SettingMobule(3, "hex", true);
}
and pass SettingMobule.FileTypeA as an instance, etc.
No. This is not possible. It is not possible because of 2 reasons:
Static classes cannot be passed around.
The receiver cannot know that these classes are supposed to contain the same set of settings and has no way to access them.
Choose another approach where there is only one non-static file type class used to create several setting objects: (C# 6.0)
public class FileType
{
public FileType(int startingRow, string extension, bool skipEmpty)
{
this.StartingRow = startingRow;
this.Extension = extension; // 'FileType': Member names cannot be the same as their
// enclosing type.
this.SkipEmpty = skipEmpty;
}
public int StartingRow { get; }
public string Extension { get; }
public bool SkipEmpty { get; }
}
The static settings class can now present several setting objects of the same type that can be passed around.
public static class SettingModule
{
public static FileType TxtFileType { get; } = new FileType(1, "txt", true);
public static FileType CsvFileType { get; } = new FileType(10, "csv", false);
public static FileType HexFileType { get; } = new FileType(3, "hex", true);
}
Now, the test class could be written as:
public class MyTestClass
{
private FileType fileType;
private string filename;
public MyTestClass(FileType fileType, string filename)
{
this.fileType = fileType;
this.filename = filename;
}
public void Execute()
{
Console.WriteLine(
$"Extension = {fileType.Extension}, starting row = {fileType.StartingRow}");
}
}
And you can perform the test like this
var test = new MyTestClass(SettingModule.TxtFileType, #"c:\temp\test.txt");
test.Execute();
Non-static classes are a kind of template from which numerous objects can be created. Unlike static classes, such classes are types that can be used to declare variables, method parameters, properties and more.
Unfortunately in C# static classes are extremely limited in what they will allow you to do.
However, with Reflection and Types, you can do something similar, but I don't think you should.
void Main() {
var test = new MyClass(typeof(settingModule.fileTypeB));
Console.WriteLine(test.StartingRow);
}
public class MyClass {
Type SettingsClass { get; set; }
public MyClass(Type sc) {
SettingsClass = sc;
}
public int StartingRow {
get {
return (int)SettingsClass.GetField("startingRow", BindingFlags.Static | BindingFlags.Public).GetValue(null);
}
}
}
public static class settingModule {
public static class fileTypeA {
public static int startingRow = 1;
public static String fileType = "txt";
public static bool skipEmpty = true;
}
public static class fileTypeB {
public static int startingRow = 10;
public static String fileType = "csv";
public static bool skipEmpty = false;
}
public static class fileTypeC {
public static int startingRow = 3;
public static String fileType = "hex";
public static bool skipEmpty = true;
}
}
I think what you should do is create instances of a subclass and pass that:
void Main() {
var test = new MyClass();
test.Settings = settingModule.fileTypeA;
Console.WriteLine(test.Settings.startingRow);
}
public class MyClass {
public settingModule.settingsSet Settings { get; set; }
}
public static class settingModule {
public class settingsSet {
public readonly int startingRow;
public readonly string fileType;
public readonly bool skipEmpty;
public settingsSet(int sr, string ft, bool se) {
startingRow = sr;
fileType = ft;
skipEmpty = se;
}
}
public static settingsSet fileTypeA = new settingsSet(1, "txt", true);
public static settingsSet fileTypeB = new settingsSet(10, "csv", false);
public static settingsSet fileTypeC = new settingsSet(3, "hex", true);
}
You can even make it written more like your static class:
public static class settingModule {
public struct settingsSet {
public int startingRow;
public string fileType;
public bool skipEmpty;
}
public static readonly settingsSet fileTypeA = new settingsSet {
startingRow = 1,
fileType = "txt",
skipEmpty = true
};
public static readonly settingsSet fileTypeB = new settingsSet {
startingRow = 10,
fileType = "csv",
skipEmpty = false
};
public static readonly settingsSet fileTypeC = new settingsSet {
startingRow = 3,
fileType = "hex",
skipEmpty = true
};
}

Create a fixture for recursive data structure with AutoFixture

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.

Categories

Resources