Odd behavior when adding to an Observable Collection - c#

I have an interesting problem. I have a Class Person:
public class Person
{
public string Name { get; set; }
public int? Score { get; set; }
public int NbrOfWins { get; set; }
public int NbrOfLosses { get; set; }
public int HighScore { get; set; }
}
I create an Observable collection:
ObservableCollection<Person> test = new ObservableCollection<Person>();
I have an extension method to add to the observable collection:
public static void myFillTest<T>(this ObservableCollection<T> value1, T value2, int nbr)
{
for (int x = 0; x < nbr; x++)
{
value1.Add(value2);
}
}
I add 5 items to the collection like this:
test.myFillTest(new Person { Name = "None" }, 5);
If I change the name in one instance:
test[2].Name = "John";
All of the items in the collection change, as if they were all pointing to the same thing.
Any reason why this would be? By the way, this works for T of type int, and string, but not for a typeof class.

This is because the class Person is a reference type while the integer is value type. When you add the same int 5 times, it is copied, when you add person 5 times, its one instance added to 5 different indexes. You can read about reference types here http://msdn.microsoft.com/en-us/library/490f96s2.aspx . You need to copy your object of type person if you want it to work as expected.
You can change your code to the following in order to always create new objects:
public static void MyFillTest<T>(this ObservableCollection<T> value1, T value2, int nbr)
{
for (int x = 0; x < nbr; x++)
{
if (typeof(T).IsValueType)
{
value1.Add(value2);
}
else
{
if (value2 is ICloneable)
{
ICloneable cloneable = (ICloneable)value2;
value1.Add((T)cloneable.Clone());
}
}
}
}
public class Person : ICloneable
{
public string Name { get; set; }
public int? Score { get; set; }
public int NbrOfWins { get; set; }
public int NbrOfLosses { get; set; }
public int HighScore { get; set; }
#region ICloneable Members
public object Clone()
{
return new Person
{
Name = this.Name,
Score = this.Score,
NbrOfWins = this.NbrOfWins,
NbrOfLosses = this.NbrOfLosses,
HighScore = this.HighScore
};
}
#endregion
}

new Person { Name = "None" } is only instantiated once, when you call your method. So they all reference to the same object.

It's quite simple - you are adding value2 to the collection nbr times. Or rather, when adding an object (as you are in your example) you are adding a reference to the same object nbr times. So if you change one, you change them all.

This extension method will do what you are trying to do:
public static void myFillTest<T>(this ObservableCollection<T> value1, Action<T> init, int nbr) where T: new()
{
for (int x = 0; x < nbr; x++)
{
var value2 = new T();
init(value2);
value1.Add(value2);
}
}
Call it like this:
test.myFillTest(p => p.Name = "None", 5);

The Person object is instantiated once and its reference is used 5 times. You could overcome this by using a memberwise clone to create shallow copies of your original object.

Related

C# generic method for returning maxId

