4

私はASP.NETWebApiサービスに取り組んでおり、コントローラーアクションの1つが0個以上のKey:Valueペアを持つJSON文字列を受け入れて、オブジェクトのコレクションを検索します。このため、コレクションをフィルタリングするリクエストにどのフィールド名が含まれるかわかりません。

これで、提供されたデータに基づいてWHERE式をチェーンすることで動的クエリを構築するコードができました。この場合の問題は、フィルタリングする必要のあるフィールド名がわからないだけでなく、フィールドのリストとその値が各オブジェクト内のコレクションに格納され、そのコレクション内のオブジェクトには2つのプロパティしかないことです。名前と価値。

データは、私が制御できない(したがって、フォーマットを変更できない)一連のXMLファイルから逆シリアル化されており、各ファイルに含まれるフィールドのリストは異なる可能性があります。(以下のクラス定義を参照してください)。

[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public class Bug
{
    [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public int ID { get; set; }

    [System.Xml.Serialization.XmlElementAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    public DateTime ChangedDate { get; set; }

    [System.Xml.Serialization.XmlArrayAttribute(Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    [System.Xml.Serialization.XmlArrayItemAttribute("Field", typeof(BugField), Form = System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable = false)]
    public List<BugField> Fields {get; set;}
}

次のクエリを実行すると、すべてが正常に機能します-JSONリクエストに基づいて探している結果が得られます-ただし、そのリクエストは1つのフィールドと1つの値のみを検索し、クエリにはハードコードされた正しいフィールドのインデックスがあります;) (FYI-itemListは、フィルタリングなしで以前に作成されたコレクションです)

itemList = (List<Bug>)itemList.Where(x => x.Fields[15].Value.ToString() == field.Value.ToString()).ToList();

JSONリクエストから提供された検索フィールドに基づいて動的LINQクエリ(WHERE式をチェーンする)を作成するためのコードがあります(長すぎるため、ここにはコードを含めていません-完全に関連性があるかどうかはわかりません...まだ)。ただし、式を解析する方法では、検索するプロパティの名前を参照できる必要があります。これは、Nameプロパティの値であるため、もちろん不明です。

だから-クエリパラメータを決定するフィールド名が事前に不明であることを考慮に入れるようにクエリを変更するにはどうすればよいですか?

編集:次のコードブロックは、私が使用したいものを示しています(つまり、動的クエリビルダーで機能します)。最初の行は、クラスのフィールド名がJSON文字列で提供されるフィールド名と同じように定義されている場合にうまく機能するコードです。2つ目は、内部コレクションのフィールド名プロパティにアクセスしようとした試みの1つです。

foreach (KeyValuePair<string, object> field in queryFields)
{
    itemList = itemList.Where<Bug>(field.Key, field.Value, (FilterOperation)StringEnum.Parse(typeof(FilterOperation), "eq"));
    itemList = itemList.Where<Bug>(x => x.Fields.Any(y => y.Name == field.Key && y.Value == field.Value)); 
}
4

3 に答える 3

1

私はこれが古いことを知っていますが、これが私がすべての可能な名前を入力する必要なしにそれをした方法です:

1)匿名のIEnumerableが入ってくる、必要に応じてIEnumerableまたはIListとしてキャストする
2)最初の要素で、

var property = element.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)  

3)プロパティ内のForeachオブジェクトで、データテーブルに列を追加します。

table.Columns.Add(property.Name, typeof(property.GetValue(element, null));   

4)コレクションを最初から繰り返します:foreach:

var prop = item.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);

dynamic i = new ExpandoObject();
IDictionary<string, object> d = (IDictionary<string, object>)i;
foreach (var p in prop)
{
    d.Add(p.Name, p.GetValue(item));
}
list.Rows.Add(((IDictionary<string, object>)d).Values.ToArray());

これにより、匿名型が取得され、ExpandoObject(IDictionaryと完全に互換性があります)に抽出され、IDictionary動的オブジェクトを配列としてキャストして行が追加されます。

スローできる任意のタイプでスケーリングし、構築時にデータを操作できるようにします。

于 2014-06-06T16:10:17.437 に答える
0

それを解決するためのいくつかのアプローチがあります。

1)フィルタリングに使用できるすべてのプロパティのリストを用意し、プロパティを照合して、linqクエリに動的に追加します。

動作する方法は、jsonオブジェクトを受け入れ、それらの値とともに渡されたプロパティを反復処理することです。だから、あなたがあなたのコントローラーに入ったとしましょう:

{
Prop[0].Name = "Name" 
Prop[0].Value = "Justin"
Prop[1].Name = "Email"
Prop[1].Value = "j@g.com"
}

たとえば、コントローラーでは、各キー値を反復処理し、それを動的にチェーンします。いくつかの擬似コード:

foreach(var keyValue in Filters){
  var prop = keyValue.Name;
  var val = keyValue.Value;
  myLinqQuery.AddWhere(c => c.GetType().GetProperty().Name == prop && c.GetValue() == Value);
}

このアプローチはうまく機能しますが、大きな欠点の1つは、リクエスト内の各プロパティをリフレクションでフィルタリングしようとすることです。その場合、ある程度の制御が失われます。

2)いくつかのフィルターのみを許可し、それぞれにフィルター条件を設定します。そのオプションでは、フィルターのリストのみを許可し、フィルターごとに何らかのwhereアクションを設定します。簡単な例を次に示します。コントローラーが次のものを取り込んだとします。

