views:

1770

answers:

3

I have scoured the internet for this answer and asked several developers and have come up short. I have a Class called StaffingPositionsDataContract that I am loading mock data (for now) into a List<> and returning to a page. This is working great, but now I need to filter the list based on another list of values that are inputted on the screen, send to this service as JSON, then deserialized into a list of the same class. Note that the user can filter on any one of these class members, so all may be null, or one may be null, etc. Here is the service method:

   public List<StaffingPositionsDataContract> GetStaffingPosition(string searchFilters)
    {
        var filters = JsonConvert.DeserializeObject<List<StaffingPositionsDataContract>>(searchFilters);
        IList<StaffingPositionsDataContract> contracts = new StaffingPositionsDataContract().LoadMockData();

        if (searchFilters.Length > 4)
        {
            //Filter contracts here!
        }

        return contracts;
    }

Here is the Data Contract Class with Mock data to load:

[DataContract] [Serializable]
public class StaffingPositionsDataContract
{
    [DataMember(Order = 1)] public int PositionId { get; set; }
    [DataMember(Order = 2)] public string Series { get; set; }
    [DataMember(Order = 3)] public string BFY { get; set; }
    [DataMember(Order = 4)] public string BudgetStatus { get; set; }
    [DataMember(Order = 5)] public string DutyStation { get; set; }
    [DataMember(Order = 6)] public string OrgLocation { get; set; }
    [DataMember(Order = 7)] public string BudgetingEntity { get; set; }
    [DataMember(Order = 8)] public string SeriesTitle { get; set; }
    [DataMember(Order = 9)] public int PersonnelId { get; set; }
    [DataMember(Order = 10)] public string PositionStatus { get; set; }
    [DataMember] public int RecId { get; set; }

    public List<StaffingPositionsDataContract> LoadMockData()
    {
        List<StaffingPositionsDataContract> staffingposition = new List<StaffingPositionsDataContract>()
        {
            new StaffingPositionsDataContract() {RecId=1, PositionId = 12345, Series="", BFY="FY2010", BudgetStatus="Actual", DutyStation="", OrgLocation="", BudgetingEntity=""},
            new StaffingPositionsDataContract() {RecId=2, PositionId = 67891, Series="", BFY="FY2011", BudgetStatus="Actual", DutyStation="", OrgLocation="", BudgetingEntity=""},
            new StaffingPositionsDataContract() {RecId=3,PositionId = 12345, Series="", BFY="FY2010", BudgetStatus="Projected", DutyStation="", OrgLocation="", BudgetingEntity=""},
            new StaffingPositionsDataContract() {RecId=4,PositionId = 67892, Series="", BFY="FY2011", BudgetStatus="Projected", DutyStation="", OrgLocation="", BudgetingEntity=""},
            new StaffingPositionsDataContract() {RecId=5,PositionId = 987654, Series="", BFY="FY2010", BudgetStatus="Projected", DutyStation="", OrgLocation="", BudgetingEntity=""}
        };
        return staffingposition;
    }
}

Note that filters will always be a list of one, some or all values being populated. Please save my sanity and help if you can! THANKS!

I should have noted earlier that I REALLY want this to be a generic function that can be used by any of these similar data classes(there are many).

+1  A: 

I would not filter where you are filtering. I would pass the filter to the load of the collection and use it internally there, either by passing those criteria to the service or the database or similar, or by forcing each object to validate itself against the criteria before adding it to the collection.

public List<StaffingPositionsDataContract> GetStaffingPosition(string searchFilters)
{
    var filters = JsonConvert.DeserializeObject<List<StaffingPositionsDataContract>>(searchFilters);
    IList<StaffingPositionsDataContract> contracts = new StaffingPositionsDataContract().LoadMockData(searchFilters);

    return contracts;
}

[DataContract] [Serializable]
public class StaffingPositionsDataContract
{
    [DataMember(Order = 1)] public int PositionId { get; set; }
    [DataMember(Order = 2)] public string Series { get; set; }
    [DataMember(Order = 3)] public string BFY { get; set; }
    [DataMember(Order = 4)] public string BudgetStatus { get; set; }
    [DataMember(Order = 5)] public string DutyStation { get; set; }
    [DataMember(Order = 6)] public string OrgLocation { get; set; }
    [DataMember(Order = 7)] public string BudgetingEntity { get; set; }
    [DataMember(Order = 8)] public string SeriesTitle { get; set; }
    [DataMember(Order = 9)] public int PersonnelId { get; set; }
    [DataMember(Order = 10)] public string PositionStatus { get; set; }
    [DataMember] public int RecId { get; set; }