I would like to have a method that could perform this code on other objects I have (For example: prize, person, team and so on.), so I don't have to write the same code multiple times and just put let's say GetMaxId(List< Person > persons, Person person).
Each of my objects has an Id property.
I'm using this so when i save to text file through user input in my winform application, so i can generate the id that be 1 bigger based on the current number of eg Persons in the text file.
public static int GetMaxId(List<Prize> prizes, Prize prize)
{
int maxId = 1;
if (prizes.Count > 0)
maxId = prizes.Max(p => p.Id) + 1;
prize.Id = maxId;
return prize.Id;
}
So, what i would like is in each of the classes, for example i want to return the id for the person when creating a new person but i don't want to modify the code from taking in parameters for Prize and having to change it to Person.
i would like a method that takes generic parameters so when i call it in Person class, i can just pass the (list persons, Person person).
I don't know which type to pass in the original method so that i can reuse it in other classes.
Well, I think what you want is a generic function to retrieve the next id of a collection. You can try using generics.
Something like this:
public static int GetNextId<T>(List<T> items, Func<T,int> selector)
{
if (items.Count < 1)
return 1;
return items.Max(selector)+1;
}
And you use the function like this:
public class Person
{
public int PersonID { get; set; }
}
public static void Test()
{
var persons = new List<Person>()
{
new Person() {PersonID=1 },
new Person() {PersonID=2 },
};
var nextId = GetNextId(persons, i => i.PersonID);//returns 3
}
Here's a simple example using an interface, where all your things would implement this IHaveId interface to ensure they have this id property. The getMaxId function is generic and requires only that your list be a list of things with id properties which implement the IHaveId interface.
You can see this working at https://dotnetfiddle.net/pnX7Ph.
public interface IHaveId {
int id { get; }
}
public class Thing1 : IHaveId {
private int _id;
public Thing1(int id) {
this._id = id;
}
int IHaveId.id {
get { return this._id; }
}
}
public class Thing2 : IHaveId {
private int _id;
public Thing2(int id) {
this._id = id;
}
int IHaveId.id {
get { return this._id; }
}
}
public static int getMaxId<T>(List<T> list) where T : IHaveId {
return list.Max(i => i.id);
}
public static void Main()
{
List<IHaveId> things = new List<IHaveId>();
for (var i=0; i<5; i++) {
things.Add(new Thing1(i));
}
for (var i=10; i<15; i++) {
things.Add(new Thing2(i));
}
Console.WriteLine("Max id is " + getMaxId(things));
}

Create Generic Method that accepts a List with custom object types and access similar properties

I am creating a search algorithm that searches through a list with custom objects I have created. They share similar properties, but I can not seem to "implicitly" access these properties..? An example:
public class Exit{
int ID {get;set;}
}
public class Room{
int ID {get;set;}
}
static void Main(string[] args){
List<Exit> exits = new List<Exit>();
List<Room> rooms = new List<Room>();
// added numerous instances of objects to both lists
int getExitID = _GetIDFromList(exits, 2); //example
int getRoomID = _GetIDFromList(rooms, 7); //example
}
private int _GetIDFromList<T>(List<T> list, int indexOfList){
return list[indexOfList].ID; // this gives me error it can't find ID
}
Is this possible? What do I need to modify to what I have to do this??
Thank you.
You can create interface for it:
public interface IId
{
int ID { get; set; }
}
public class Exit : IId
{
int ID { get; set; }
}
public class Room : IId
{
int ID { get; set; }
}
private int _GetIDFromList<T>(List<T> list, int indexOfList) where T : IId
{
return list[indexOfList].ID;
}
Or you can use Reflection and Expression for it:
public static Expression<Func<T, P>> GetGetter<T, P>(string propName)
{
var parameter = Expression.Parameter(typeof(T));
var property = Expression.PropertyOrField(parameter, propName);
return Expression.Lambda<Func<T, P>>(property, parameter);
}
Retrives int Id from type T and returns it:
private static int _GetIDFromList<T>(List<T> list, int indexOfList)
{
var lambda = GetGetter<T, int>("Id").Compile();
return lambda(list[indexOfList]);
}
I'm little rewrote your Room class:
public class Room
{
public int ID { get; set; }
}
And usage:
Console.WriteLine(_GetIDFromList(new List<Room> { new Room { ID = 5 } }, 0));

Add log to destination object when using AutoMapper

