Search Generic List<T> for Id property - c#

I have a Generic List which can contain different class objects. Al these classes have the same base class which has a Id property.
I would like to do a search for the Id in the List and extract the first occourence, something like this
T result = myGenericList.Where(i => i.Id == id).FirstOrDefault()
However I cannot get to the properties, I only get as far as: i => i.
EDIT:
I'm asked to provide the code for the myGenericList. Here it goes:
public static IEnumerable<T> Initialize()
{
List<T> allCalls = new List<T>();
var phoneCalls = new PhoneCall[]
{
new PhoneCall{Id = 1, Date= DateTime.Now.AddDays(-1), DurationSec = 60, Phone = "", Region = new Country { From = "", To = ""}},
...
};
foreach (PhoneCall s in phoneCalls)
{
allCalls.Add(s as T);
}
var dataCalls = new DataCall[]
{
new DataCall{Id = 6, WebData = 5120, Phone = "", Region = new Country { From = "", To = ""}},
...
};
foreach (DataCall s in dataCalls)
{
allCalls.Add(s as T);
}
var smsCalls = new SmsCall[]
{
new SmsCall{Id = 6, SmsData = 512, Phone = "", Region = new Country { From = "", To = ""}},
...
};
foreach (SmsCall s in smsCalls)
{
allCalls.Add(s as T);
}
return allCalls;
Regarding the T that is a class which I have defined at the top of my Repository.cs class
public class Repository<T> : IRepository<T> where T : class

What you should do is define what T is and not cast the items to T. As the compiler has no way to know what T is it does not know it has the Id property. Use a generic constraint to do so:
public static IEnumerable<T> Initialize<T where T : BaseClass>()
{
var allCalls = new List<T>();
allCalls.Add(new PhoneCall { /* Details */};
return allCalls;
}
After question update - move the constraint from being one of the function to being one of the class
Also you can use the FirstOrDefault overload that gets a predicate:
var result = myGenericList.FirstOrDefault(i => i.Id == id);

Have you tried casting to the base class?
myGenericList
.Cast<BaseClass>()
.FirstOrDedault(i => i.Id === id)
Or alternatively
myGenericList.FirstOrDefault(i => (i as BaseClass).Id === id)

Related

Issues in Xunit.Assert.Collection - C#

I have a Class Library, it contains the following Model and Method
Model:
public class Employee {
public int EmpId { get; set; }
public string Name { get; set; }
}
Method:
public class EmployeeService {
public List<Employee> GetEmployee() {
return new List<Employee>() {
new Employee() { EmpId = 1, Name = "John" },
new Employee() { EmpId = 2, Name = "Albert John" },
new Employee() { EmpId = 3, Name = "Emma" },
}.Where(m => m.Name.Contains("John")).ToList();
}
}
I have a Test Method
[TestMethod()]
public void GetEmployeeTest() {
EmployeeService obj = new EmployeeService();
var result = obj.GetEmployee();
Xunit.Assert.Collection<Employee>(result, m => Xunit.Assert.Contains("John",m.Name));
}
I got an Exception message
Assert.Collection() Failure
Collection: [Employee { EmpId = 1, Name = "John" }, Employee { EmpId = 2, Name = "Albert John" }]
Expected item count: 1
Actual item count: 2
My requirement is to check all the items.Name should contain the sub string "John". Kindly assist me how to check using Xunit.Assert.Collection
It appears that Assert.Collection only uses each element inspector once. So, for your test, the following works:
If the sequence result has exactly two elements:
[Fact]
public void GetEmployeeTest()
{
EmployeeService obj = new EmployeeService();
var result = obj.GetEmployee();
Assert.Collection(result, item => Assert.Contains("John", item.Name),
item => Assert.Contains("John", item.Name));
}
If there are many elements, changing the Assert to
Assert.All(result, item => Assert.Contains("John", item.Name));
should give you the result you are expecting.
This is an expansion on Ayb4btu's answer for those who aren't interested in the order of items in the collection.
The following method is based on the original XUnit implementation, and will allow you to test using a very similar interface:
public static class TestExpect
{
public static void CollectionContainsOnlyExpectedElements<T>(IEnumerable<T> collectionToTest, params Func<T, bool>[] inspectors)
{
int expectedLength = inspectors.Length;
T[] actual = collectionToTest.ToArray();
int actualLength = actual.Length;
if (actualLength != expectedLength)
throw new CollectionException(collectionToTest, expectedLength, actualLength);
List<Func<T, bool>> allInspectors = new List<Func<T, bool>>(inspectors);
int index = -1;
foreach (T elementToTest in actual)
{
try
{
index++;
Func<T, bool> elementInspectorToRemove = null;
foreach (Func<T, bool> elementInspector in allInspectors)
{
if (elementInspector.Invoke(elementToTest))
{
elementInspectorToRemove = elementInspector;
break;
}
}
if (elementInspectorToRemove != null)
allInspectors.Remove(elementInspectorToRemove);
else
throw new CollectionException(collectionToTest, expectedLength, actualLength, index);
}
catch (Exception ex)
{
throw new CollectionException(collectionToTest, expectedLength, actualLength, index, ex);
}
}
}
}
The difference here is that for a collection
string[] collectionToTest = { "Bob", "Kate" };
Both the following lines will not produce a CollectionException
TestExpect.CollectionContainsOnlyExpectedElements(collectionToTest, x => x.Equals("Bob"), x => x.Equals("Kate"));
TestExpect.CollectionContainsOnlyExpectedElements(collectionToTest, x => x.Equals("Kate"), x => x.Equals("Bob"));
Whereas using Assert.Collection - Only the first of the above two lines will work as the collection of inspectors is evaluated in order.
There is a potential performance impact using this method, but if you are only testing fairly small sized collections (as you probably will be in a unit test), you will never notice the difference.
Use Assert.Contains(result, item => item.Name == "John");

LINQ query to exclude overridden values?

I have some tables as follows:
ImageSettingOverrides
TechniqueSettings
SettingKeyValues
From TechniqueSettings table:
BAZ-FOO setting (SettingKeyId: 7) is an override to the BAZ-Default (SettingKeyId: 4) setting.
Example of expected return from query grouped by Override value:
I want to compile a list of SettingKeyValues given technique BAZ and override FOO that excludes the overridden BAZ-Default settings and includes non-overridden BAZ-Default settings.
I currently have a LINQ query that groups setting-key values based on Default/Override values:
var techniqueSettings = _dataRepository.All<TechniqueSetting>()
.Where(s => s.Technique.Equals(TechniqueName, StringComparison.InvariantCultureIgnoreCase))
// group settings by: e.g. Default | FOO
.GroupBy(s => s.Override);
From there I determine if the user is querying for just the defaults or the defaults with overrides:
var techniqueGroups = techniqueSettings.ToArray();
if (OverridesName.Equals("Default", StringComparison.InvariantCultureIgnoreCase)) {
// get the default group and return as queryable
techniqueSettings = techniqueGroups
.Where(grp => grp.Key.Equals("Default", StringComparison.InvariantCultureIgnoreCase))
.AsQueryable();
} else {
// get the overrides group - IGrouping<string, TechniqueSetting>
var overridesGroup = techniqueGroups
.Where(grp => !grp.Key.Equals("Default", StringComparison.InvariantCultureIgnoreCase))
.First();
var defaultGroup = techniqueGroups
.Where(grp => grp.Key.Equals("Default", StringComparison.InvariantCultureIgnoreCase))
// we know what is in the overrides, so exlude them from being selected here
// how to exlude overridden defaults???
.First();
}
In addition, I can't help but think there must be an easier - less clumsy - LINQ query using JOIN (maybe ???).
NOTE: Using EntityFramework 6.x
__
UPDATE:
I found Aggregate seems to simplify somewhat but still required an anonymous method.
var defaultGroup = techniqueGroups
.Where(grp => grp.Key.Equals("Default", StringComparison.InvariantCultureIgnoreCase))
.Aggregate(overridesGroup,
(overrides, defaults) => {
var settings = new List<TechniqueSetting>();
foreach (var setting in defaults) {
if (overrides.Any(o => o.SettingKey.Key == setting.SettingKey.Key)) {
continue;
}
settings.Add(setting);
}
return settings.GroupBy(s => s.Override).First();
},
setting => setting);
I haven't tried the Join yet per comment by #MarkoDevcic.
Can Except be used in this query?
Revised Answer
With values
int myImageId = 1;
string myOverride = "FOO";
string myTechnique = "BAZ";
results =
ImageId Override Value
1 FOO 1000
With Values
int myImageId = 1;
string myOverride = "Default";
string myTechnique = "BAZ";
results =
ImageId Override Value
1 Default 10000
void Main()
{
// Create Tables and Initialize values
// ***********************************
var techniqueSettings = new List<TechniqueSettings>();
techniqueSettings.Add(new TechniqueSettings { Id = 1, Override = "Default", SettingKeyId = 3, Technique="BAZ"});
techniqueSettings.Add(new TechniqueSettings { Id = 2, Override = "Default", SettingKeyId = 4, Technique="BAZ"});
techniqueSettings.Add(new TechniqueSettings { Id = 3, Override = "FOO", SettingKeyId = 7, Technique="BAZ"});
techniqueSettings.Add(new TechniqueSettings { Id = 4, Override = "FOO", SettingKeyId = 8, Technique="BAZ"});
var imageSettingOverrides = new List<ImageSettingOverrides>();
imageSettingOverrides.Add(new ImageSettingOverrides {SettingId = 1, ImageId=1, Override=null } );
imageSettingOverrides.Add(new ImageSettingOverrides {SettingId = 2, ImageId=1, Override="FOO" } );
imageSettingOverrides.Add(new ImageSettingOverrides {SettingId = 3, ImageId=1, Override="FOO" } );
var settingKeyValues = new List<SettingKeyValues>();
settingKeyValues.Add(new SettingKeyValues {Id = 4, Setting="Wait", Value=1000 } );
settingKeyValues.Add(new SettingKeyValues {Id = 7, Setting="Wait", Value=10000 } );
int myImageId = 1;
string myOverride = "FOO";
string myTechnique = "BAZ";
var results = from iso in imageSettingOverrides
join ts in techniqueSettings on iso.SettingId equals ts.Id
join skv in settingKeyValues on ts.SettingKeyId equals skv.Id
where iso.ImageId == myImageId &&
//iso.Override.Equals(myOverride,StringComparison.InvariantCultureIgnoreCase) &&
ts.Override.Equals(myOverride,StringComparison.InvariantCultureIgnoreCase) &&
ts.Technique.Equals(myTechnique, StringComparison.InvariantCultureIgnoreCase)
select new {
ImageId = iso.ImageId,
Override = ts.Override,
Value = skv.Value
};
results.Dump();
}
// Define other methods and classes here
public class ImageSettingOverrides
{
public int SettingId {get; set;}
public int ImageId {get; set;}
public string Override {get; set;}
}
public class TechniqueSettings
{
public int Id {get; set;}
public string Override {get; set;}
public int SettingKeyId {get; set;}
public string Technique { get; set;}
}
public class SettingKeyValues
{
public int Id {get; set;}
public String Setting {get; set;}
public int Value {get; set;}
}
I assume you expect each of SettingKeyValues in the result will have unique Setting value (it doesn't make sense to have two 'Wait' records with different numbers against them).
Here is query:
var result =
(
from ts in techniqueSettings
// For only selected technique
where ts.Technique.Equals("BAZ", StringComparison.InvariantCultureIgnoreCase)
// Join with SettingsKeyValues
join skv in settingKeyValues on ts.SettingKeyId equals skv.Id
// intermediate object
let item = new { ts, skv }
// Group by SettingKeyValues.Setting to have only one 'Wait' in output
group item by item.skv.Setting into itemGroup
// Order items inside each group accordingly to Override - non-Default take precedence
let firstSetting = itemGroup.OrderBy(i => i.ts.Override.Equals("Default") ? 1 : 0).First()
// Return only SettingKeyValue
select firstSetting.skv
)
.ToList();
I'm going to make some assumptions.
That if there is an ImageSettingOverrides the override also has to match the override passed in AKA FOO (that's the where iSettingsOverrides => iSettingsOverrides.Override == OverridesName in the join clause
You only want a distinct list of SettingKeyValues
TechniqueSetting.Id is the key and ImageSettingOverride.TechniqueSettingsId is the foreign key and that's how they are related
SettingKeyValue.Id is the key and TechniqueSetting.SettingKeyId is the foreign key and that's how they are related.
You don't have navigation properties and I have to do the join.
If I understand your classes and how they are related this should give you a list of SettingKeyValues. Since everything stays IQueryable it should execute on the server.
//I'm assuming these are your variables for each IQueryable
IQueryable<TechniqueSetting> techniqueSettings;
IQueryable<ImageSettingOverride> imageSettingOverrides;
IQueryable<SettingKeyValue> settingKeyValues;
var OverridesName = "FOO";
var TechniqueName = "BAZ";
IQueryable<TechniqueSetting> tSettings;
if (OverridesName.Equals("Default", StringComparison.InvariantCultureIgnoreCase))
{
// Get a list of TechniqueSettings that have this name and are default
tSettings = techniqueSettings.Where(t => t.Override == OverridesName && t.Technique == TechniqueName);
}
else
{
// Get a list of TechniqueSettings Id that are overridden
// The ImageSettingOverrides have the same override
var overriddenIDs = techniqueSettings.Where(t => t.Technique == TechniqueName && t.Override == "Default")
.Join(
imageSettingOverrides.Where(
iSettingsOverrides =>
iSettingsOverrides.Override == OverridesName),
tSetting => tSetting.SettingKeyId,
iSettings => iSettings.TechniqueSettingsId,
(tSetting, iSettingsOverrides) => tSetting.Id);
// Get a list of techniqueSettings that match the override and TechniqueName but are not part of the overriden IDs
tSettings =
techniqueSettings.Where(
t =>
t.Technique == TechniqueName && !overriddenIDs.Contains(t.Id) &&
(t.Override == OverridesName || t.Override == "Default"));
}
// From expected results seems you just want techniqueSettings and that's what would be in techniqueSettings right now.
// If you want a list of SettingKeyValues (which is what is stated in the questions we just need to join them in now)
var settings = tSettings.Join(settingKeyValues, tSetting => tSetting.SettingKeyId,
sKeyValues => sKeyValues.Id, (tSetting, sKeyValues) => sKeyValues)
.Distinct();
I found Aggregate seems to simplify somewhat but still required an anonymous method.
var defaultGroup = techniqueGroups
.Where(grp => grp.Key.Equals("Default", StringComparison.InvariantCultureIgnoreCase))
.Aggregate(overridesGroup,
(overrides, defaults) => {
var settings = new List<TechniqueSetting>();
foreach (var setting in defaults) {
if (overrides.Any(o => o.SettingKey.Key == setting.SettingKey.Key)) {
continue;
}
settings.Add(setting);
}
return settings.GroupBy(s => s.Override).First();
},
setting => setting);
Update:
I came up with a couple of extension methods that allows for exclusion of items and comparisons and replacements:
internal static IEnumerable<TSource> Exclude<TSource>(this IEnumerable<TSource> Source, Func<TSource, bool> Selector) {
foreach (var item in Source) {
if (!Selector(item)) {
yield return item;
}
}
}
internal static IEnumerable<TResult> ReplaceWith<TSource1, TSource2, TResult>(this IEnumerable<TSource1> Source1,
Func<TSource1, TResult> Source1Result,
IEnumerable<TSource2> Source2,
Func<TSource1, IEnumerable<TSource2>, TResult> Selector) {
foreach (var item in Source1) {
var replaceWith = Selector(item, Source2);
if (replaceWith == null) {
yield return Source1Result(item);
continue;
}
yield return replaceWith;
}
}
Exclude is fairly straightforward. For ReplaceWith usage:
var settings = _repository.Settings
.ReplaceWith(s => s.SettingKeyValue,
_repository.SettingOverrides.Where(o => o.OverrideName == overrideName),
(s, overrides) => overrides.Where(o => o.Setting == s)
.Select(o => o.SettingKeyValueOverride)
.FirstOrDefault())
.ToList();

C# Refactoring the same action with different details using design patterns

I try to find the way for refactoring my code but no idea how to do this.
For example, we have several classes
class A {
string name;
int year;
}
class B {
long id;
string code;
DateTime currentTime;
}
class C {
string lastname;
DateTime currentDate;
}
And latter I need to return list of objects of these classes List, List, List and convert them into Object[][].
For every conversion I do the same
private Object[][] converAListToObjectArray(List<A> dataList)
{
long countRecords = dataList.Count();
const int countProperty = 2;
var arrayRes = new object[countRecords][];
for (int i = 0; i < countRecords; i++)
{
var arrayObjProperty = new object[countProperty];
arrayObjProperty[0] = dataList[i].Name;
arrayObjProperty[1] = dataList[i].Year;
arrayRes[i] = arrayObjProperty;
}
return arrayRes;
}
private Object[][] converBListToObjectArray(List<B> dataList)
{
long countRecords = dataList.Count();
const int countProperty = 3;
var arrayRes = new object[countRecords][];
for (int i = 0; i < countRecords; i++)
{
var arrayObjProperty = new object[countProperty];
arrayObjProperty[0] = dataList[i].Id;
arrayObjProperty[1] = dataList[i].Code;
arrayObjProperty[2] = dataList[i].CurrentTime;
arrayRes[i] = arrayObjProperty;
}
return arrayRes;
}
Is it possible separate this convertion using some design pattern?
You could write a generic function which uses reflection to get each object's field names and values. You'd need to decide whether it should work for public or private fields. The example below grabs both the public and the private fields:
static object[][] ConvertToObjectArray<T>(IList<T> objects)
{
var fields = (from fieldInfo in typeof(T).GetFields(
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
orderby fieldInfo.Name
select fieldInfo).ToArray();
object[][] table = new object[objects.Count][];
for (int i = 0; i < table.Length; i++)
{
table[i] = (from fieldInfo in fields
select fieldInfo.GetValue(objects[i])).ToArray();
}
return table;
}
You could use an action and do something like this: -
class Program
{
// Your new function, (doesn't have to be static; just did it for the demo)
// If you really really want to return object[][] still,
// You'll need to pass an index to foo as well
private static List<IList<object>> convert<T>(IList<T> dataList, Action<IList<object>, T> foo)
{
var arrayRes = new List<IList<object>>();
foreach (var item in dataList)
{
var arrayObjProperty = new List<object>();
foo(arrayObjProperty, item);
arrayRes.Add(arrayObjProperty);
}
return arrayRes;
}
// The rest is just calling the function with two examples
static void Main(string[] args)
{
var bar = new List<A>();
bar.Add(new A() { name = "qux", year = 2013 });
var objects1 = convert(bar, (a, b) =>
{
a.Add(b.name);
a.Add(b.year);
});
var baz = new List<B>();
baz.Add(new B() { code = "qux", id = 2013 });
var objects2 = convert(baz, (a, b) =>
{
a.Add(b.code);
a.Add(b.id);
});
}
}
You can just copy this into your IDE to have a play and see how it works. Basically, this uses generics and then an action to allow you to do the only part that differs each time in a lambda that is passed to the method.
You could simplify and use something like this - e.g. for B...
List<B> list = new List<B>
{
new B{ id = 1, code = "", currentTime = DateTime.Now},
new B{ id = 1, code = "", currentTime = DateTime.Now},
new B{ id = 1, code = "", currentTime = DateTime.Now},
};
var array = list.Select(x => new object[] { x.id, x.code, x.currentTime }).ToArray();
Your Object[][] structure is a little unusual to my eye. Did you know there's a Cast method for lists?
You can do the following:
List<A> foo = new List<A> ();
// initialize foo here
var bar = foo.Cast<Object>().ToArray();
Which will get you a an array of objects with your field names intact. If you absolutely need to have the fields as array elements (why do you want this) you could add a ToObject method to each of your classes:
class A
{
public string name;
public int year;
public Object[] ToObject()
{
return new Object[] {name, year};
}
}
List<A> foo = new List<A>
{
new A{name="reacher",year=2013},
new A{name="Ray",year=2013}
};
var bux = foo.Select(a => a.ToObject()).ToArray() ;

how to get values from var-source with linq

I have a Dictionary:
Dictionary<int, Type> AllDrillTypes = new Dictionary<int, Type>()
{
{13,typeof(TCHEMISTRY)},
{14,typeof(TDRILLSPAN)}
};
where TCHEMISTRY and TDRILLSPAN are classes. Then I want to get rows from one of this classes like this:
Type T = AllDrillTypes[13];
var LC = Activator.CreateInstance( typeof(List<>).MakeGenericType( T ) );
MethodInfo M = T.GetMethod("FindAll", BindingFlags.Public | BindingFlags.Static, null, new Type[] { }, null);
LC = M.Invoke(null, new object[] { });
All this code works correctly. After that I need to get some rows like this:
var LingLC = from obj in LC where obj.RunID == 1001 select obj;
But this line causes error:
"Could not find an implementation of the query pattern for source type
'object'. 'Where' not found."
What's wrong with this code line?
Even if you can't change the class definitions, you can avoid using reflection:
// Getter dictionary rather than type dictionary.
Dictionary<int, Func<IEnumerable<object>>> DrillTypeGetters =
new Dictionary<int, Func<IEnumerable<object>>>()
{
{ 13, () => TCHEMISTRY.FindAll().Cast<object>() },
{ 14, () => TDRILLSPAN.FindAll().Cast<object>() }
};
Dictionary<int, Func<object, int>> IDGetters =
new Dictionary<int, Func<object, int>>()
{
{ 13, o => ((TCHEMISTRY)o).RunID },
{ 14, o => ((TDRILLSPAN)o).RunID }
};
IEnumerable<object> LC = DrillTypeGetters[13]();
IEnumerable<object> LingLC =
from obj in LC
where IDGetters[13](obj) == 1001
select obj;
Or you could even just switch on 13/14 and run a completely different method per type.
if (choice == 13)
IEnumerable<TCHEMISTRY> LingLC =
TCHEMISTRY.FindAll().Where(tc => tc.RunID == 1001);
else if (choice == 14)
IEnumerable<TDRILLSPAN> LingLC =
TDRILLSPAN.FindAll().Where(td => td.RunID == 1001);
Basically, if the two classes don't share any common hierarchy, you can't write any common code to deal with them. If they have lots of similar properties, you can use getters as in my first example to provide a way to get the similar properties whatever type of class you're dealing with. If they don't even have similar properties, don't try to write shared code.
Maybe you could rewrite your code to something like this, .... to get a more type-safe solution (without using reflection).
void Main()
{
var driller1 = new DrillerWhichYouCannotChange1();
var driller2 = new DrillerWhichYouCannotChange2();
var allDrillTypes = new Dictionary<int, IList<IDriller>>()
{
{ 13, new List<IDriller>() { new DrillerWhichYouCannotChange1Adapter(driller1) } },
{ 14, new List<IDriller>() { new DrillerWhichYouCannotChange2Adapter(driller2) } },
};
Console.WriteLine(allDrillTypes[13][0].SomeCommonProperty); // prints 123
Console.WriteLine(allDrillTypes[14][0].SomeCommonProperty); // prints 456
}
interface IDriller
{
int SomeCommonProperty { get; }
}
class DrillerWhichYouCannotChange1Adapter : IDriller
{
private DrillerWhichYouCannotChange1 inner;
public DrillerWhichYouCannotChange1Adapter(DrillerWhichYouCannotChange1 inner)
{
this.inner = inner;
}
public int SomeCommonProperty { get { return this.inner.PropertyX; } }
}
class DrillerWhichYouCannotChange2Adapter : IDriller
{
private DrillerWhichYouCannotChange2 inner;
public DrillerWhichYouCannotChange2Adapter(DrillerWhichYouCannotChange2 inner)
{
this.inner = inner;
}
public int SomeCommonProperty { get { return this.inner.PropertyY; } }
}
class DrillerWhichYouCannotChange1
{
public int PropertyX { get { return 123; } }
}
class DrillerWhichYouCannotChange2
{
public int PropertyY { get { return 456; } }
}
EDIT: If you cannot change the driller classes, you could use the adapter-pattern to create one adapter per driller, which implements IDiller.
you need to cast LC to the to the return type of the FindAll method. Something on the lines of:
var genericList = ((List<TChemistry>) LC);
var LingLC = from obj in genericList where obj.RunID == 1001 select obj;
This is assuming that FindAll returns a collection of TChemistry.
--edit
If you do not know at runtime if the type will be TChemistry or TDrillspan, then you will have to write an if/else of switch statement to cast to the correct type. I would rather have TChemistry and TDrillSpan extend an abstract class or an interface, and you can just cast to List, and you will always have RunId property.
public abstract class TAbstract
{
public abstract int RunId {get; set;}
}
public class TChemistry : TAbstract
{
public override int RunId {get; set;}
}
public class TDrillSpan : TAbstract
{
public override int RunId {get; set;}
}
Type T = AllDrillTypes[13] as TAbstract;
var LC = Activator.CreateInstance( typeof(List<>).MakeGenericType( T ) );
MethodInfo M = T.GetMethod("FindAll", BindingFlags.Public | BindingFlags.Static, null, new Type[] { }, null);
LC = M.Invoke(null, new object[] { });
var genericList = ((List<TAbstract>) LC);
var LingLC = from obj in genericList where obj.RunID == 1001 select obj;
If you cannot change the declaration of classes, then you are only left with ugly if else:
var typeInfo = LC.GetType();
IEnumerable<T> genericList;
if (typeInfo == typeof(IEnumerable<TChemistry>)
{
genericList = (List<TChemistry>) LC;
)
else if (typeInfo == typeof(IEnumerable<TDrillSpan>)
{
genericList = (List<TDrillSpan>) LC;
}
var LingLC = from obj in genericList where obj.RunID == 1001 select obj;
try
IEnumerable returnedObjects = (IEnumerable)M.Invoke(null, new object[] { }) as IEnumerable;
then iterate through your ienumerable
foreach (object report in returnedObjects)
{
// Use reflection to read properties or add to a new List<object> if you
// need an ICollection<object>
}
instead of :
LC = M.Invoke(null, new object[] { });

How to update an object in a List<> in C#

I have a List<> of custom objects.
I need to find an object in this list by some property which is unique and update another property of this object.
What is the quickest way to do it?
Using Linq to find the object you can do:
var obj = myList.FirstOrDefault(x => x.MyProperty == myValue);
if (obj != null) obj.OtherProperty = newValue;
But in this case you might want to save the List into a Dictionary and use this instead:
// ... define after getting the List/Enumerable/whatever
var dict = myList.ToDictionary(x => x.MyProperty);
// ... somewhere in code
MyObject found;
if (dict.TryGetValue(myValue, out found)) found.OtherProperty = newValue;
Just to add to CKoenig's response. His answer will work as long as the class you're dealing with is a reference type (like a class). If the custom object were a struct, this is a value type, and the results of .FirstOrDefault will give you a local copy of that, which will mean it won't persist back to the collection, as this example shows:
struct MyStruct
{
public int TheValue { get; set; }
}
Test code:
List<MyStruct> coll = new List<MyStruct> {
new MyStruct {TheValue = 10},
new MyStruct {TheValue = 1},
new MyStruct {TheValue = 145},
};
var found = coll.FirstOrDefault(c => c.TheValue == 1);
found.TheValue = 12;
foreach (var myStruct in coll)
{
Console.WriteLine(myStruct.TheValue);
}
Console.ReadLine();
The output is 10,1,145
Change the struct to a class and the output is 10,12,145
HTH
or without linq
foreach(MyObject obj in myList)
{
if(obj.prop == someValue)
{
obj.otherProp = newValue;
break;
}
}
Can also try.
_lstProductDetail.Where(S => S.ProductID == "")
.Select(S => { S.ProductPcs = "Update Value" ; return S; }).ToList();
You can do somthing like :
if (product != null) {
var products = Repository.Products;
var indexOf = products.IndexOf(products.Find(p => p.Id == product.Id));
Repository.Products[indexOf] = product;
// or
Repository.Products[indexOf].prop = product.prop;
}
var itemIndex = listObject.FindIndex(x => x == SomeSpecialCondition());
var item = listObject.ElementAt(itemIndex);
item.SomePropYouWantToChange = "yourNewValue";
This was a new discovery today - after having learned the class/struct reference lesson!
You can use Linq and "Single" if you know the item will be found, because Single returns a variable...
myList.Single(x => x.MyProperty == myValue).OtherProperty = newValue;
I found a way of doing it in one Line of code unsing LINQ:
yourList.Where(yourObject => yourObject.property == "yourSearchProperty").Select(yourObject => { yourObject.secondProperty = "yourNewProperty"; return yourObject; }).ToList();
var index = yourList.FindIndex(x => x.yourProperty == externalProperty);
if (index > -1)
{
yourList[index] = yourNewObject;
}
yourlist now has the updated object inside of it.
It is also worth mentioning that what Matt Roberts said applies to structs inside classes.
So even when you have a class (which will pass by reference, theoretically), a property which is a list of structs inside it will pass by value when you try to find it and change a value inside a given struct (on the list).
For instance (using the same code as Matt proposed):
ParentClass instance = new ParentClass();
var found = instance.ListofStruct.FirstOrDefault(c => c.TheValue == 1);
found.TheValue = 12;
foreach (var myStruct in instance.ListofStruct)
{
Console.WriteLine(myStruct.TheValue);
}
Console.ReadLine();
public class ParentClass
{
public List<MyStruct> ListofStruct { get; set; }
public struct MyStruct
{
public int TheValue { get; set; }
}
public ParentClass()
{
ListofStruct = new List<MyStruct>()
{
new MyStruct {TheValue = 10},
new MyStruct {TheValue = 1},
new MyStruct {TheValue = 145}
};
}
}
Will output: 10, 1, 145
Whereas changing the struct (inside the ParentClass) to a class, like this:
public class ParentClass
{
public List<MySubClass> ListofClass { get; set; }
public class MySubClass
{
public int TheValue { get; set; }
}
public ParentClass()
{
ListofClass = new List<MySubClass>()
{
new MySubClass {TheValue = 10},
new MySubClass {TheValue = 1},
new MySubClass {TheValue = 145}
};
}
}
Will output: 10, 12, 145
//Find whether the element present in the existing list
if (myList.Any(x => x.key == "apple"))
{
//Get that Item
var item = myList.FirstOrDefault(x => x.key == ol."apple");
//update that item
item.Qty = "your new value";
}

Categories