I will give you a short review about what i'm trying to do:
I want to fill the DropDown of a AutoCompleteTextView with my own Objects. those objects contain 3 Strings. In the list_item_view should be 2 of the strings. This List should be filterable.
Now a bit of code, what i've done till now:
My CustomAdapter looks like this:
public class CustomerAdapter : ArrayAdapter<CustomerSingle>, IFilterable
{
private ws_test.Test ws=null;
public static List<CustomerSingle> _contactList;
private Activity _activity;
private CustomerAdapterFilter filter = null;
public CustomerAdapter(Activity activity, Context context,int resourceId)//List<CustomerSingle> assets)
:base(context,resourceId)//,assets)
{
_activity = activity;
ws=new ws_test.Test();
_contactList = new List<CustomerSingle>();
}
public static List<CustomerSingle> getCustomerList()
{
return _contactList;
}
public void Add(CustomerSingle item)
{
_contactList.Add(item);
}
public override int Count
{
get { return _contactList.Count; }
}
public override long GetItemId(int position)
{
return _contactList[position].id;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var view = convertView ?? _activity.LayoutInflater.Inflate(Resource.Layout.details, parent, false);
var contactName = view.FindViewById<TextView>(Resource.Id.Name);
var contactAddress = view.FindViewById<TextView>(Resource.Id.Address);
contactName.Text = _contactList[position].name;// +"\n" + _contactList[position].address;
contactAddress.Text = _contactList[position].address;
return view;
}
public override Filter Filter
{
get
{
return new CustomerAdapterFilter();
}
}
public override void NotifyDataSetChanged()
{
base.NotifyDataSetChanged();
}
public override void NotifyDataSetInvalidated()
{
base.NotifyDataSetInvalidated();
}
}
The CustomerSingle looks like this:
public class CustomerSingle
{
public string no { get; set; }
public string name { get; set; }
public string address { get; set; }
public int id { get; set; }
public CustomerSingle(string no, string name, string address, int id)
{
this.address = address;
this.name = name;
this.no = no;
this.id = id;
}
}
Ok, now I need an own Filter what i'm trying to do here:
public class CustomerAdapterFilter:Filter
{
protected object mLock = new object();
protected List<CustomerSingle> mOriginalValues = null;
protected override FilterResults PerformFiltering(Java.Lang.ICharSequence prefix)
{
FilterResults results = new FilterResults();
if (mOriginalValues == null) {
lock(mLock) {
mOriginalValues = new List<CustomerSingle>(CustomerAdapter._contactList);
}
}
if (prefix == null || prefix.Length() == 0) {
lock (mLock) {
List<CustomerSingle> list = new List<CustomerSingle>(mOriginalValues);
IntPtr listptr = list.
results.Values = list;
results.Count = list.Count;
}
} else {
String prefixString = prefix.ToString().ToLowerInvariant();
List<CustomerSingle> values = mOriginalValues;
int count = values.Count;
List<CustomerSingle> newValues = new List<CustomerSingle>(count);
for (int i = 0; i < count; i++) {
CustomerSingle value = values.ElementAt(i);
String valueText = value.ToString().ToLowerInvariant();
// First match against the whole, non-splitted value
if (valueText.StartsWith(prefixString)) {
newValues.Add(value);
} else {
String[] words = valueText.Split(' ');
int wordCount = words.Length;
for (int k = 0; k < wordCount; k++) {
if (words[k].StartsWith(prefixString)) {
newValues.Add(value);
break;
}
}
}
}
results.Values = (Object) newValues;
results.Count = newValues.Count;
}
return results;
}
protected override void PublishResults(Java.Lang.ICharSequence constraint, Filter.FilterResults results)
{
//noinspection unchecked
var mObjects = results.Values;
if (results.Count > 0)
{
NotifyDataSetChanged();
}
else
{
notifyDataSetInvalidated();
}
}
}
My problem is, I can't Convert from Java.Lang.Object to my CustomerSingle... Does someone have an Idea?
Thank you!
Update: I changed to JavaList in the filter and did the extension to Java.Lang.Object at the CustomerSingle
Make your CustomerSingle class subclass Java.Lang.Object:
public class CustomerSingle : Java.Lang.Object
UPDATE:
My guess is that this line:
results.Values = (Object) newValues;
is trying to convert to System.Object instead of Java.Lang.Object, try (Java.Lang.Object) instead.
Related
I want to remove Server1 from it's listbox, i'd want it to remove all the other items in the other listboxes associated with it as well. ("Server-Domain1" and all the "Server1-Domain1-CSR's"). Is there a way to do this?
To "bind" these list boxes i just used:
domainListBox.Items.Add((serverListBox.SelectedItem) + "-" + (this.domainTextbox.Text));
and
csrListBox.Items.Add((domainListBox.SelectedItem) + ("-CSR-1"));
csrListBox.Items.Add((domainListBox.SelectedItem) + ("-CSR-2"));
csrListBox.Items.Add((domainListBox.SelectedItem) + ("-CSR-3"));
If you choose your server from servers listbox, you can remove associated items like this (lets pretend there is some remove button, you selecting domain from listbox and clicking on remove button):
private void removeBtn_Click(object sender, EventArgs e)
{
List<string> items = csrListBox.Items.Cast<string>().ToList();
foreach (string item in csrListBox.Items)
{
Regex regex = new Regex(#"^" + domainListBox.SelectedItem + #"\w*");
Match match = regex.Match(item);
if (match.Success)
{
items.Remove(item);
}
}
csrListBox.DataSource = items;
}
Hope it helps.
Create a class that encapsulates the Server and it's details such as Domains and Csrs. Create a list of Servers and bind it to the first List Box. Then bind the other two List Boxes to the currently selected item of the first List Box. The end result may look like this:
serverListBox.ItemSource = Servers;
domainListBox.ItemSource = (serverListBox.SelectedItem as Server).Domains;
csrListBox.ItemSource = (serverListBox.SelectedItem as Server).Csrs;
This enables you to set the different List Boxes data without writing a lot of code that could make it unmaintainable.
Well, seeing your code, you just have to do something like this when you remove your server:
string server = serverListBox.SelectedItem as string;
serverListBox.Remove(server);
for (int i = domainListBox.Items.Count -1; i >-1; i--)
{
if (domainListBox.Items[i].ToString().StartsWith(server))
{
string domain = domainListBox.Items[i].ToString();
domainListBox.Items.RemoveAt(i);
for (int j = csrListBox.Items.Count-1; j > -1; j--)
{
if (csrListBox.Items[j].ToString().StartsWith(domain))
{
csrListBox.Items.RemoveAt(j);
}
}
}
}
Edit I have tested it now, this should work
a better option would be using observable pattern as
public class ObservableObject<T> : IList, IListSource
{
protected BindingSource src = null;
List<ListControl> Subscribers;
ObservableCollection<T> Data = new ObservableCollection<T>();
public bool IsReadOnly
{
get
{
return false;
}
}
public bool IsFixedSize
{
get
{
return false;
}
}
public int Count
{
get
{
return Data.Count;
}
}
public object SyncRoot
{
get
{
throw new NotImplementedException();
}
}
public bool IsSynchronized
{
get
{
throw new NotImplementedException();
}
}
public bool ContainsListCollection
{
get
{
return true;
}
}
object IList.this[int index]
{
get
{
return Data[index];
}
set
{
Data[index] = (T)value;
}
}
public T this[int index]
{
get
{
return Data[index];
}
set
{
Data[index] = value;
}
}
public ObservableObject()
{
Data.CollectionChanged += Domains_CollectionChanged;
Subscribers = new List<ListControl>();
src = new BindingSource();
src.DataSource = Data;
}
private void Domains_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
src.ResetBindings(false);
}
public virtual void Subscribe(ListBox ctrl)
{
this.Subscribers.Add(ctrl);
//ctrl.DataBindings.Add(new Binding("SelectedValue", src, "Name",
// true, DataSourceUpdateMode.Never));
ctrl.DataSource = src;
}
public int Add(object value)
{
Data.Add((T)value);
return Data.Count - 1;
}
public bool Contains(object value)
{
return Data.Contains((T)value);
}
public void Clear()
{
Data.Clear();
}
public int IndexOf(object value)
{
return Data.IndexOf((T)value);
}
public void Insert(int index, object value)
{
Data.Insert(index, (T)value);
}
public void Remove(object value)
{
Data.Remove((T)value);
}
public void RemoveAt(int index)
{
Data.RemoveAt(index);
}
public void CopyTo(Array array, int index)
{
throw new NotImplementedException();
}
public IEnumerator GetEnumerator()
{
return Data.GetEnumerator();
}
public IList GetList()
{
return Data;
}
}
public class BaseModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public string Name
{
get { return _name; }
set
{
this._name = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
}
public class CSR : BaseModel
{
public override string ToString()
{
return Name;
}
}
public class Domain : BaseModel
{
public ObservableObject<CSR> CSRs { get; set; }
public Domain()
{
CSRs = new ObservableObject<CSR>();
}
public override string ToString()
{
return Name;
}
}
public class Server : BaseModel
{
public ObservableObject<Domain> Domains { get; set; }
public Server()
{
Domains = new ObservableObject<Domain>();
}
public override string ToString()
{
return Name;
}
}
public class DataModel : BaseModel
{
public ObservableObject<Server> Servers
{
get; set;
}
public DataModel()
{
Servers = new ObservableObject<Server>();
}
public override string ToString()
{
return Name;
}
}
on Form
DataModel m = new DataModel();
public Form1()
{
...
m.Servers.Subscribe(listBox1);
}
private void listBox2_Click(object sender, EventArgs e)
{
if (listBox2.SelectedIndex >= 0)
{
m.Servers[listBox1.SelectedIndex].Domains[listBox2.SelectedIndex].CSRs.Subscribe(listBox3);
}
}
private void listBox1_Click(object sender, EventArgs e)
{
if (listBox1.SelectedIndex >= 0)
{
m.Servers[listBox1.SelectedIndex].Domains.Subscribe(listBox2);
}
}
you dont have to write code to remove items from forms in this perspective.removing object from model would do
Im trying to create an undoredo manager that doesnt matter on what object you pass in. But I cant figure out how to do this in C#. I would like to do something like a pointer to the Original Object and only change where that pointer is pointing to.
C# code
public class UndoableObject<T> : IUndoableAction
{
private T UndoObject;
private T ClonedObject;
private readonly string _name;
public string Name
{
get { return _name; }
}
public UndoableObject(ref T ObjectToUndo, string name)
{
_name = name;
UndoObject = ObjectToUndo;
ClonedObject = ObjectExtensions.Copy<T>(ObjectToUndo);
}
public void Redo()
{
T temp = ObjectExtensions.Copy<T>(UndoObject);
UndoObject = ClonedObject;
ClonedObject = temp;
}
public void Undo()
{
T temp = ObjectExtensions.Copy<T>(UndoObject);
UndoObject = ClonedObject;
ClonedObject = temp;
}
}
A C++ implementation of what i would like to happen
template<typename T>
public class UndoableObject : IUndoableAction
{
private T *UndoObject;
private T *ClonedObject;
private string _name;
public string GetName
{
return _name;
}
public UndoableObject(T *ObjectToUndo, string name)
{
_name = name;
UndoObject = ObjectToUndo;
ClonedObject = &ObjectExtensions.Copy(*ObjectToUndo);
}
public void Redo()
{
T *temp = &ObjectExtensions.Copy(*UndoObject);
delete UndoObject;
UndoObject = ClonedObject;
ClonedObject = temp;
}
public void Undo()
{
T *temp = &ObjectExtensions.Copy(*UndoObject);
delete UndoObject;
UndoObject = ClonedObject;
ClonedObject = temp;
}
}
I know there are probably memory leaks in it but i wrote up the c++ without compiling it. Hopefully I made some sense.
Have a look at the following:
//using System;
//using Sytem.Collections.Generic
public class UndoableObject<T>
{
List<T> _list = new List<T>();
string _name;
int _step = 0;
public UndoableObject(T source, string name)
{
_list.Clear();
_list = new List<T>();
_list.Add(source);
_name = name;
_step = 0;
}
public string GetName
{
get { return _name; }
}
public void Record(T item)
{
_list.Add(item);
_step++;
}
public T Undo()
{
_step--;
if (_step >= 0)
{
return _list[_step];
}
else
{
_step = 0;
return _list[_step];
}
}
public T Redo()
{
_step++;
if (_step < _list.Count)
{
return _list[_step];
}
else
{
_step = _list.Count - 1;
return _list[_step];
}
}
}
Basic Usage:
var pictureBox1 = new PictureBox();
var _bitmap = new Bitmap(300, 300);
var uo = new UndoableObject<Bitmap>(_bitmap, "bitmap");
Undo:
pictureBox1.Image = uo.Undo();
Record:
uo.Record((Bitmap)pictureBox1.Image);
Redo:
pictureBox1.Image = uo.Redo();
I searched this, but I didn't find the answer to this specific question (although there are many similar ones).
I have a Column class in my ViewModel like this:
public class Column
{
public string Header { get; set; }
public ObservableCollection<double> Data { get; private set; }
public DummyColumn()
{
Data = new ObservableCollection<double>();
}
}
In MyViewModel class itself, I have Columns property:
public class MyViewModel
{
public ObservableCollection<Column> { get; private set; }
public MyViewModel()
{
Columns = new ObservableCollection<DummyColumn>();
var c1 = new DummyColumn() { Header = "A" };
c1.Data.Add(5);
c1.Data.Add(6);
Columns.Add(c1);
var c2 = new DummyColumn() { Header = "B" };
c2.Data.Add(51);
c2.Data.Add(61);
Columns.Add(c2);
}
}
I want to bind the Columns property of the latter class to the columns of a DataGrid in view. For each Column instance, I want to show the Header property as the column's header and its Data property as cells' values. How can I do that?
I reached this goal using ideas from here and here. I implemented three classes: Table, Row and RowPropertyDescriptor in my VM. Here is the code:
class RowPropertyDescriptor : PropertyDescriptor
{
private int index;
public RowPropertyDescriptor(string name, int index)
: base(name, null)
{
this.index = index;
}
#region PropertyDescriptor
public override string DisplayName { get { return Name; } }
public override Type ComponentType { get { return typeof(double); } }
public override bool IsReadOnly { get { return false; } }
public override Type PropertyType { get { return typeof(double); } }
public override object GetValue(object component)
{
return ((Row)component)[index];
}
public override void SetValue(object component, object value)
{
((Row)component)[index] = (double)value;
}
public override bool CanResetValue(object component)
{
return false;
}
public override void ResetValue(object component)
{
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
#endregion
}
class Row : DynamicObject
{
private Table table;
private int row;
public Row(Table namedArraysView, int row)
{
this.table = namedArraysView;
this.row = row;
}
public double this[int col]
{
get { return table.RawData[col].Data[row]; }
set { table.RawData[col].Data[row] = value; }
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
int idx;
bool found = table.PropertiesIndex.TryGetValue(binder.Name, out idx);
if (found)
{
try
{
this[idx] = Convert.ToDouble(value);
return true;
}
catch (Exception ex)
{
return false;
}
}
return base.TrySetMember(binder, value);
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
int idx;
bool found = table.PropertiesIndex.TryGetValue(binder.Name, out idx);
if (found)
{
result = this[idx];
return true;
}
return base.TryGetMember(binder, out result);
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return table.PropertyNames;
}
}
class Table : BindingList<Row>, ITypedList
{
public ObservableCollection<INamedArray> RawData { get; private set; }
internal List<string> PropertyNames { get; private set; }
internal Dictionary<string, int> PropertiesIndex { get; private set; }
public Table(ObservableCollection<INamedArray> headeredArrays)
{
bind(headeredArrays);
headeredArrays.CollectionChanged += (object sender, NotifyCollectionChangedEventArgs e) => { bind(headeredArrays); };
}
private void bind(ObservableCollection<INamedArray> headeredArrays)
{
Clear();
if (headeredArrays == null)
{
RawData = null;
PropertyNames = null;
PropertiesIndex = null;
return;
}
RawData = headeredArrays;
PropertyNames = RawData.Select(d => d.Name).ToList();
PropertiesIndex = new Dictionary<string, int>();
for (int i = 0; i < RawData.Count; i++)
PropertiesIndex.Add(RawData[i].Name, i);
int nRows = headeredArrays[0].Data.Count;
for (int i = 0; i < nRows; i++)
Add(new Row(this, i));
}
#region ITypedList
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
var dynamicDescriptors = new List<PropertyDescriptor>();
if (this[0].GetDynamicMemberNames() == null) return new PropertyDescriptorCollection(new PropertyDescriptor[] { });
var memberNames = this[0].GetDynamicMemberNames().ToArray();
for (int i = 0; i < memberNames.Length; i++)
dynamicDescriptors.Add(new RowPropertyDescriptor(memberNames[i], i));
return new PropertyDescriptorCollection(dynamicDescriptors.ToArray());
}
public string GetListName(PropertyDescriptor[] listAccessors)
{
return null;
}
#endregion
}
Then, one can easily create a Table with passing his/her columns to this class. This Table can be visualised correctly in view. The only limitation is that binding is one way which is not very hard to work around.
I'm trying to control the depth of generation of an object tree with Autofixture. In some cases I want just to generate the root object and in another set of cases I may want to generate the tree up to a certain depth (2, 3, let's say).
class Foo {
public string Name {get;set;}
public Bar Bar {get;set;}
public AnotherType Xpto {get;set;}
public YetAnotherType Xpto {get;set;}
}
class Bar {
public string Name {get;set;}
public string Description {get;set;}
public AnotherType Xpto {get;set;}
public YetAnotherType Xpto {get;set;}
public Xpto Xpto {get;set;}
}
class Xpto {
public string Description {get;set;}
public AnotherType Xpto {get;set;}
public YetAnotherType Xpto {get;set;}
}
With the example above I would want (depth 1) to control the generation process so that only the Foo class is instantiated and the Bar property or any other reference type on that class is not populated or (depth 2) I would want the Foo class instantiated, the Bar property populated with a new instance of Bar but the Xpto property or any other reference type on that class not populated.
In case I did not spot it in the codebase does Autofixture have a customisation or behaviour to allow us to have that kind of control?
Again, it's not recursion that I want to control but the depth of population of the object graph.
No Bar
One-off:
var f = fixture.Build<Foo>().Without(f => f.Bar).Create();
Reusable:
fixture.Customize<Foo>(c => c.Without(f => f.Bar));
var f = fixture.Create<Foo>();
No Xpto
One-off:
var f = fixture
.Build<Foo>()
.With(
f => f.Bar,
fixture.Build<Bar>().Without(b => b.Xpto).Create())
.Create();
Reusable:
fixture.Customize<Bar>(c => c.Without(b => b.Xpto));
var f = fixture.Create<Foo>();
You can use the below GenerationDepthBehavior class as follows:
fixture.Behaviors.Add(new GenerationDepthBehavior(2));
public class GenerationDepthBehavior : ISpecimenBuilderTransformation
{
private const int DefaultGenerationDepth = 1;
private readonly int generationDepth;
public GenerationDepthBehavior() : this(DefaultGenerationDepth)
{
}
public GenerationDepthBehavior(int generationDepth)
{
if (generationDepth < 1)
throw new ArgumentOutOfRangeException(nameof(generationDepth), "Generation depth must be greater than 0.");
this.generationDepth = generationDepth;
}
public ISpecimenBuilderNode Transform(ISpecimenBuilder builder)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
return new GenerationDepthGuard(builder, new GenerationDepthHandler(), this.generationDepth);
}
}
public interface IGenerationDepthHandler
{
object HandleGenerationDepthLimitRequest(object request, IEnumerable<object> recordedRequests, int depth);
}
public class DepthSeededRequest : SeededRequest
{
public int Depth { get; }
public int MaxDepth { get; set; }
public bool ContinueSeed { get; }
public int GenerationLevel { get; private set; }
public DepthSeededRequest(object request, object seed, int depth) : base(request, seed)
{
Depth = depth;
Type innerRequest = request as Type;
if (innerRequest != null)
{
bool nullable = Nullable.GetUnderlyingType(innerRequest) != null;
ContinueSeed = nullable || innerRequest.IsGenericType;
if (ContinueSeed)
{
GenerationLevel = GetGenerationLevel(innerRequest);
}
}
}
private int GetGenerationLevel(Type innerRequest)
{
int level = 0;
if (Nullable.GetUnderlyingType(innerRequest) != null)
{
level = 1;
}
if (innerRequest.IsGenericType)
{
foreach (Type generic in innerRequest.GetGenericArguments())
{
level++;
level += GetGenerationLevel(generic);
}
}
return level;
}
}
public class GenerationDepthGuard : ISpecimenBuilderNode
{
private readonly ThreadLocal<Stack<DepthSeededRequest>> requestsByThread
= new ThreadLocal<Stack<DepthSeededRequest>>(() => new Stack<DepthSeededRequest>());
private Stack<DepthSeededRequest> GetMonitoredRequestsForCurrentThread() => this.requestsByThread.Value;
public GenerationDepthGuard(ISpecimenBuilder builder)
: this(builder, EqualityComparer<object>.Default)
{
}
public GenerationDepthGuard(
ISpecimenBuilder builder,
IGenerationDepthHandler depthHandler)
: this(
builder,
depthHandler,
EqualityComparer<object>.Default,
1)
{
}
public GenerationDepthGuard(
ISpecimenBuilder builder,
IGenerationDepthHandler depthHandler,
int generationDepth)
: this(
builder,
depthHandler,
EqualityComparer<object>.Default,
generationDepth)
{
}
public GenerationDepthGuard(ISpecimenBuilder builder, IEqualityComparer comparer)
{
this.Builder = builder ?? throw new ArgumentNullException(nameof(builder));
this.Comparer = comparer ?? throw new ArgumentNullException(nameof(comparer));
this.GenerationDepth = 1;
}
public GenerationDepthGuard(
ISpecimenBuilder builder,
IGenerationDepthHandler depthHandler,
IEqualityComparer comparer)
: this(
builder,
depthHandler,
comparer,
1)
{
}
public GenerationDepthGuard(
ISpecimenBuilder builder,
IGenerationDepthHandler depthHandler,
IEqualityComparer comparer,
int generationDepth)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));
if (depthHandler == null) throw new ArgumentNullException(nameof(depthHandler));
if (comparer == null) throw new ArgumentNullException(nameof(comparer));
if (generationDepth < 1)
throw new ArgumentOutOfRangeException(nameof(generationDepth), "Generation depth must be greater than 0.");
this.Builder = builder;
this.GenerationDepthHandler = depthHandler;
this.Comparer = comparer;
this.GenerationDepth = generationDepth;
}
public ISpecimenBuilder Builder { get; }
public IGenerationDepthHandler GenerationDepthHandler { get; }
public int GenerationDepth { get; }
public int CurrentDepth { get; }
public IEqualityComparer Comparer { get; }
protected IEnumerable RecordedRequests => this.GetMonitoredRequestsForCurrentThread();
public virtual object HandleGenerationDepthLimitRequest(object request, int currentDepth)
{
return this.GenerationDepthHandler.HandleGenerationDepthLimitRequest(
request,
this.GetMonitoredRequestsForCurrentThread(), currentDepth);
}
public object Create(object request, ISpecimenContext context)
{
if (request is SeededRequest)
{
int currentDepth = 0;
var requestsForCurrentThread = GetMonitoredRequestsForCurrentThread();
if (requestsForCurrentThread.Count > 0)
{
currentDepth = requestsForCurrentThread.Max(x => x.Depth) + 1;
}
DepthSeededRequest depthRequest = new DepthSeededRequest(((SeededRequest)request).Request, ((SeededRequest)request).Seed, currentDepth);
if (depthRequest.Depth >= GenerationDepth)
{
var parentRequest = requestsForCurrentThread.Peek();
depthRequest.MaxDepth = parentRequest.Depth + parentRequest.GenerationLevel;
if (!(parentRequest.ContinueSeed && currentDepth < depthRequest.MaxDepth))
{
return HandleGenerationDepthLimitRequest(request, depthRequest.Depth);
}
}
requestsForCurrentThread.Push(depthRequest);
try
{
return Builder.Create(request, context);
}
finally
{
requestsForCurrentThread.Pop();
}
}
else
{
return Builder.Create(request, context);
}
}
public virtual ISpecimenBuilderNode Compose(
IEnumerable<ISpecimenBuilder> builders)
{
var composedBuilder = ComposeIfMultiple(
builders);
return new GenerationDepthGuard(
composedBuilder,
this.GenerationDepthHandler,
this.Comparer,
this.GenerationDepth);
}
internal static ISpecimenBuilder ComposeIfMultiple(IEnumerable<ISpecimenBuilder> builders)
{
ISpecimenBuilder singleItem = null;
List<ISpecimenBuilder> multipleItems = null;
bool hasItems = false;
using (var enumerator = builders.GetEnumerator())
{
if (enumerator.MoveNext())
{
singleItem = enumerator.Current;
hasItems = true;
while (enumerator.MoveNext())
{
if (multipleItems == null)
{
multipleItems = new List<ISpecimenBuilder> { singleItem };
}
multipleItems.Add(enumerator.Current);
}
}
}
if (!hasItems)
{
return new CompositeSpecimenBuilder();
}
if (multipleItems == null)
{
return singleItem;
}
return new CompositeSpecimenBuilder(multipleItems);
}
public virtual IEnumerator<ISpecimenBuilder> GetEnumerator()
{
yield return this.Builder;
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
public class GenerationDepthHandler : IGenerationDepthHandler
{
public object HandleGenerationDepthLimitRequest(
object request,
IEnumerable<object> recordedRequests, int depth)
{
return new OmitSpecimen();
}
}
This feature was requested in a github issue. It was ultimately rejected. However, it was rejected because there was a nice, simple solution posted within the issue.
public class GenerationDepthBehavior: ISpecimenBuilderTransformation
{
public int Depth { get; }
public GenerationDepthBehavior(int depth)
{
Depth = depth;
}
public ISpecimenBuilderNode Transform(ISpecimenBuilder builder)
{
return new RecursionGuard(builder, new OmitOnRecursionHandler(), new IsSeededRequestComparer(), Depth);
}
private class IsSeededRequestComparer : IEqualityComparer
{
bool IEqualityComparer.Equals(object x, object y)
{
return x is SeededRequest && y is SeededRequest;
}
int IEqualityComparer.GetHashCode(object obj)
{
return obj is SeededRequest ? 0 : EqualityComparer<object>.Default.GetHashCode(obj);
}
}
}
You can then use this as follows:
fixture.Behaviors.Add(new GenerationDepthBehavior(2));
I recently migrated to new version of protobuf-net, and i started getting this error message after
Repeated data (a list, collection, etc) has inbuilt behaviour and cannot be used as a subclass
Call Stack Trace
protobuf-net.dll!ProtoBuf.Meta.MetaType.AddSubType(int fieldNumber = 1, System.Type derivedType = {Name = "InfoColumn`1" FullName = "Om.Common.InfoSet.InfoColumn`1[[System.Double, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"}) Line 83 C#
protobuf-net.dll!ProtoBuf.Meta.MetaType.ApplyDefaultBehaviour() Line 431 + 0x32 bytes C#
Any help in this regard is appreciated. I am planning to rollback my code to previous version of protobuf-net
Below is the class info.
[DataContract]
[ProtoInclude(1, typeof(InfoColumn<Double>))]
[ProtoInclude(2, typeof(InfoColumn<String>))]
[ProtoInclude(3, typeof(InfoColumn<DateTime>))]
[ProtoInclude(4, typeof(InfoColumn<Boolean>))]
public abstract class IInfoColumnBase
{
[DataMember(Order = 101)]
public abstract bool IsSingleValue { get; set; }
[DataMember(Order = 102)]
public abstract string Name { get; set; }
[DataMember(Order = 103)]
public abstract InfoColumnDataType DataType { get; set; }
public abstract long Insert();
public abstract void Insert(long index);
public abstract void SetValue(long index, object val);
public abstract void CopyValues(long start, long end, IInfoColumnBase destCol, long index);
public abstract long GetIndex(object val);
public abstract void Remove(long index);
public abstract object GetValue(long index);
public abstract object GetInternalArrayValue(long index);
public abstract void Clear();
public abstract long Count { get; }
public abstract long ArrayCount { get; }
}
public interface IInfoColumn<T> : IEnumerable<T>
{
T this[double index] { get; set; }
InfoTable Table { get; set; }
double Add(T item);
}
[DataContract(Name = "InfoColumn{0}")]
[KnownType(typeof(InfoColumn<double>))]
[KnownType(typeof(InfoColumn<String>))]
[KnownType(typeof(InfoColumn<bool>))]
[KnownType(typeof(InfoColumn<DateTime>))]
public class InfoColumn<T> : IInfoColumnBase, IInfoColumn<T>
{
long counter = 0;
[DataMember(Order = 1)]
public IList<T> Values { get; set; }
//[DataMember(Order = 2)]
bool isSingleVal = false;
//[DataMember(Order=3)]
public override string Name { get; set; }
//[DataMember(Order=4)]
public override InfoColumnDataType DataType { get; set; }
public InfoTable Table { get; set; }
public override long Count
{
get
{
return this.Table.Count;
}
}
public override long ArrayCount
{
get { return this.Values.Count; }
}
public InfoColumn()
{
}
public InfoColumn(string name,InfoTable table)
{
this.Values = new List<T>();
this.Name = name;
this.Table = table;
}
public override void Clear()
{
this.Values = new List<T>();
}
public override void Remove(long index)
{
int newindex = (int)index;
this.Values.RemoveAt(newindex);
}
public override void CopyValues(long start, long end, IInfoColumnBase destCol, long startIndex)
{
InfoColumn<T> typeCol = destCol as InfoColumn<T>;
for (long ctr = start; ctr <= end; ctr++)
{
typeCol.SetValue(startIndex, this.Values[(int)ctr]);
startIndex++;
}
}
public override void Insert(long rows)
{
if (this.IsSingleValue == true) return;
for (int ctr = 0; ctr < rows; ctr++)
{
this.Values.Add(default(T));
}
}
public T this[double a]
{
get
{
if (a >= this.Count) throw new IndexOutOfRangeException();
long index = (long)a;
if (this.Table.IsFreezed == false)
index = this.Table.CheckData(a);
if (this.isSingleVal == true)
return this.Values[0];
else
return this.Values[(int)index];
}
set
{
if (a >= this.Count) throw new IndexOutOfRangeException();
long index = (long)a;
if (this.Table.IsFreezed == false)
index = this.Table.CheckData(a);
if (this.isSingleVal == true)
this.Values[0] = value;
else
this.Values[(int)index] = value;
}
}
public override long GetIndex(object val)
{
T item = (T)val;
return this.Values.IndexOf(item);
}
public override void SetValue(long index, object val)
{
if (val is InfoSetLink)
this.Values[(int)index] = (T)val;
else
this.Values[(int)index] = (T)Convert.ChangeType(val, typeof(T));
}
public override object GetValue(long index)
{
return this[index];
}
public override object GetInternalArrayValue(long index)
{
return this.Values[(int)index];
}
//[DataMember(Order=5)]
public override bool IsSingleValue
{
get { return isSingleVal; }
set
{
if (isSingleVal == true)
{
this.Values = new List<T>(1);
}
}
}
public override long Insert()
{
if (this.IsSingleValue == true) return -1;
this.Values.Add(default(T));
return this.Values.Count - 1;
}
public double Add(T item)
{
this.Values.Add(item);
return this.Values.Count - 1;
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return new InfoColumnEnumerator<T>(this);
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return new InfoColumnEnumerator<T>(this);
}
#endregion
}
InfoColumn<T> has a public Add(T) and implements IEnumerable<T> (via IInfoColumn<T>).
There is wider support for list-like types in v2, and it may be that it is trying to interpret the above as a list. Which indeed, it does look a lot like! I will try to take a look to see if this general scenario can be detected and avoided, but it is an edge case (since it is indeed very list-esque).
There is an existing IgnoreListBehaviour switch, however when validating this for the model shown above, it seems that for this specific scenario the "you can't do that" fires before the code that disables list handling; I have changed this in the source, and this will be included in the next release. Basically, you can address this by adding:
[ProtoContract(IgnoreListHandling = true)]
to the impacted type (InfoColumn<T>), with the next build. Which will be shortly, as soon as I've completed validation etc.