    public List<StaffingPositionsDataContract> LoadMockData(string searchfilters)
    {
        // filter the list returned here

        List<StaffingPositionsDataContract> staffingposition = new List<StaffingPositionsDataContract>()
        {
            new StaffingPositionsDataContract() {RecId=1, PositionId = 12345, Series="", BFY="FY2010", BudgetStatus="Actual", DutyStation="", OrgLocation="", BudgetingEntity=""},
            new StaffingPositionsDataContract() {RecId=2, PositionId = 67891, Series="", BFY="FY2011", BudgetStatus="Actual", DutyStation="", OrgLocation="", BudgetingEntity=""},
            new StaffingPositionsDataContract() {RecId=3,PositionId = 12345, Series="", BFY="FY2010", BudgetStatus="Projected", DutyStation="", OrgLocation="", BudgetingEntity=""},
            new StaffingPositionsDataContract() {RecId=4,PositionId = 67892, Series="", BFY="FY2011", BudgetStatus="Projected", DutyStation="", OrgLocation="", BudgetingEntity=""},
            new StaffingPositionsDataContract() {RecId=5,PositionId = 987654, Series="", BFY="FY2010", BudgetStatus="Projected", DutyStation="", OrgLocation="", BudgetingEntity=""}
        };
        return staffingposition;
    }
}
Cade Roux
A: 

How are you filtering? Are you excluding contracts deserialized from json? Are you having an issue with evaluating equality? Its a little hard to get the issue here.

Filtering one list with another is pretty easy with Linq:

var foo = new int[]{1,2,3,4,5};
var bar = new int[]{1,3,5};
var result = from x in foo where !bar.Contains(x) select x;

This results in result containing 2, 4. You just have to have an Equals method in your data contract that performs whatever equivalency tests your requirements dictate.

If your UI allows users to "create" 1 to n contracts to filter on, and you get this as a collection of contracts (none to all of which may be null), this will still work.


In response to Zacho's comment:

var foo = new string[]{"1","2","3","4","5"}; 
var bar = new string[]{"1","3",null,"5"};
var result = from x in foo where !bar.Contains(x) select x;

The filter enumerable--bar--contains nulls. This function works. If this solution doesn't work for you, Zacho, you've got a different issue. My guess it would have something to do with equality comparisons.

Will
I like the idea, but this does not work because some of the members of the filters may be null, so using the above example filters nothing.
Zacho
Good observation Will. Your right in that your sample works, it worked for me too, but using that exact same idea, and plugging in the appropriate values for my use, and the filter does not work. Here is what I am doing. var resultx = from x in contracts where !filters.Contains(x) select x;
Zacho
You need to implement Equals in your objects. Contains is using the default implementation of Equals(on System.Object), which is a reference check. You want an implementation of Equals that checks properties within the object for equality. If that's not understandable, I suggest you grab CLR Via C# and read it.
Will
From my answer: "You just have to have an Equals method in your data contract that performs whatever equivalency tests your requirements dictate." http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx
Will
Thanks Will. As much as I hate to do this, I have to give up. I will have to build a manual json object and filter the list manually. The solution really sucks, but this is too far above my head and staring at it for several days doesn't help.
Zacho
A: 

Send the items of both collections to XElements, making each property an XAttribute.

Then you can compare attributes to find matches, which is easy.

public class Customer
{
    public int CustomerId { get; set; }
    public string Name { get; set; }
    public string FavoriteColor { get; set; }

    // probably a better way to do this
    // just writing fast for demo
    public XElement ToXElement()
    {
        List<XAttribute> attributes = new List<XAttribute>();

        if (this.CustomerId != 0)
            attributes.Add(new XAttribute("CustomerId", this.CustomerId));
        if (this.Name != null)
            attributes.Add(new XAttribute("Name", this.Name));
        if (this.FavoriteColor != null)
            attributes.Add(new XAttribute("FavoriteColor", this.FavoriteColor));

        XElement result = new XElement("Customer", attributes);
        return result;
    }
}

Then to compare them....

List<Customer> source = new List<Customer>()
{ new Customer(){ CustomerId=1, FavoriteColor="Blue", Name="Alex" },
  new Customer(){ CustomerId=2, FavoriteColor="Green", Name="Bob" },
  new Customer(){ CustomerId=3, FavoriteColor="Blue", Name="Cindy" },
  new Customer(){ CustomerId=4, FavoriteColor="Red", Name="Darren" }
};
List<Customer> examples = new List<Customer>()
{ new Customer(){ FavoriteColor="Blue", CustomerId = 3},
  new Customer(){ Name="Darren"}
};

List<XElement> sourceElements = source.Select(c => c.ToXElement()).ToList();
List<XElement> exampleElements = examples.Select(c => c.ToXElement()).ToList();

//and now the query...
var query = sourceElements
  .Where(s => exampleElements
    .Any(ex => ex.Attributes()
      .All(exatt => s.Attribute(exatt.Name).Value == exatt.Value)
    )
  );

foreach(XElement x in query)
  Console.WriteLine(x);

Also:

  • If you send in one empty example (all defaults), you get back the whole source list.
  • If you send in no examples (an empty list), you get back nothing.
David B
Do you have an example?
Zacho