| William さんのプロフィール.Net Zoneブログリスト | ヘルプ |
|
11月16日 Return total count of query plus paging with Data Services...And in the same reply. I racked my brain for a few days trying to figure out a way to return the count of a query in the same reply with the results for my client paging control. I started with making two queries, one for the count and one for the records. There is a couple of issues with that. First, your sending two queries. Second, before you get the results of the second query, your count has probably changed. On a CRUD table, count is a point in time anyway, so you can't "count" on it being accurate. The other issue was I had to maintain two queries, one for the count and one for the results; which was difficult to maintain, so using a Batch was not really helping. It finally occured to me that I might be able to stuff the count in the HTTP reply header. As it turned out, it worked, and worked pretty well. I was able to send one query and get results and the count in same reply. Service WebGet method/// <summary> /// Returns the entries using the predicate. /// The total count of records matching the predicate is returned in /// Http header "EntryCount". /// </summary> /// <param name="predicate"></param> /// <param name="skip"></param> /// <param name="take"></param> /// <returns></returns> [WebGet] public IEnumerable<Entries> GetEntries(string predicate, string orderKeys, int skip, int take) { // TODO: Security check. if (orderKeys == null || orderKeys.Length == 0) throw new DataServiceException("orderKeys required."); // Get the records using skip and take. IEnumerable<Entries> q = null; int count = 0; if (predicate == null || predicate.Length == 0) { q = this.CurrentDataSource.Entries.OrderBy(orderKeys).Skip(skip).Take(take); count = this.CurrentDataSource.Entries.Count(); } else { q = this.CurrentDataSource.Entries.Where(predicate).OrderBy(orderKeys).Skip(skip).Take(take); count = this.CurrentDataSource.Entries.Where(predicate).Count(); } // Get the count all records matching predicate and add to HTTP header. HttpContext.Current.Response.AppendHeader("EntryCount", count.ToString()); return q; }
Client side Proxy method/// <summary> /// Get entries using a string predicate and read the total count in the reply header. /// </summary> /// <param name="predicate"></param> /// <param name="skip"></param> /// <param name="take"></param> /// <returns></returns> public IEnumerable<Entries> GetEntries(string predicate, string orderBy, int skip, int take, out int count) { // Why this work around? We have to pass an eSql string for the predicate instead of composing over IQueryable, // because at the server side we need to "know" the full predicate in order to get the count. // This also allows us to use the same query for the count and the select. if (predicate == null) predicate = ""; // Empty predicate on server side equals all rows. if (orderBy == null) throw new ArgumentNullException("orderKeys"); // order clause required. predicate = predicate.Replace("'", "''"); // Must add another single quote as these will be inside a single quote pair. string request = string.Format("GetEntries?predicate='{0}'&orderKeys='{1}'&skip={2}&take={3}", predicate, orderBy, skip, take); request = Uri.EscapeUriString(request); // Escape spaces and other. var q = this.Execute<Entries>(request.ToRelativeUri()); // Have to Execute (not CreateQuery) so we can inspect the header here. // Get full record count in header. var qReply = (QueryOperationResponse)q; // Downcast to get the Headers in the reply. Debug.WriteLine("Request URI: " + qReply.Query.RequestUri); string cs = qReply.Headers["EntryCount"]; count = int.Parse(cs); return q; }
TestOne down side here is that we have to send our query as an entity Sql string, so we don't get the help of linq expressions or designer support. If there was a way to get the user's query inside a service operation, we could use that query and append .Count() to it. TMK, there is not a way to do that with Astoria. If anyone has ideas on that, please let me know. // Get all entries > 1. Take 10 and skip 0. The count of all entries > 1 will be returned so you can set your paging buttons (i.e. Next, Prev) as needed. int count; 11月13日 Back to CSV - Convert CSV text to Objects; via JSONAs Json is easier to read and write then Xml. It follows that CSV (comma seperated values) is easier to read and write then Json. CSV also has tools such as Excel and others that make it easy to work with and create. So if you ever want to create a config or data file for your next app, here is some code to convert CSV to JSON to POCO objects. Control Methodpublic void ConvertCsvToObjects() { string csv = File.ReadAllText(@"c:\temp\mycsv.txt"); /* mycsv.txt - Note the doube quotes around strings are required by json. ID, Name, Price 1, "ABC", 1200.00 2, "Zip Co.", 1400 3, "Contoso", 5000 */ // Step 1: Reshape csv to a json array format. string json = csv.CsvToJson(); Console.WriteLine(json); /* JSON text [ {ID: 1, Name: "ABC", Price: 1200.00}, {ID: 2, Name: "Zip Co.", Price: 1400}, {ID: 3, Name: "Contoso", Price: 5000} ] */ // Step 2: Convert json to objects. List<Customer> customers = json.FromJson<List<Customer>>(); foreach(var cust in customers) Console.WriteLine("{0}\t{1}\t{2:c}", cust.ID, cust.Name, cust.Price); /* Output. 1 ABC $1,200.00 2 Zip Co. $1,400.00 3 Contoso $5,000.00 */ } public class Customer { public int ID {get;set;} public string Name { get; set; } public decimal Price { get; set; } } CsvToJson extention method/// <summary> /// Converts a CSV string to a Json array format. /// </summary> /// <remarks>First line in CSV must be a header with field name columns.</remarks> /// <param name="value"></param> /// <returns></returns> public static string CsvToJson(this string value) { // Get lines. if (value == null) return null; string[] lines = value.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); if (lines.Length < 2) throw new InvalidDataException("Must have header line."); // Get headers. string[] headers = lines.First().SplitQuotedLine(new char[] { ',' }, false); // Build JSON array. StringBuilder sb = new StringBuilder(); sb.AppendLine("["); for (int i = 1; i < lines.Length; i++) { string[] fields = lines[i].SplitQuotedLine(new char[] { ',', ' ' }, true, '"', false); if (fields.Length != headers.Length) throw new InvalidDataException("Field count must match header count."); var jsonElements = headers.Zip(fields, (header, field) => string.Format("{0}: {1}", header, field)).ToArray(); string jsonObject = "{" + string.Format("{0}", string.Join(",", jsonElements)) + "}"; if (i < lines.Length - 1) jsonObject += ","; sb.AppendLine(jsonObject); } sb.AppendLine("]"); return sb.ToString(); } FromJson extention methodpublic static T FromJson<T>(this string json) { JavaScriptSerializer ser = new JavaScriptSerializer(); return ser.Deserialize<T>(json); } |
|
|