ODS List is a collection of abstract classes that implement a filtered select method for a paged/sorted object datasource. I have defined three absract classes that represent the filter, the returned data and the methods that produce these results:
[Serializable]
public abstract class ListFilter
{
// Define filter properties
}
[Serializable]
public abstract class ListData
{
// Define properties to be returned from the query
}
public abstract class ListMethods
{
public int ListCount(ListFilter filter)
{
var rows = listQuery();
rows = listFilter(rows, filter);
return rows.Count();
}
/// <summary>
/// List returns a page of data
/// </summary>
/// <param name="filter"></param>
/// <param name="sortType"></param>
/// <param name="startRowIndex"></param>
/// <param name="maximumRows"></param>
/// <returns></returns>
public IEnumerable<ListData> List(ListFilter filter, string sortType, int startRowIndex, int maximumRows)
{
var rows = listQuery();
rows = listFilter(rows, filter);
rows = listSort(rows, sortType);
return rows.Distinct().Skip(startRowIndex).Take(maximumRows).ToList();
}
public abstract IQueryable<ListData> listQuery();
public virtual IQueryable<ListData> listFilter(IQueryable<ListData> rows, ListFilter filter)
{
return rows;
}
public virtual IQueryable<ListData> listSort(IQueryable<ListData> rows, string sortType)
{
bool sortDescending = false;
if (!string.IsNullOrEmpty(sortType))
{
string[] values = sortType.Split(' ');
sortType = values[0];
if (values.Length > 1)
{
sortDescending = values[1] == "DESC";
}
}
if (!string.IsNullOrEmpty(sortType))
{
if (sortDescending)
{
rows = rows.OrderBy(sortType + " DESC");
}
else
{
rows = rows.OrderBy(sortType);
}
}
return rows;
}
}
My implementation hits a problem when I try to cast the ListData to the explicit returned data.
[Serializable]
public class EmployeeData : ODSList.ListData
{
public int EmployeeId { get; set; }
public int? ReportsToId { get; set; }...
}
public override IQueryable<ListData> listQuery()
{
var dc = new NorthwindDataContext();
var allrows = from emp in dc.Employees
select new EmployeeData()
{
EmployeeId = emp.EmployeeID,
ReportsToId = emp.ReportsTo, ...
};
return (IQueryable<ListData>)allrows; <-- PROBLEM ENCOUNTERED HERE
}
The diagnostic I hit is:
Unable to cast object of type 'System.Data.Linq.DataQuery1[BusinessLayer.EmployeeData]' to type 'System.Linq.IQueryable1[ODSList.ListData]'.
What version of .Net are you using? If you're on a version earlier than 4.0 the types specified in a generic interface are invariant, meaning you can't cast from IQueryable to IQueryable.
Check out the MSDN article here.
edit: After a bit of experimenting and finding this SO post, I found that something like the following should work for you:
public override IQueryable<ListData> listQuery()
{
var dc = new NorthwindDataContext();
var allrows = from emp in dc.Employees
select new EmployeeData()
{
EmployeeId = emp.EmployeeID,
ReportsToId = emp.ReportsTo, ...
} as ListData;
return allrows.OfType<ListData>();
}
You can try
return allrows.AsQueryable();
and I'm not sure but you may need to do
return allrows.Cast<ListData>().AsQueryable(); // if EmployeeData inherits from ListData; it is not required.
// but i'm not sure if ListData is the same as ODSList.ListData
Related
I am using Strategy Pattern, I have heaps of rules and I need to check all rows in Azure storage table against each Rule.
interface IRule where TEntity : TableEntity, new()
{
string TableName { get; } // It could be "ContractAccount", "Bill", "Transaction" etc.
string Rule { get; }
string SaveToTable { get; }
TableQuery<TEntity> TableQuery { get; }
ReportEntity Handle(TableEntity entity);
}
So instance of rules lives inside the Validator.
public Validator()
{
Rules = new List<IRule>();
Rules.Add(new AddressRule());
}
The Table Entity class(ContractAccount.cs Bill.cs etc.) will have the same name as the value IRule.TableName holds.
So this is where the ContractAccount comes from.
Then in the Validator, I have Validate() which looks like:
public async void Validate(CloudStorageAccount storageAccount)
{
var tableClient = storageAccount.CreateCloudTableClient();
//.....
var query = new TableQuery<ContractAccount>(); //<-- I want to replace ContractAccount with something generic
//...
var rows = await tableToBeValidated.ExecuteQuerySegmentedAsync(query, token);
}
//...
}
In my AddressRule.cs
public class AddressRule : IRule<ContractAccount>
{
public string TableName => "ContractAccount";
public string Rule => "Email cannot be empty";
public string SaveToTable => "XXXX";
public TableQuery<ContractAccount> TableQuery => new TableQuery<ContractAccount>();
public ReportEntity Handle(TableEntity entity)
{
var contract = entity as ContractAccount;
if(contract == null)
{
throw new Exception($"Expecting entity type {TableName}, but passed in invalid entity");
}
if (string.IsNullOrWhiteSpace(contract.Address))
{
var report = new ReportEntity(this.Rule, contract.UserId, contract.AccountNumber, contract.ContractNumber)
{
PartitionKey = contract.UserId,
RowKey = contract.AccountNumber
};
return report;
}
return null;
}
}
As you can see
var query = new TableQuery<ContractAccount>();
I need to replace the Hard-coded with something like:
var type = Type.GetType(tableName);
var query = new TableQuery<type>();
but the placeholder(ContractAccount) will change when app is running, it could be Bill, Policy, Transaction etc....
I cannot use the <T> thing.
How can I replace the ContractAccount with a generic thing?
Update 2
After applied Juston.Another.Programmer's suggection, I got this error.
Update 3
Now I updated code to below:
interface IRule<TEntity> where TEntity : TableEntity
{
string TableName { get; }
string Rule { get; }
string SaveToTable { get; }
ReportEntity Handle(TableEntity entity);
TableQuery<TEntity> GetTableQuery();
}
Which I specified what type of class the TEntity has to be, it removes the 1st error, but the 2nd error persists:
Error CS0310 'TEntity' must be a non-abstract type with a public
parameterless constructor in order to use it as parameter 'TElement'
in the generic type or method 'TableQuery'
Update 4
I found how to fix the another error:
interface IRule<TEntity>
where TEntity : TableEntity, new()
But then, I have problem to add my AddressRule into Rules in the Validator class.
public Validator()
{
Rules = new List<IRule<TableEntity>>();
var addressRule = new AddressRule();
Rules.Add(addressRule);
}
Something like this:
var genericType = typeof(TableQuery<>);
Type[] itemTypes = { Type.GetType("MyNamespace.Foo.Entities." + tableName) };
var concretType = genericType.MakeGenericType(itemTypes);
var query = Activator.CreateInstance(concretType);
You could use reflection like #Christoph suggested, but in this case there's an easier approach. Add a TEntity generic parameter to your IRule class instead of using the TableName string property and add a GetTableQuery method to the class.
interface IRule<TEntity>
{
string Rule { get; }
string SaveToTable { get; }
ReportEntity Handle(TableEntity entity);
TableQuery<TEntity> GetTableQuery();
}
Then, in your IRule<TEntity> implementations add the correct entity. Eg for AddressRule.
public class AddressRule : IRule<ContractAcccount>
{
public string TableName => "ContractAccount";
public string Rule => "Email cannot be empty";
public string SaveToTable => "XXXX";
public ReportEntity Handle(TableEntity entity)
{
var contract = entity as ContractAccount;
if(contract == null)
{
throw new Exception($"Expecting entity type {TableName}, but passed in invalid entity");
}
if (string.IsNullOrWhiteSpace(contract.Address))
{
var report = new ReportEntity(this.Rule, contract.UserId, contract.AccountNumber, contract.ContractNumber)
{
PartitionKey = contract.UserId,
RowKey = contract.AccountNumber
};
return report;
}
return null;
}
public TableQuery<ContractAccount> GetTableQuery()
{
return new TableQuery<ContractAccount>();
}
}
Now, in your Validate method, you can use the GetTableQuery method from the IRule.
public async void Validate(CloudStorageAccount storageAccount)
{
var tableClient = storageAccount.CreateCloudTableClient();
//.....
var query = rule.GetTableQuery();
//...
var rows = await tableToBeValidated.ExecuteQuerySegmentedAsync(query, token);
}
//...
}
The longer I think about it the more I get the feeling that what you need is a generic solution and not one with generics. I guess that the table client in line
var tableClient = storageAccount.CreateCloudTableClient();
does always return something like a DataTable or an object with an IEnumerable, independently of whether you ask for a ContractAccount or a Bill. If that's the case, it might be better to have a validator that loads all the rules of all entities from the database (or through factory patterns or hardcoded) and then applies the according ones to the given entity.
Like that, the set of rules can be defined using XML or some other sort of serialization (not part of this example) and only a few rule classes are needed (I call them EntityValidationRule).
The parent of all rules for all entities could look like this:
public abstract class EntityValidationRule {
//Private Fields
private Validator validator;
//Constructors
public EntityValidationRule(String tableName, IEnumerable<String> affectedFields) {
TableName = tableName ?? throw new ArgumentNullException(nameof(tableName));
AffectedFields = affectedFields?.ToArray() ?? Array.Empty<String>();
}
//Public Properties
public String TableName { get; }
public String[] AffectedFields { get; }
public virtual String Description { get; protected set; }
//Public Methods
public Boolean IsValid(DataRow record, ref IErrorDetails errorDetails) {
if (record == null) throw new InvalidOperationException("Programming error in Validator.cs");
if (!Validator.IdentifyerComparer.Equals(record.Table.TableName, TableName)) throw new InvalidOperationException("Programming error in Validator.cs");
String myError = GetErrorMessageIfInvalid(record);
if (myError == null) return true;
errorDetails = CreateErrorDetails(record, myError);
return false;
}
//Protected Properties
public Validator Validator {
get {
return validator;
}
internal set {
if ((validator != null) && (!Object.ReferenceEquals(validator, value))) {
throw new InvalidOperationException("An entity validation rule can only be added to a single validator!");
}
validator = value;
}
}
//Protected Methods
protected virtual IErrorDetails CreateErrorDetails(DataRow record, String errorMessage) {
return new ErrorDetails(record, this, errorMessage);
}
protected abstract String GetErrorMessageIfInvalid(DataRow record);
}
and to stay with your example, the sample implementation for an empty text field check could look like this (having an intermediate class OneFieldRule):
public abstract class OneFieldRule : EntityValidationRule {
public OneFieldRule(String tableName, String fieldName) : base(tableName, new String[] { fieldName }) {
}
protected String FieldName => AffectedFields[0];
}
and like this:
public class TextFieldMustHaveValue : OneFieldRule {
public TextFieldMustHaveValue(String tableName, String fieldName) : base(tableName, fieldName) {
Description = $"Field {FieldName} cannot be empty!";
}
protected override String GetErrorMessageIfInvalid(DataRow record) {
if (String.IsNullOrWhiteSpace(record.Field<String>(FieldName))) {
return Description;
}
return null;
}
}
Then the central validator that works like a service to validate whatever entity needs to be validated I might implement like this:
public sealed class Validator {
//Private Fields
private Dictionary<String, List<EntityValidationRule>> ruleDict;
//Constructors
//The list of all rules we just have somehow...
public Validator(IEnumerable<EntityValidationRule> rules, StringComparer identifyerComparer) {
if (rules == null) throw new ArgumentNullException(nameof(rules));
if (identifyerComparer == null) identifyerComparer = StringComparer.OrdinalIgnoreCase;
IdentifyerComparer = identifyerComparer;
ruleDict = new Dictionary<String, List<EntityValidationRule>>(IdentifyerComparer);
foreach (EntityValidationRule myRule in rules) {
myRule.Validator = this;
List<EntityValidationRule> myRules = null;
if (ruleDict.TryGetValue(myRule.TableName, out myRules)) {
myRules.Add(myRule);
} else {
myRules = new List<EntityValidationRule> { myRule };
ruleDict.Add(myRule.TableName, myRules);
}
}
}
//Public Properties
public StringComparer IdentifyerComparer { get; }
//Public Methods
public Boolean IsValid(DataRow record, ref IErrorDetails[] errors) {
//Check whether the record is null
if (record == null) {
errors = new IErrorDetails[] { new ErrorDetails(record, null, "The given record is null!") };
return false;
}
//Loop through every check and invoke them
List<IErrorDetails> myErrors = null;
IErrorDetails myError = null;
foreach (EntityValidationRule myRule in GetRules(record.Table.TableName)) {
if (myRule.IsValid(record, ref myError)) {
if (myErrors == null) myErrors = new List<IErrorDetails>();
myErrors.Add(myError);
}
}
//Return true if there are no errors
if (myErrors == null) return true;
//Otherwise assign them as result and return false
errors = myErrors.ToArray();
return false;
}
//Private Methods
private IEnumerable<EntityValidationRule> GetRules(String tableName) {
if (ruleDict.TryGetValue(tableName, out List<EntityValidationRule> myRules)) return myRules;
return Array.Empty<EntityValidationRule>();
}
}
And the error details as an interface:
public interface IErrorDetails {
DataRow Entity { get; }
EntityValidationRule Rule { get; }
String ErrorMessage { get; }
}
...and an implementation of it:
public class ErrorDetails : IErrorDetails {
public ErrorDetails(DataRow entity, EntityValidationRule rule, String errorMessage) {
Entity = entity;
Rule = rule;
ErrorMessage = errorMessage;
}
public DataRow Entity { get; }
public EntityValidationRule Rule { get; }
public String ErrorMessage { get; }
}
I know this is a totally different approach as you started off, but I think the generics give you a hell of a lot of work with customized entities that have customized validators for each and every table in your database. And as soon as you add a table, code needs to be written, compiled and redistributed.
I have a list where TestClass is a class with some predefined properties. So when i get data and bind my list with data i need to ignore some properties of TestClass by comparing it with a list. How can i achieve that?
Below is my code
public class TestClass
{
public int id{get;set;}
public string fname{get;set;}
public string lname{get;set;}
public string job {get;set;}
public string role{get;set;}
public string address{get;set;}
}
List<TestClass> ulist = null;
ulist = ToList<TestClass>(usersdataset.tables[0]); //fill my list with the data code is given below
so after getting the data into the list i need to remove some properties by comparing it with list of properties which should be returned.for example if my filteredlist should only show id,fname,role then i need to remove the extra properties from my ulist. so after the filter ulist should only contain id,fname and role
ToList Method
public static List<T> ToList<T>(DataTable dataTable) where T : new()
{
var dataList = new List<T>();
//Define what attributes to be read from the class
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
//Read Attribute Names and Types
var objFieldNames = typeof(T).GetProperties(flags).Cast<PropertyInfo>().
Select(item => new
{
Name = item.Name,
Type = Nullable.GetUnderlyingType(item.PropertyType) ?? item.PropertyType
}).ToList();
//Read Datatable column names and types
var dtlFieldNames = dataTable.Columns.Cast<DataColumn>().
Select(item => new {
Name = item.ColumnName,
Type = item.DataType
}).ToList();
foreach (DataRow dataRow in dataTable.AsEnumerable().ToList())
{
var classObj = new T();
foreach (var dtField in dtlFieldNames)
{
PropertyInfo propertyInfos = classObj.GetType().GetProperty(dtField.Name);
var field = objFieldNames.Find(x => x.Name == dtField.Name);
//var field = filteredColumns.Find(x => x.PropertyName == dtField.Name);
if (field != null)
{
if (dataRow[dtField.Name] != DBNull.Value)
propertyInfos.SetValue(classObj, dataRow[dtField.Name], null);
}
}
dataList.Add(classObj);
}
return dataList;
}
Use the overvride function Equals:
This sample compare only the id property
public class TestClass
{
public int id { get; set; }
public string fname { get; set; }
public string lname { get; set; }
public string job { get; set; }
public string role { get; set; }
public string address { get; set; }
public override bool Equals(object obj)
{
if (obj.GetType().Name != this.GetType().Name)
{
return false;
}
TestClass testclassObject = (TestClass)obj;
if (testclassObject.id != this.id)
{
return false;
}
return true;
}
I have a problem:
public class PaginationSet
{
public int TotalItemCount { get; set; }
public int Page { get; set; }
public int Amount { get; set; }
public string Sort { get; set; }
public string Order { get; set; }
/// <summary>
/// This is used to store all the above information in, while still maintaining the automated index count from the internal for loop link builder.
///
/// Don't forget to pass the index into this!
/// </summary>
public Func<int, object> PaginationLinkData
{
get
{
return index => new
{
page = index, // This is the internal pointer part that is used currently by Bootstrap pagination function
amount = this.Amount,
sort = this.Sort,
order = this.Order
};
}
}
}
this.Sort and this.Order are sometimes null. If they are, I would like to not place them inside the returning Func<int,object>... How do i go about doing this?
It might look something like this:
public Func<int, object> PaginationLinkData
{
get
{
Func<int,object> something = index => new
{
page = index, // This is the internal pointer part that is used currently by Bootstrap pagination function
amount = this.Amount
};
if( this.Sort != null )
{
something.sort = this.Sort,
something.order= this.Order
}
return something;
}
}
}
Try using expando object...
public Func<int, object> PaginationLinkData
{
get
{
return index =>
{
dynamic obj = new ExpandoObject();
obj.page = index;
obj.amount = this.Amount;
if (this.Sort != null)
{
obj.sort = this.Sort;
}
if (this.Order != null)
{
obj.order = this.Order;
}
return obj;
};
}
}
Can't you just do something like this?
public Func<int, object> PaginationLinkData
{
get
{
if( this.Sort != null )
{
return index => new
{
page = index, // This is the internal pointer part that is used currently by Bootstrap pagination function
amount = this.Amount,
sort = this.Sort,
order = this.Order
};
}
else
{
return index => new
{
page = index, // This is the internal pointer part that is used currently by Bootstrap pagination function
amount = this.Amount,
};
}
}
}
I am guessing you are serializing to JSon somewhere. If so and you can use dynamic why not:
/// <summary>
/// This is used to store all the above information in, while still maintaining the automated index count from the internal for loop link builder.
///
/// Don't forget to pass the index into this!
/// </summary>
public Func<int, object> PaginationLinkData
{
get
{
dynamic res = new ExpandoObject();
res.amount = Amount;
if (Sort != null) res.sort = Sort;
if (Order != null) res.order = Order;
return index =>
{
res.page = index;
return res;
};
}
}
I’m experiencing a problem with the longlistselector witch is a bit strange... I’m reading a list asynchronously on a multi Pivot page, and if I don’t change the pivot until the resulto f the list Reading, it will create the contacts list successfuly ( in a longlistselector on the pivot item number 3 ) and when I go to that pivot the contacts list is displayed withou any problems, but when I open the Page and change the pivot before the longlistselector is created when the asychronous function returns the results and fills the longlistselector on pivot no.3 the contacts wont get updated ( when I go to pivot 3 no contacts are shown)...
I’ll post my code so you can have a clearer picture of the situation and maybe figure out what is happening.
The code is based in the PhoneToolkit LongListSelector example (buddies list)
public partial class Feeds : PhoneApplicationPage
{
bool isLoading = false;
bool loadingFilmates = true;
public Feeds()
{
InitializeComponent();
// ...
Loaded += FeedsPage_Loaded;
SystemTray.ProgressIndicator = new ProgressIndicator();
DataContext = this;
getFilmatesList();
longlistFilmates.SelectionChanged += FilmateSelectionChanged;
// ...
}
private async void getFilmatesList()
{
Feed userFilmates = await Feed.GetFilmates(App.Current.AppUser, App.Current.WSConfig, 0, "", 10000); // read feeds from webservice
Filmates = AlphaKeyGroup<StayObjectFilmates>.CreateGroups(AllFilmates.GetCurrent(userFilmates), CultureInfo.CurrentUICulture, (p) => { return p.Name; }, true);
//longlistFilmates.Visibility = System.Windows.Visibility.Collapsed;
longlistFilmates.Visibility = System.Windows.Visibility.Visible;
longlistFilmates.UseLayoutRounding = true;
pivotFeed.Visibility = System.Windows.Visibility.Collapsed;
pivotFeed.Visibility = System.Windows.Visibility.Visible;
}
}
Notice that I’ve even tried changing the Visibility property when it loads to force a re-render on the screen and it didn’t work.
This is the StayObjectFilmates class:
public class StayObjectFilmates
{
public string Img { get; private set; }
public string Name { get; private set; }
public string UserId { get; private set; }
public string Id { get; set; }
public User user { get; set; }
public StayObjectFilmates()
{
//Img = "";
//Name = "";
//Id = "";
}
public StayObjectFilmates(string p_img, string p_name, string p_Id)
{
Img = p_img;
Name = p_name;
UserId = p_Id;
}
public StayObjectFilmates(User p_user)
{
user = p_user;
}
public static string GetNameKey(StayObjectFilmates filmate)
{
char key = char.ToLower(filmate.Name[0]);
if (key < 'a' || key > 'z')
{
key = '#';
}
return key.ToString();
}
public static int CompareByName(object obj1, object obj2)
{
StayObjectFilmates p1 = (StayObjectFilmates)obj1;
StayObjectFilmates p2 = (StayObjectFilmates)obj2;
int result = p1.Name.CompareTo(p2.Name);
if (result == 0)
{
result = p1.Img.CompareTo(p2.Img);
}
return result;
}
}
This is the AllFilmates class:
public class AllFilmates : IEnumerable<StayObjectFilmates>
{
private static Dictionary<int, StayObjectFilmates> _filmateLookup;
private static AllFilmates _instance;
private Feed filmates;
// public List<StayObjectFilmates> Filmates { get; private set; }
public static AllFilmates GetCurrent(Feed p_filmates)
{
if (_instance == null)
{
_instance = new AllFilmates();
}
_instance.filmates = p_filmates;
return _instance;
}
public static AllFilmates Current
{
get
{
return _instance ?? (_instance = new AllFilmates());
}
}
public StayObjectFilmates this[int index]
{
get
{
StayObjectFilmates filmate;
_filmateLookup.TryGetValue(index, out filmate);
return filmate;
}
}
#region IEnumerable<StayObjectFilmates> Members
public IEnumerator<StayObjectFilmates> GetEnumerator()
{
EnsureData();
return _filmateLookup.Values.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
EnsureData();
return _filmateLookup.Values.GetEnumerator();
}
#endregion
private void EnsureData()
{
if (_filmateLookup == null)
{
_filmateLookup = new Dictionary<int, StayObjectFilmates>();
if (filmates != null)
{
int i = 0;
foreach (var item in filmates.itemsList)
{
User friend = item as User;
string userphoto = (friend.photo == null) ? "Images/avatar.jpg" : friend.photo;
StayObjectFilmates f = new StayObjectFilmates(userphoto, friend.fullName, i.ToString());
_filmateLookup[i] = f;
i++;
}
}
}
}
}
And this is the AlphaKeyGroup.cs file :
public class AlphaKeyGroup<T> : List<T>
{
private const string GlobeGroupKey = "\uD83C\uDF10";
/// <summary>
/// The delegate that is used to get the key information.
/// </summary>
/// <param name="item">An object of type T</param>
/// <returns>The key value to use for this object</returns>
public delegate string GetKeyDelegate(T item);
/// <summary>
/// The Key of this group.
/// </summary>
public string Key { get; private set; }
/// <summary>
/// Public constructor.
/// </summary>
/// <param name="key">The key for this group.</param>
public AlphaKeyGroup(string key)
{
Key = key;
}
public AlphaKeyGroup(IGrouping<string, T> grouping)
{
Key = grouping.Key;
this.AddRange(grouping);
}
/// <summary>
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
/// </summary>
/// <param name="slg">The </param>
/// <returns>Theitems source for a LongListSelector</returns>
private static List<AlphaKeyGroup<T>> CreateGroups(SortedLocaleGrouping slg)
{
List<AlphaKeyGroup<T>> list = new List<AlphaKeyGroup<T>>();
foreach (string key in slg.GroupDisplayNames)
{
if (key == "...")
{
list.Add(new AlphaKeyGroup<T>(GlobeGroupKey));
}
else
{
list.Add(new AlphaKeyGroup<T>(key));
}
}
return list;
}
/// <summary>
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
/// </summary>
/// <param name="items">The items to place in the groups.</param>
/// <param name="ci">The CultureInfo to group and sort by.</param>
/// <param name="getKey">A delegate to get the key from an item.</param>
/// <param name="sort">Will sort the data if true.</param>
/// <returns>An items source for a LongListSelector</returns>
public static List<AlphaKeyGroup<T>> CreateGroups(IEnumerable<T> items, CultureInfo ci, GetKeyDelegate getKey, bool sort)
{
SortedLocaleGrouping slg = new SortedLocaleGrouping(ci);
List<AlphaKeyGroup<T>> list = CreateGroups(slg);
foreach (T item in items)
{
int index = 0;
if (slg.SupportsPhonetics)
{
//check if your database has yomi string for item
//if it does not, then do you want to generate Yomi or ask the user for this item.
//index = slg.GetGroupIndex(getKey(Yomiof(item)));
}
else
{
index = slg.GetGroupIndex(getKey(item));
}
if (index >= 0 && index < list.Count)
{
list[index].Add(item);
}
}
if (sort)
{
foreach (AlphaKeyGroup<T> group in list)
{
group.Sort((c0, c1) => { return ci.CompareInfo.Compare(getKey(c0), getKey(c1)); });
}
}
return list;
}
}
The FilmatesInGroup.cs and FilmatesByName.cs is the same as PeopleInGroup.cs and PeopleByFirstName.cs in the PhoneToolKit example with the names adapted.
The longlistFilmates LongListSelector Object is inserted directly inside the PivotItem no.3 ( no Grid and no ScrollView )
Thanks in advance for any help!
I have 2 lists that I need to check for common objects that are being passed to a generic wrapper.
The first list (selList) is a typed entity list. The ID field in this list is different, based on what the base type for the list being created.
The second list (masterList) is an anonymous IList that I know has 2 properties {ID, DESC} - ID (could be int or string), and description (string). I can get the value of the ID property in this list.
I would like to return an extension of the master list that has a boolean field indicating whether the item in the master list is contained in the selList.
I'm thinking that I'm somewhere along the lines of the Visitor pattern.
public class SelectionCriteria<T> : where T : class
{
public IList<T> EligibleList { get; private set; }
public IList LookupList { get; private set; }
}
LookupList = new List<object>
{
new { ID = "fid", DESC = "Record 1"},
new { ID = "Record2", DESC = "Record 2"},
new { ID = "Record3", DESC = "Record 3"},
new { ID = "Record4", DESC = "Record 4"},
};
EligibleList = new List<AssetClass>
{
new AssetClass { FEE_ID = "fid", ASSET_CLASS = "A" },
};
I should get the following results:
LookupList[0] == true
LookupList[1] == false
LookupList[2] == false
LookupList[3] == false
Is there a better way to solve this problem?
var results = LookupList.Select(l => EligibleList.Any(e => e.FEE_ID==l.ID))
.ToList();
Using this as a definition for SelectionCriteria<T>
public class SelectionCriteria<T>
where T : class
{
public IList<T> EligibleList { get; private set; }
public IList LookupList { get; private set; }
public SelectionCriteria(IList lookupList, IList<T> eligibleList)
{
LookupList = lookupList;
EligibleList = eligibleList;
}
public bool this[int index]
{
get
{
var element = LookupList[index];
foreach (var item in EligibleList)
{
if (item.Equals(element))
{
return true;
}
}
return false;
}
}
}
And this as a definition for AssetClass
public class AssetClass : IEquatable<AssetClass>
{
public string FEE_ID { get; set; }
public string ASSET_CLASS { get; set; }
public bool Equals(AssetClass other)
{
return !ReferenceEquals(other, null) && other.FEE_ID == FEE_ID && other.ASSET_CLASS == ASSET_CLASS;
}
//Check to see if obj is a value-equal instance of AssetClass, if it's not, proceed
// to doing some reflection checks to determine value-equality
public override bool Equals(object obj)
{
return Equals(obj as AssetClass) || PerformReflectionEqualityCheck(obj);
}
//Here's where we inspect whatever other thing we're comparing against
private bool PerformReflectionEqualityCheck(object o)
{
//If the other thing is null, there's nothing more to do, it's not equal
if (ReferenceEquals(o, null))
{
return false;
}
//Get the type of whatever we got passed
var oType = o.GetType();
//Find the ID property on it
var oID = oType.GetProperty("ID");
//Get the value of the property
var oIDValue = oID.GetValue(o, null);
//If the property type is string (so that it matches the type of FEE_ID on this class
// and the value of the strings are equal, then we're value-equal, otherwise, we're not
return oID.PropertyType == typeof (string) && FEE_ID == (string) oIDValue;
}
}
You can get elements that are found in the list of eligible items that exist in the list of lookup items like so:
for (var i = 0; i < assetClassSelectionCriteria.LookupList.Count; ++i)
{
Console.WriteLine("LookupList[{0}] == {1}", i, assetClassSelectionCriteria[i]);
}
You could also use the following for PerformReflectionEqualityCheck in AssetClass if you don't like seeing the reflection goodness
private bool PerformReflectionEqualityCheck(object o)
{
if (ReferenceEquals(o, null))
{
return false;
}
dynamic d = o;
try
{
return FEE_ID == (string) d.ID;
}
catch
{
return false;
}
}
If by "extension of the master list" you meant an extension method, then, instead of declaring an indexer on SelectionCriteria<T> to get the results, you could do something like this:
public static class SelectionCriteriaExtensions
{
public static bool IsLookupItemEligible<T>(this SelectionCriteria<T> set, int index)
where T : class
{
var element = set.LookupList[index];
foreach (var item in set.EligibleList)
{
if (item.Equals(element))
{
return true;
}
}
return false;
}
}
and call it like this:
assetClassSelectionCriteria.IsLookupItemEligible(0);