public ActionResult Filter(string name, string email){
 //name and email are the filter values
 var query = ( from c in (List<Bug>)itemList select c);
if(!string.IsNullOrEmpty(name))
  query.AddWhere(c => c.Name == name);

if(!string.IsNullOrEmpty(email))
  query.AddWhere(c => c.Email == email);


}

許可されたフィルターごとに個別のクラスを作成し、そのフィルターアクションを公開することで、より多くのOOPとSOLIDになるようにさらに設計できます。

それが役立つかどうか教えてください!

于 2013-03-25T17:02:40.717 に答える
0

他の場所で大きな変更を加えずにこのソリューションを機能させるためにほぼ丸2日を費やした後、私は最終的に、これを簡単にするためにモデルにわずかな設計変更を加えることにしました。

これを解決した方法は、カスタムオブジェクトのリストを使用する代わりに、各フィールドをモデルに直接追加することでした。このため、XML逆シリアル化を削除し、代わりにXDocumentを使用してノード内のすべての要素をループしました。次に、「Name」属性の値を比較してフィールドを判別し、「Value」属性の値を割り当てました。モデル内の対応するプロパティに。

少し余分なコードと、割り当てを処理するための22のケースを持つ素敵な小さなswitchステートメント...

やりたかった方法ではありませんが、少なくとも時間どおりにサービスを提供し、後でこれを行うためのより良い方法があるかどうかを確認することができます...

完全を期すために、ここで私が変更したものを示します。

public class Bug
{
    public int ID { get; set; }

    public DateTime ChangedDate { get; set; }

    public string State { get; set; }
    public string AreaPath { get; set; }
    public string Title { get; set; }
    public string Status { get; set; }
    public string SubStatus { get; set; }
    public string OpenedBy { get; set; }
    public string ChangedBy { get; set; }
    public DateTime OpenedDate { get; set; }
    public DateTime ResolvedDate { get; set; }
    public string AssignedTo { get; set; }
    public string IssueType { get; set; }
    public string IssueSubtype { get; set; }
    public string Priority { get; set; }
    public string Severity { get; set; }
    public string Keywords { get; set; }
    public string ScreenID { get; set; }
    public string ResolvedBy { get; set; }
    public string Resolution { get; set; }
    public string ReproSteps { get; set; }
    public string HowFound { get; set; }
    public string FullHistory { get; set; }
    public string EverChangedBy { get; set; }


}

かつての逆シリアル化方法は次のとおりです。

internal static Bug Deserialize(string filePath)
    {
        XDocument doc = XDocument.Load(filePath);
        Bug bug = new Bug();

        bug.ID = int.Parse(doc.Root.Element("ID").Value);
        bug.ChangedDate = DateTime.Parse(doc.Root.Element("ChangedDate").Value);

        foreach (var el in doc.Root.Element("Fields").Elements())
        {
            XAttribute fieldName = el.Attributes("Name").Single();
            XAttribute fieldValue = el.Attributes("Value").Single();

            switch (fieldName.Value.ToString())
            {
                case "State":
                    bug.State = fieldValue.Value.ToString();
                    break;
                case "Area Path":
                    bug.AreaPath = fieldValue.Value.ToString();
                    break;
                case "Title":
                    bug.Title = fieldValue.Value.ToString();
                    break;
                case "Status":
                    bug.Status = fieldValue.Value.ToString();
                    break;
                case "SubStatus":
                    bug.SubStatus = fieldValue.Value.ToString();
                    break;
                case "Opened By":
                    bug.OpenedBy = fieldValue.Value.ToString();
                    break;
                case "ChangedBy":
                    bug.ChangedBy = fieldValue.Value.ToString();
                    break;
                case "Opened Date":
                    bug.OpenedDate = DateTime.Parse(fieldValue.Value.ToString());
                    break;
                case "Resolved Date":
                    bug.ResolvedDate = DateTime.Parse(fieldValue.Value.ToString());
                    break;
                case "Assigned To":
                    bug.AssignedTo = fieldValue.Value.ToString();
                    break;
                case "Issue Type":
                    bug.IssueType = fieldValue.Value.ToString();
                    break;
                case "Issue Subtype":
                    bug.IssueSubtype = fieldValue.Value.ToString();
                    break;
                case "Priority":
                    bug.Priority = fieldValue.Value.ToString();
                    break;
                case "Severity":
                    bug.Severity = fieldValue.Value.ToString();
                    break;
                case "Keywords":
                    bug.Keywords = fieldValue.Value.ToString();
                    break;
                case "ScreenID":
                    bug.ScreenID = fieldValue.Value.ToString();
                    break;
                case "ResolvedBy":
                    bug.ResolvedBy = fieldValue.Value.ToString();
                    break;
                case "Resolution":
                    bug.Resolution = fieldValue.Value.ToString();
                    break;
                case "ReproSteps":
                    bug.State = fieldValue.Value.ToString();
                    break;
                case "HowFound":
                    bug.State = fieldValue.Value.ToString();
                    break;
                case "FullHistory":
                    bug.State = fieldValue.Value.ToString();
                    break;
                case "EverChangedBy":
                    bug.State = fieldValue.Value.ToString();
                    break;
            }
        }

        return bug;
    }

これにより、次の呼び出しを使用してLINQクエリを構築できました。

foreach (KeyValuePair<string, object> field in queryFields)
        {
            itemList = itemList.Where<Bug>(field.Key, field.Value, (FilterOperation)StringEnum.Parse(typeof(FilterOperation), "eq"));                
        }

乾杯!

于 2013-03-26T21:04:46.580 に答える