Search code examples
c#nhibernatemulti-value-dictionary

Use MultiValueDictionary instead of Dictionary with NHibernate.Criterion.Restrictions


Currently, I have this code, which works, getting a foodList:

var myDictionary = new System.Collections.Generic.Dictionary<string, object>();
myDictionary .Add("RefId",1);
myDictionary.Add("Type", "fruit");
var foodList = this.factoryService.GetMyService()
                    .GetByCriteria(sortOrder, NHibernate.Criterion.Restrictions.AllEq(myDictionary));

I want to use MultiValueDictionary so as to gather more than one Type.

var multiValueDictionary = new System.Collections.Generic.MultiValueDictionary<string, object>();
multiValueDictionary.Add("RefId",1);
multiValueDictionary.Add("Type", "fruit");
multiValueDictionary.Add("Type", "vegetable");
multiValueDictionary.Add("Type", "fungus");

var foodList = this.factoryService.GetMyService()
                    .GetByCriteria(sortOrder, NHibernate.Criterion.Restrictions.AllEq(multiValueDictionary));

That last line doesn't compile because MultiValueDictionary does not implement IDictionary, giving the error:

Error CS1503 Argument 1: cannot convert from 'System.Collections.Generic.MultiValueDictionary' to 'System.Collections.IDictionary'

How can I do this?


Solution

  • AllEq implementation is simply:

    /// <summary>
    /// Apply an "equals" constraint to each property in the key set of a IDictionary
    /// </summary>
    /// <param name="propertyNameValues">a dictionary from property names to values</param>
    /// <returns></returns>
    public static AbstractCriterion AllEq(IDictionary propertyNameValues)
    {
        Conjunction conj = Conjunction();
    
        foreach (DictionaryEntry item in propertyNameValues)
        {
            conj.Add(Eq(item.Key.ToString(), item.Value));
        }
    
        return conj;
    }
    

    So for your multi-value case, you could cease putting it in the dictionary and manually add it afterward:

    var myDictionary = new Dictionary<string, object>();
    myDictionary.Add("RefId", 1);
    
    var filter = (Conjunction)Restrictions.AllEq(myDictionary);
    filter.Add(Restrictions.In("Type", new [] {"fruit", "vegetable", "fungus"});
    
    var foodList = this.factoryService.GetMyService()
        .GetByCriteria(sortOrder, filter);
    

    Of course if you now only have RefId to put in your dictionary, better simplify it:

    var filter = Restrictions.Conjunction();
    filter.Add(Restrictions.Eq("RefId", 1));
    filter.Add(Restrictions.In("Type", new [] {"fruit", "vegetable", "fungus"}));
    
    var foodList = this.factoryService.GetMyService()
        .GetByCriteria(sortOrder, filter);
    

    Now if you want an AllIn handling multi-values, code it:

    public static AbstractCriterion AllIn<TValue>(
        MultiValueDictionary<string, TValue> propertyNameValues)
    {
        var conj = Restrictions.Conjunction();
    
        foreach (KeyValuePair<TKey, IReadOnlyCollection<TValue>> item in propertyNameValues)
        {
            if (item.Value.Count > 1)
            {
                conj.Add(Restrictions.In(item.Key, item.Value.ToArray()));
            }
            else
            {
                conj.Add(Restrictions.Eq(item.Key, item.Value.FirstOrDefault()));
            }
        }
    
        return conj;
    }