Is it possible to access a collection item by a string reference instead of an index offset for DotLiquid?
public class MyItem
{
public string Name;
public object Value;
public MyItem(string Name, object Value)
{
this.Name = Name;
this.Value = Value;
}
}
public class MyCollection : List<MyItem>
{
public MyCollection()
{
this.Add(new MyItem("Rows", 10));
this.Add(new MyItem("Cols", 20));
}
public MyItem this[string name]
{
get
{
return this.Find(m => m.Name == name);
}
}
}
So in normal c# if I create an instance of the MyCollection class I can access the elements like this
MyCollection col =new MyCollection();
col[1] or col["Rows"]
Can I access via the name element col["Rows"] in a DotLiquid template? If so how do I implement this?
Yes, it is possible. First, define a Drop class like this:
public class MyCollectionDrop : Drop
{
private readonly MyCollection _items;
public MyCollectionDrop(MyCollection items)
{
_items = items;
}
public override object BeforeMethod(string method)
{
return _items[method];
}
}
Then, in the code that renders your template, add an instance of it to the context:
template.Render(Hash.FromAnonymousObject(new { my_items = new MyCollectionDrop(myCollection) }));
Finally, access it like this in your template:
{{ my_items.rows.name }}
"rows" will be passed as-is to MyCollectionDrop.BeforeMethod as the method argument.
Note that you'll also need to make MyItem inherit from Drop, to be able to access its properties. Or write a MyItemDrop class like this:
public class MyItemDrop : Drop
{
private readonly MyItem _item;
public MyItemDrop(MyItem item)
{
_item = item;
}
public string Name
{
get { return _item.Name; }
}
public string Value
{
get { return _item.Value; }
}
}
and then change MyCollectionDrop.BeforeMethod to this:
public override object BeforeMethod(string method)
{
return new MyItemDrop(_items[method]);
}
Related
I have code as follows
public class Field<T>
{
private T_fieldValue;
public T FieldValue
{
get
{
return _fieldValue;
}
set
{
_fieldValue = value;
}
}
}
public class Container
{
private ObservableCollection<Field> _fieldValues;
public ObservableCollection<Field> FieldValues
{
get { return _fieldValues; }
set { _fieldValues = value; }
}
}
How can I pass my Type parameter in container class as well ??
i.e I am looking for something like
private ObservableCollection<Field<T>> _fieldValues;
EDIT:
I made changes to the class as follows
public class Container<T>
{
public string MyName { get; set; }
private ObservableCollection<Field<T>> _fieldValues;
public ObservableCollection<Field<T>> FieldValues
{
get { return _fieldValues; }
set { _fieldValues = value;}
}
}
}
Now in my Main class how can i call something like
new ObservableCollection<Container>() { MyName ="Kyle",
new Container() { FieldValues = new ObservableCollection<Field<int>>()
{new Field<int>(){FieldValue=10}}; // I am not sure if this syntax is correct
You will need to make the Container class generic as well:
class Container<T> : BindableBase
{
private ObservableCollection<Field<T>> _fieldValues;
}
If you always have a specific T parameter type in mind for Container, then you can of course just hard-code that:
class Container : BindableBase
{
private ObservableCollection<Field<SomeSpecificType>> _fieldValues;
}
To create an instance of your generic Container<T> class, you use the exact same syntax you'd use for any generic type. For example, if T is int, you would use:
new Container<int>()
Then you can assign the FieldValues property as you indicate in your updated example. I.e.:
FieldValues = new ObservableCollection<Field<int>>()
I'm using a bindingsource to fill a form from Nhibernate list:
public class Customer{
public string Name { get; set;}
public IList<Order> Orders { get; set;}
}
bindingSourceCustomer.DataSource = session.Query<Customer>().ToList();
bindingSourceOrder.DataSource = bindingSourceCustomer;
bindingSourceOrder.DataMember = "Orders";
now when I call
bindingSourceOrder.AddNew();
an exception is thrown:
The value "System.Object" is not of type "Model.Order" and cannot be
used in this generic collection.
Now I changed the first line to:
bindingSourceCustomer.DataSource = session.Query<Customer>().Select(customer =>
{
customer.Orders = customer.Orders.ToList();
return customer;
})
.ToList();
it worked, the reason why, is because Nhibernate uses the PersistentBag as an implementation of IList, which apparently doesn't work well with binding source (As far as I see).
Any suggestion either how to make Nhibernate return List class, or how to solve the problem with binding source?
That's because the BindingSource is unable to discover the list type:
NHibernate's persistent bag has not the ITypedList interface nor the Indexer Property public.
You need to replace NHibernate CollectionTypeFactory with a custom one, before adding mappings.
I attach my implementation:
PersistentGenericBag:
public class EnhancedPersistentGenericBag<T> : PersistentGenericBag<T> , ITypedList
{
public EnhancedPersistentGenericBag(ISessionImplementor session, ICollection<T> coll) : base(session, coll) { }
public EnhancedPersistentGenericBag(ISessionImplementor session) : base(session) { }
public EnhancedPersistentGenericBag() { }
public new T this[int index]
{
get
{
return (T)base[index];
}
set
{
base[index] = value;
}
}
public string GetListName(PropertyDescriptor[] listAccessors) { return GetType().Name; }
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
return TypeDescriptor.GetProperties(typeof(T));
}
}
CollectionTypeFactory:
public class EnhancedCollectionTypeFactory : DefaultCollectionTypeFactory
{
public override CollectionType Bag<T>(string role, string propertyRef, bool embedded)
{
return new EnhancedGenericBagType<T>(role, propertyRef);
}
}
GenericBagType:
public class EnhancedGenericBagType<T> : BagType
{
public EnhancedGenericBagType(string role, string propertyRef) :
base(role, propertyRef, false) { }
public override IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister, object key)
{
return new EnhancedPersistentGenericBag<T>(session);
}
public override IPersistentCollection Wrap(ISessionImplementor session, object collection)
{
return new EnhancedPersistentGenericBag<T>(session, (ICollection<T>)collection);
}
public override Type ReturnedClass
{
get
{
return typeof(ICollection<T>);
}
}
protected override void Add(object collection, object element)
{
((ICollection<T>)collection).Add((T)element);
}
protected override void Clear(object collection)
{
((ICollection<T>)collection).Clear();
}
public override object Instantiate(int anticipatedSize)
{
if (anticipatedSize > 0)
return new List<T>(anticipatedSize + 1);
else
return new List<T>();
}
}
How to override default CollectionTypeFactory:
Configuration cfg = new Configuration();
cfg.CollectionTypeFactory<EnhancedCollectionTypeFactory>();
This is a Two (2) Part Question about Generics
I've got to create several similar classes to model similarly designed database tables.
All tables contain an ID int and a Text nvarchar(50) field. One or two may contain a few other fields.
I rarely use generics, but I see examples of it on here quite frequently. This is my largest attempt to create a generic class that is used in another generic class.
My basic construct is as follows, and I will point out with a comment what does not work and the error message Visual Studio 2010 is displaying:
public class IdText {
public IdText(int id, string text) {
ID = id;
Text = text;
}
public int ID { get; private set; }
public string Text { get; private set; }
}
public class TCollection<T> : IEnumerable<T> where T : IdText {
private List<T> list;
public TCollection() {
list = new List<T>();
}
public void Add(int id, string text) {
foreach (var item in list) {
if (item.ID == id) {
return;
}
}
list.Add(new T(id, text)); // STOP HERE
// Cannot create an instance of the variable type 'T'
// because it does not have the new() constraint
}
public T this[int index] {
get {
if ((-1 < 0) && (index < list.Count)) {
return list[index];
}
return null;
}
}
public T Pull(int id) {
foreach (var item in list) {
if (item.ID == id) {
return item;
}
}
return null;
}
public T Pull(string status) {
foreach (var item in list) {
if (item.Text == status) {
return item;
}
}
return null;
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator() {
foreach (var item in list) yield return item;
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return list.GetEnumerator();
}
#endregion
}
Visual Studio's IntelliSence wants me to add list.Add(T item), but I need to create this first.
I have attempted to re-write the offending line list.Add(new T(id, text)); as list.Add(new IdText(id, text));, but then I am reprimanded with the message "cannot convert from IdText to T".
How exactly do I get around this?
Next: When I go in to actually create a version of this IdText class later, I am not sure how exactly I can use this new class in the TCollection class I have designed for it.
For example, given this derived class:
public class ManufacturedPart : IdText {
public ManufacturedPart(int id, string partNum, string description)
: base(int id, string partNum) {
Description = description;
}
public string Description { get; private set; }
}
...would I need to also derive a special version of TCollection to accompany it, like so?
public class ManufacturedParts<T> : IEnumerable<T> where T : ManufacturedPart {
// OK, now I'm lost! Surely this can't be right!
}
1) You could use the new() constraint, make your properties public and add a parameterless constructor:
public class IdText
{
public IdText()
{
}
public IdText(int id, string text)
{
ID = id;
Text = text;
}
public int ID { get; set; }
public string Text { get; set; }
}
public class TCollection<T> : IEnumerable<T> where T : IdText, new()
{
private List<T> list;
public TCollection()
{
list = new List<T>();
}
public void Add(int id, string text)
{
foreach (var item in list)
{
if (item.ID == id)
{
return;
}
}
list.Add(new T { ID = id, Text = text });
}
}
2) You have multiple options:
If you want your collection to store any IdText (ManufacturedPart or anything else that derived from IdText):
TCollection<IdText> ss = new TCollection<IdText>();
The above, for now, can only store IdText as you instantiate objects in the Add(int, string) method, but if you provide a Add(T object) method, it could store any IdText instance.
If you want your collection to only contains ManufacturedParts:
public class ManufacturedParts<T> : TCollection<T> where T : ManufacturedPart, new()
{
// Provide here some specific implementation related to ManufacturedParts
// if you want. For example, a TotalPrice property if ManufacturedPart
// has a Price property.
}
TCollection<ManufacturedPart> ss2 = new ManufacturedParts<ManufacturedPart>();
or even simpler, if your collection doesn't provide any additional method depending on the type of the stored objects:
TCollection<ManufacturedPart> ss2 = new TCollection<ManufacturedPart>();
Even simpler, if your goal is to only store objects, a custom collection isn't needed:
List<IdText> ss2 = new List<IdText>(); // Uses the built-in generic List<T> type
About the first question: c# doesn't support constructors with parameters as a generic constrain. Something you can do is replace it with
(T)Activator.CreateInstance(typeof(T),new object[]{id,text});
By the other hand... you don't actually know how the constructors of the derived class will look like, so you can't ensure they will have that constructor.
About the second question, you can do this:
var collection = new TCollection<ManufacturedPart>();
in the same way List works.
Hope it helps.
If your collection class will be responsible for instantiating elements of its collected type, then you probably don't want to be using either the new() constraint or Activator.CreateInstance() -- as Jon Skeet has blogged, both of these exhibit poor performance.
It sounds like what you actually want is a provider delegate, like so:
public class MyCollection<T> : IEnumerable<T> where T : IdText {
private readonly List<T> list;
private readonly Func<int, string, T> provider;
public MyCollection(Func<int, string, T> provider) {
this.list = new List<T>();
this.provider = provider;
}
public void Add(int id, string text) {
list.Add(provider(id, text));
}
}
And then you'd use it like:
var collection = new MyCollection((id, text) => new ManufacturedPart(id, text));
You can think of this as passing the specific constructor you want to use as an argument into the class, which it then uses to construct instances as needed.
And you don't need to create a separate subclass for MyCollection<ManufacturedPart> -- just use the generic class directly.
I'm creating a table from a dynamically created IBindingList using
class TableBuilder
{
private Type m_TableType;
// ... create and define m_TableType here
public IBindingList CreateTable()
{
return Activator.CreateInstance(m_TableType) as IBindingList;
}
}
class DynamicTable : IBindingList
{
private IBindingList m_theList;
private TableBuilder m_tableBuilder;
public DynamicTable(TableBuilder tableBuilder)
{
m_tableBuilder = tableBuilder;
m_theList = tableBuilder.CreateTable();
}
public void LoadData()
{
// ...
}
}
I would like to promote the IBindingList functionality of m_theList to the level of the class so I can make calls like
var myTable = new DynamicTable(someTableBuilder);
int count = myTable.Count;
myTable.LoadData();
count = myTable.Count;
How can I get all the m_theList public members to be members of DynamicTable. I can not derive DynamicTable from m_TableType since it is only known at run time.
-Max
You will have to do it as old subclassing, implement the interface and in each method call the corresponding method in m_theList:
//methods
public void AddIndex(PropertyDescriptor property)
{
m_theList.AddIndex(property);
}
public object AddNew()
{
return m_theList.AddNew();
}
//properties
public bool AllowEdit
{
get { return m_theList.AllowEdit; }
}
....
//for events you can use add/remove syntax
public event ListChangedEventHandler ListChanged
{
add { m_theList.ListChanged += value; }
remove { m_theList.ListChanged -= value; }
}
....
//indexer...
public object this[int index]
{
get
{
return m_theList[index];
}
set
{
m_theList[index] = value;
}
}
Consider this programs
public class ItemManager
{
private ItemFetcher itemFetcher;
public ItemManager(ItemFetcher _itemFetcher)
{
itemFetcher = _itemFetcher;
}
public List<Item> GetItemsFomTable()
{
List<Item> itemsList = new List<Item>();
Item item;
DataTable resultDataTable = itemFetcher.GetItemsFromDB();
foreach (DataRow row in resultDataTable.Rows)
{
item = new Item();
// set item's name property
// set item's price property
itemsList.Add(item);
}
return itemsList;
}
}
public class Item
{
string name;
public string Name
{
get { return name; }
set { name = value; }
}
double price;
public double Price
{
get { return price; }
set { price = value; }
}
}
public class ItemFetcher
{
public virtual DataTable GetItemsFromDB()
{
// DoSomething and fetch records in DataTable and return
return new DataTable();
}
}
I want to test itemFetcher.GetItemsFromDB method is called once in the method "GetItemsFomTable()" of the class ItemManager. Here is the Test
[TestFixture]
public class ItemManagerTester
{
[SetUp]
public void Init()
{
}
[Test]
public void TestForGetItemsFomTable()
{
var mockItemFetcher = new Mock<ItemFetcher>();
var itemManager = new ItemManager(mockItemFetcher.Object);
mockItemFetcher.Setup(x => x.GetItemsFromDB());
itemManager.GetItemsFomTable();
mockItemFetcher.VerifyAll();
}
}
As you may see there is List initialised inside the method under test
List<Item> itemsList = new List<Item>();
I get this Exception raised:
TestCase 'MockingSample.ItemManagerTester.TestForGetItemsFomTable'
failed: System.NullReferenceException : Object reference not set to an instance of an object.
ItemManager.cs(26,0): at MockingSample.ItemManager.GetItemsFomTable()
ItemManager.cs(77,0): at MockingSample.ItemManagerTester.TestForGetItemsFomTable()
What should I do with the List ? How and where can I mock this if needed ?
Since you haven't specified a return value, the mock item fetcher returns null, so the attempt to access resultDataTable.Rows throws.
To fix the error, tell Moq what you want it to return when you configure the expectation:
mockItemFetcher.Setup(x => x.GetItemsFromDB()).Returns(new DataTable());