Is it possible to add a log to the destination object, when using AutoMapper?
If I have two objects:
class A
{
int PropertyOne
int PropertyTwo
int PropertyThree
List<string> Log
}
class B
{
int PropertyOne
int PropertyTwo
}
When mapping from B to A, I'd like to automatically have a log entry added to A.Log for every property that is changed.
E.g. if during a mapping operation, PropertyOne = 3 on both objects, but A.PropertyTwo = 1 and B.PropertyTwo = 2, I'd like to have a log entry added to A.Log - preferably something like "PropertyTwo changed from 1 to 2"
Instead of an auto-property, create a property with a custom setter in which you add an entry to the log list.
Example console application:
public static class Program
{
public class A
{
private int _PropertyOne;
private int _PropertyTwo;
private int _PropertyThree;
public int PropertyOne
{
get { return _PropertyOne; }
set
{
if (value == _PropertyOne)
return;
Log.Add(string.Format("PropertyOne changing value from {0} to {1}", _PropertyOne, value));
_PropertyOne = value;
}
}
public int PropertyTwo
{
get { return _PropertyTwo; }
set
{
if (value == _PropertyTwo)
return;
Log.Add(string.Format("PropertyOne changing value from {0} to {1}", _PropertyTwo, value));
_PropertyTwo = value;
}
}
public int PropertyThree
{
get { return _PropertyThree; }
set
{
if (value == _PropertyThree)
return;
Log.Add(string.Format("PropertyOne changing value from {0} to {1}", _PropertyThree, value));
_PropertyThree = value;
}
}
public List<string> Log { get; private set; }
public A()
{
Log = new List<string>();
}
}
public class B
{
public int PropertyOne { get; set; }
public int PropertyTwo { get; set; }
}
public static void Main(string[] args)
{
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<A, B>().ReverseMap();
});
var b = new B() {PropertyOne = 1, PropertyTwo = 2};
var a = AutoMapper.Mapper.Map<B, A>(b);
a.Log.ForEach(s => Console.WriteLine(s));
}
}
This will output:
PropertyOne changing value from 0 to 1
PropertyTwo changing value from 0 to 2
You could implement a custom type converter that would work with a marker interface called IPropertyLogger. Any subtype of that could be explicitly used by AutoMapper.
The type converter could use reflection and perform the diff-like operation you are requesting before calling default AutoMapper behavior. This would work for all tagged types and you would not have to code each object specifically for the case.
Your reflection based diff code would handle all of the logging you require keeping you objects clean from implementation code.

Value cannot be null. Parameter name: source when Adding object to IEnumerable list

I am building a simple MVC application to perform CRUD applications. I have two simple model classes.
public class MoniModel
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public int CategoryId { get; set; }
public string CategoryName { get; set; }
}
public class MoniGridModel
{
public IEnumerable<MoniModel> MoniDetails { get; set; }
}
In my controller, I am trying to do the following:
public ActionResult MoniDetails()
{
MoniModel mim = new MoniModel();
MoniGridModel migm = new MoniGridModel();
mim.CategoryId = 1;
mim.CategoryName = "a";
mim.ProductId = 1;
mim.ProductName = "b";
migm.MoniDetails.ToList().Add(mim);
return View(migm);
}
When migm.MoniDetails.ToList().Add(mim); is executed, it gives the mentioned error. I am not able to figure out why this is happening. I am assigning value to each member of mim object. below is the error detail, not sure if that will help, though.
You've never instantiated MoniDetails (it's value is null when you create the MoniGridModel):
public ActionResult MoniDetails()
{
MoniModel mim = new MoniModel();
MoniGridModel migm = new MoniGridModel();
mim.CategoryId = 1;
mim.CategoryName = "a";
mim.ProductId = 1;
mim.ProductName = "b";
var details = new List<MoniModel>();
details.Add(mim);
migm.MoniDetails = details;
return View(migm);
}
As #David's answer points out, it's probably best to let the MoniGridModel class handle the IEnumerable<MoniModel> instead though.
In addition to the options he presents, you could create a private member that backs the IEnumerable<MoniModel> and expose it through the getter you currently have:
public class MoniGridModel
{
private List<MoniModel> moniDetails;
public MoniGridModel()
{
this.moniDetails = new List<MoniModel>();
}
public IEnumerable<MoniModel> MoniDetails
{
get { return this.moniDetails; }
}
public void AddDetail(moniDetail detail)
{
this.moniDetails.Add(detail);
}
}
And then call AddDetail from your controller action:
public ActionResult MoniDetails()
{
MoniModel mim = new MoniModel();
MoniGridModel migm = new MoniGridModel();
mim.CategoryId = 1;
mim.CategoryName = "a";
mim.ProductId = 1;
mim.ProductName = "b";
migm.AddDetail(mim);
return View(migm);
}
MoniDetails is null because it was never instantiated. This should generally be done when constructing the object:
public class MoniGridModel
{
public IEnumerable<MoniModel> MoniDetails { get; set; }
public MoniGridModel()
{
MoniDetails = new List<MoniModel>();
}
}
That way consuming code doesn't need to worry about instantiating it. The responsibility of maintaining the state of the object belongs encapsulated within the object.
Additionally, this doesn't do what you think it does:
migm.MoniDetails.ToList().Add(mim);
ToList() returns an enumerated list of the IEnumerable<>. But you want to add to the enumerable itself. Given this use, you probably want the property to be an IList<> in the first place so it can support the Add() operation:
public class MoniGridModel
{
public IList<MoniModel> MoniDetails { get; set; }
public MoniGridModel()
{
MoniDetails = new List<MoniModel>();
}
}
Then you can add to it:
migm.MoniDetails.Add(mim);

