I'm having some trouble getting an indexer to work with a List of objects. I'd like to be able to access each object in the list by it's name (a string) rather than it's index. So, I'd like to overload the [] operator to achieve this. So far I can't get the overload to show up in intellisense and it does not work at the moment. What I have so far is:
A singleton object of a class called MapInfo:
MapInfo mi = MapInfo.Instance;
In the MapInfo class I have a list of Table objects:
List<Table> tables;
public List<Table> Tables
{
get { return tables; }
}
And finally in the Tables class I have tried this:
class Table : IEnumerable<Table>
{
public Table this[string tableName]
{
get
{
foreach (Table table in this)
{
if (table.name == tableName)
return table;
}
return null;
}
}
I'd like to be able to access my Table objects using:
mi.Tables["SomeTableName"]
This is the first time I've tried this so I'm not quite sure where I'm going wrong.
you can use approach like this
public class MapInfo {
private readonly TableCollection _tables = new TableCollection();
public TableCollection Tables {
get { return _tables; }
}
}
public class TableCollection : List<Table> {
public Table this[string name] {
get { return this.FirstOrDefault(t => t.Name == name); /*using System.Linq;*/ }
}
}
public class Table {
public string Name { get; set; }
}
or simply use a dictionary (Dictionary<string, Table>) as Danaldo suggested. but not BOTH of them as he'd coded =))
IMO, the right way is not to use indexers like these, cause as I see there can be more than one table with 'unique' name in your collections. I'd recommend using a simple List of Tables and a method like GetTableWithName to make things clearer because indexers usually give a (false) hope that that your data is unique
OR you can replace a call to FirstOrDefault with SingleOrDefault which will internally ensure that if there is an element with the 'name' no other element has the same 'name'
You are overloading the indexer on the Table class. You got something wrong in your code structure. The Table class is an IEnumerable<Table>, so a Table contains other Tables.
So, a List<Table> will contain Table instances which in turn also contain Table instances.
With mi.Tables["SomeTableName"] you are trying to access the indexer of List, not the indexer of Table.
Why not define an indexer in MapInfo?
Use a dictionary
Private Dictionary<String, Table> tables;
public Dictionary<String, Table> Tables
{
get { return tables; }
}
Then:
class Table : IEnumerable<Table>
{
public Table this[string tableName]
{
get
{
Table table;
if(mi.tables.TryGetValue(tableName, out table))
{
return table;
}
else
{
return null;
}
}
}
}
Your implementation of MapInfo/Table isn't quite right to allow you to do what you want.
Your property of Tables in MapInfo can't be a list of tables, it has to be of type Table. Then your Table class should fully implement the IEnumerable interface.
Quick example written in LinqPad:
class Table : IEnumerable<Table>
{
public Table()
{
_tables = new Dictionary<string, Table>();
}
public string name { get; set; }
Dictionary<string, Table> _tables;
public void Add(string tname)
{
_tables.Add(tname, new Table { name = tname });
}
public Table this[string tableName]
{
get
{
Table table;
if (_tables.TryGetValue(tableName, out table))
return table;
return null;
}
}
public IEnumerator<Table> GetEnumerator()
{
return _tables.Values.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
void Main()
{
MapInfo mi = new MapInfo();
mi.Table["foo"].Dump();
}
class MapInfo
{
public MapInfo()
{
Tables = new Table();
test();
}
private void test()
{
Tables.Add("foo");
Tables.Add("bar");
Tables.Add("soom");
}
public Table Tables { get; set; }
}
Related
I have a Product table in my DB. Also, I have Brand and Category tables in my DB which are not related to each other. I want to relate these. In the form UI when I click the one of the Categories, should come the Brands which they have products in the related category.
I tried this way to do this. First, I get my products by categoryID with GetList method then I get these products' brands and I added these brands to pblist list(Brand type). However, some products have the same brands and pblist have repeated brand names. I tried to fix this with contains method but it does not work. Also, I have the same problem in the other part which I try to remove brands not included in pblist from blist(all brands' list). I tried removing item from blist by taking its index with this code: blist.RemoveAt(blist.IndexOf(item)); but this one also not working.It returns -1. But item is in the blist.
public class BrandVM : BaseVM
{
public int ProductCount { get; set; }
}
public class BaseVM
{
public int ID { get; set; }
public string Name { get; set; }
public override string ToString()
{
return this.Name;
}
public class BrandService : ServiceBase, IBrandService
{
public List<BrandVM> GetList(int Count)
{
try
{
var result = GetQuery();
result = Count > 0 ? result.Take(Count) : result;
return result.ToList();
}
catch (Exception ex)
{
return null;
}
}
public List<BrandVM> GetListByCatID(int pCatID)
{
var plist = productService.GetListByCatID(pCatID);
List<BrandVM> pblist = new List<BrandVM>();
foreach (var item in plist)
{
if (!pblist.Contains(item.Brand))
{
pblist.Add(item.Brand);
}
};
var blist = GetList(0);
var blistBackup = GetList(0);
foreach (BrandVM item in blistBackup)
{
if (!pblist.Contains(item))
{
blist.Remove(item);
}
};
return blist;
}
These are my classes related to Brand. In BrandService I shared the filled methods there are more methods to fill.
This is method is in my ProductService:
I use that method to pull product list by CategoryID (plist)
public List<ProductVM> GetListByCatID(int EntityID)
{
try
{
var result = GetQuery().Where(x => x.Category.ID==EntityID);
return result.ToList();
}
catch (Exception ex)
{
return null;
}
}
This GetQuery method for ProductService, in other services there are some differences but there are similar
private IQueryable<ProductVM> GetQuery()
{
return from p in DB.Products
select new ProductVM
{
ID = p.ProductID,
Name = p.ProductName,
UnitPrice = (decimal)p.UnitPrice,
Category =p.CategoryID==null?null:new CategoryVM()
{
ID = (int)p.CategoryID,
Name = p.Category.CategoryName
},
Brand = p.BrandID == null ? null :
new BrandVM
{
ID=(int)p.BrandID,
Name=p.Brand.BrandName,
}
};
}
Entity framework will translate Linq queries into SQL statements, which means that Equals (and GetHashCode) will not be used for comparison of database objects. However, if you're comparing local instances of these objects, then these methods will be used for comparisons.
The default Equals does a reference comparison to determine equality, which literally means that two instances of a type are only considered equal if they both refer to the exact same object in memory.
Instead, we want to use the ID property for equality comparison, which means we need to override the Equals (and GetHashCode) methods for the class.
Here's an example of how you could do this:
public class BaseVM
{
public int ID { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Name;
}
public override bool Equals(object obj)
{
return obj is BaseVM &&
((BaseVM) obj).ID == ID;
}
public override int GetHashCode()
{
return ID;
}
}
Alternatively, if you don't want to modify the class (which I would recommend since it solves this problem everywhere), you can modify your code to filter out any brands that have the same id (or name):
foreach (var item in plist)
{
// Note: you could potentially use 'Name' instead of 'Id'
if (!pblist.Any(productBrand => productBrand.Id == item.Brand.Id))
{
pblist.Add(item.Brand);
}
}
Since you don't ensure that two different instances for a same brand are not equal,
in the sense that ´.Equals(object other)´ returns true,
the ´.Contains´ method as no way to identify them.
I think you'ĺl solve you issue by overriding .Equals in you Brand class.
I have table in a sqlite database in a Xamarin Forms app with about 12000 items stored in it. When I try to return only one column with the following query, I got a list with all 12000 values but the entries of the list are null.
Code for the class:
public class BaseModel
{
private int? _PrimaryKey;
private string _Code;
private string _Name;
[PrimaryKey, Required, NotNull]
public int? PrimaryKey { get { return _PrimaryKey; } set { _PrimaryKey = value; } }
public string Code { get { return _Code; } set { _Code = value; } }
public string Name { get { return _Name; } set { _Name = value; } }
}
I to query the table with the following line:
internal T GetInsertItem<T>() where T : BaseModel, new()
{
T item = new T();
//...
List<int?> items = new List<int?>(_Conn.Table<T>().Select(ac => ac.PrimaryKey));
//...
return item;
}
So the result is not what i expected. I got the full list with 12000 items but every item is NULL. Why?
Image from the debugging ...
If I use another query like the following one, it works as I expected.
List<T> items = new List<T>(_Conn.Table<T>().Where<T>(ac => ac.PrimaryKey > 1000));
It seems there is an error in the implementation of select in SQLite-net, so calling _Conn.Table<T>().ToList() and then the select .Select(...) helps.
sqlite-net issues
I have the following:
public class Broadcast {
public int NumUsersToMessage { get; set; }
public int NumMessagesQueued { get; set; }
public string DbUsersMessaged { get; set; }
public int NumMessagesSent {
get {
return UsersMessaged.Count();
}
}
public List<int> UsersMessaged {
get {
return DbUsersMessaged == null ? new List<int>() : DbUsersMessaged.Split(',').Select(Int32.Parse).ToList();
}
set {
DbUsersMessaged = value != null ? String.Join(",", value) : null;
}
}
}
My goal here is to only ever access DbUsersMessaged through UsersMessaged. I'm attempting to do broadcast.UsersMessaged.Add(2), however since this is not an assignment, I can't get the property to behave as I like. Instead, I have to do this:
tempList = broadcast.UsersMessaged();
tempList.Add(2);
broadcast.UsersMessaged = tempList;
db.SaveChanges();
Which is obviously unwieldy. I'm considering making an AddReassign extension method but I want to know - what's the standard practice here for supporting Lists of primitive types? It looks like even with the extension method, my best shot looks like this:
broadcast.UsersMessaged = broadcast.UsersMessaged.AddReassign(2) // yuck!
Before anyone asks - we've intentionally denormalized this for performance reasons.
If you don't care about performance, you can create own list:
public class MyList : IList<int>
{
private List<int> underlyingList;
private Broadcast entity;
public MyList(Broadcast entity)
{
this.entity = entity;
this.underlyingList = entity.DbUsersMessaged?.Split(",") ?? new List<int>();
}
public void Add(int i)
{
this.underlyingList.Add(i);
this.entity.DbUsersMessaged = String.Join(",", underylingList);
}
// other interface memebers impl
}
Then
MyList list;
public IList<int> UsersMessaged {
get {
return myList ?? (myList = new MyList(this));
}
}
Of course it is only sample.
I recommend you to have a look at this: Entity Framework 5 - Looking for Central Point to Execute Custom Code after Entity is Loaded from Database
And then convert from string to list, and then use Saving Changes event to convert back into the string construction when saving.
Then, for performance, maybe you want to use byte[] rather than a string for storing the data in the database.
I have a Linq to Entities query and I want to select some specific columns and store the new object into a pre-defined object. However, I'm getting the error
<object> does not contain a constructor that takes 0 arguments.
Not sure what is wrong here...
Also not sure if this is the best way or if using anonymous type is better instead of creating a payroll object.
Linq Query
public Payroll GetTestCasePayroll(decimal testScenarioID) //not sure if object is correct return
{
Payroll instance = (from o in DbContext.UI_OnDemandCheckHeader
where o.TestScenarioID == testScenarioID
select new Payroll(o.PayEntityCode, o.PayrollYear, o.PayrollNumber)).First();
//{ PayEntityCode = , PayrollYear = o.PayrollYear, PayrollNumber = o.PayrollNumber }).First();
return instance;
}
Payroll object
class Payroll
{
private string _payEntityCode;
private decimal _payrollYear;
private string _payrollNumber;
public Payroll(string payEntityCode, decimal payrollYear, string payrollNumber)
{
PayEntityCode = payEntityCode;
PayrollYear = payrollYear;
PayrollNumber = payrollNumber;
}
public decimal PayrollYear
{
get { return _payrollYear; }
set { _payrollYear = value; }
}
public string PayEntityCode
{
get { return _payEntityCode; }
set { _payEntityCode = value; }
}
public string PayrollNumber
{
get { return _payrollNumber; }
set { _payrollNumber = value; }
}
Your Payroll class needs a constructor that takes no parameters e.g.
Public Payroll() { }
Linq works by creating an empty instance of the output class and then using the setters on each of the properties. It does not use anything but an empty constructor.
I have an in-memory "table" that might looks something like this:
Favorite# Name Profession
--------- ---------- ------------------
3 Names.Adam Profession.Baker
9 Names.Bob Profession.Teacher
7 Names.Carl Profession.Coder
7 Names.Dave Profession.Miner
5 Names.Fred Profession.Teacher
And what I want to do, is do quick and efficient lookups, using any of the 3 fields.
In other words, I want:
myTable[3] and myTable[Names.Adam] and myTable[Professions.Baker] to all return {3,Names.Adam,Profession.Baker}
myTable[Profession.Teacher] to return both {9,Names.Bob,Profession.Teacher} and {5,Names.Fred,Profession.Teacher}.
The table is built during runtime, according to the actions of the user, and cannot be stored in a database since it is used in sections in which database connectivity cannot be guaranteed.
Right now, I "simply" (hah!) store this using 3 uber-Dictionaries, each keyed using one of the columns (FavoriteNumber, Name, Profession), and each value in the uber-Dictionaries holding 2 Dictionaries which are themselves keyed with each of the remaining columns (so the values in the "Name" uber-dictionary are of the type Dictionary<FavoriteNumber,Profession[]> and Dictionary<Profession, FavoriteNumber[]>
This requires 2 lookups in 2 Dictionaries, and another traverse of an array (which usually holds 1 or 2 elements.)
Can anyone suggest a better way to do this? I don't mind spending extra memory, since the table is likely to be small (no more than 20 entries) but I'm willing to sacrifice a little CPU to make it more readily maintainable code...
Not really however using a dictionary, but if you create a collection of classes like this
class Person {
public int FavoriteNumber;
public string Name;
public string Profession;
}
you can use LINQ to search the collections.
IList<Person> people = /* my collection */;
var selectedPeople = people.Where(p => p.FavoriteNumber = 3);
var selectedPeople2 = people.Where(p => p.Name == "Bob");
var selectedPeople3 = people.Where(p => p.Profession = "Teacher");
or if you prefer the normal LINQ syntax
var selectedPeople4 = from p in people
where p.Name == "Bob"
select p;
Each of these selectedPeople variables will be typed as IEnumerable<Person> and you can use a loop to search through them.
For 20 rows, just use linear scanning - it will be the most efficient in every way.
For larger sets; hzere's an approach using LINQ's ToLookup and delayed indexing:
public enum Profession {
Baker, Teacher, Coder, Miner
}
public class Record {
public int FavoriteNumber {get;set;}
public string Name {get;set;}
public Profession Profession {get;set;}
}
class Table : Collection<Record>
{
protected void Rebuild()
{
indexName = null;
indexNumber = null;
indexProfession = null;
}
protected override void ClearItems()
{
base.ClearItems();
Rebuild();
}
protected override void InsertItem(int index, Record item)
{
base.InsertItem(index, item);
Rebuild();
}
protected override void RemoveItem(int index)
{
base.RemoveItem(index);
Rebuild();
}
protected override void SetItem(int index, Record item)
{
base.SetItem(index, item);
Rebuild();
}
ILookup<int, Record> indexNumber;
ILookup<string, Record> indexName;
ILookup<Profession, Record> indexProfession;
protected ILookup<int, Record> IndexNumber {
get {
if (indexNumber == null) indexNumber = this.ToLookup(x=>x.FavoriteNumber);
return indexNumber;
}
}
protected ILookup<string, Record> IndexName {
get {
if (indexName == null) indexName = this.ToLookup(x=>x.Name);
return indexName;
}
}
protected ILookup<Profession, Record> IndexProfession {
get {
if (indexProfession == null) indexProfession = this.ToLookup(x=>x.Profession);
return indexProfession;
}
}
public IEnumerable<Record> Find(int favoriteNumber) { return IndexNumber[favoriteNumber]; }
public IEnumerable<Record> Find(string name) { return IndexName[name]; }
public IEnumerable<Record> Find(Profession profession) { return IndexProfession[profession]; }
}
I think the way to do this is to write your own object that has
public ICollection<Record> this[int] { get; }
public ICollection<Record> this[Profession] { get; }
public ICollection<Record> this[Names] { get; }
where record is a class that holds your elements.
Internally, you keep a List and each indexer does List.FindAll() to get what you need.
Nothing out-of-the-box (except perhaps a DataTable). Nevertheless, it can be accomplished in a more simple way that what you've got:
Create a class to hold the data:
class PersonData {
public int FavoriteNumber;
public string Name;
public string Profession;
}
Then keep 3 dictionaries that point to the same reference:
PersonData personData = new PersonData();
Dictionary<int, PersonData> ...;
Dictionary<string, PersonData> ...;
Dictionary<string, PersonData> ...;
I'd recommend encapsulating all of this into a facade class that hides the implementation details.
Could you use an sqlite database as the backing? With sqlite you even have the option of building an in-memory db.