How to store multiple database columns into an array with Linq2Sql

I have to work with multiple SQL Server tables that generally look like this:
int id_this, int id_that, ..., double Value1, double Value2, ..., double Value96
I know this sucks, but I can't change it. What I want to do now is to define some class like
public class Foo
{
public int Id_This { get; set; }
public int Id_That { get; set; }
...
public double Value[];
}
The Value-Array being a property of course, but I think you get the idea.
The question is, how to get the 96 columns into the array as painlessly as possible.
I could do that with a plain SqlDataReader, since DataRow allows indexed access, but I wonder if I could declare some attributes or write some minimum amount of code to use the class directly with LINQ2SQL.
As a minimum, I would like to do
dataContext.ExecuteQuery<Foo>("SELECT * FROM Foo");
Ooh, that is... nice? The DataContext methods always expect an entity type; there is no ExecuteReader, which is a pain (but understandable, as it wants to behave as an ORM). To be honest, I would be tempted to use ADO.NET for the guts against this table, but if you have mapped the wide table to the DataContext you should be able to use either regular C# or reflection.
Since the number doesn't change, unless you have multiple tables I'd just bite the bullet and write some ugly code:
Foo foo = new Foo { Id_This = obj.Id_This, Id_That = obj.Id_That,
Values = new double[] {obj.Value1, obj.Value2, ... } };
If you have multiple tables... reflection, perhaps optimised via Expression:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
class FooUgly
{
public int IdThis { get; set; }
public int IdThat { get; set; }
public double Value1 { get; set; }
public double Value2 { get; set; }
public double Value3 { get; set; }
}
class Foo
{
public int IdThis { get; set; }
public int IdThat { get; set; }
public double[] Values { get; set; }
public Foo() { }
internal Foo(FooUgly ugly)
{
IdThis = ugly.IdThis;
IdThat = ugly.IdThat;
Values = extractor(ugly);
}
// re-use this!!!
static readonly Func<FooUgly, double[]> extractor =
ValueExtractor<FooUgly, double>.Create("Value", 1, 3);
}
static class Program
{
static void Main()
{
FooUgly ugly = new FooUgly { IdThis = 1, IdThat = 2, Value1 = 3, Value2 = 4, Value3 = 5 };
Foo foo = new Foo(ugly);
}
}
static class ValueExtractor<TFrom,TValue>
{
public static Func<TFrom, TValue[]> Create(string memberPrefix, int start, int end)
{
if(end < start) throw new ArgumentOutOfRangeException();
ParameterExpression param = Expression.Parameter(typeof(TFrom), "source");
List<Expression> vals = new List<Expression>();
for(int i = start ; i <= end ; i++) {
vals.Add(Expression.PropertyOrField(param, memberPrefix + i));
}
Expression arr = Expression.NewArrayInit(typeof(TValue), vals);
return Expression.Lambda<Func<TFrom, TValue[]>>(arr, param).Compile();
}
}

Categories