William さんのプロフィール.Net Zoneブログリスト ツール ヘルプ
10月27日

Social Rest

Almost complete with my new social service codenamed "Social Rest".  Still needs a name.

image

10月19日

LINQ-a-fi your file system and create a neat little Grep tool

After watching this Channel9 video http://channel9.msdn.com/shows/Going+Deep/Erik-Meijer-and-Bart-De-Smet-LINQ-to-Anything/

I was inspired to try out writing my own domain specific language (DSL) for the file system.  I started with writing my own Where and Select in a class, but soon discovered I did not need that.  All a really needed was a method to return IEnumerable and an abstraction to make using FileSystemInfo a bit easier to use with linq.  So I created LFileInfo (Linq FileInfo) for lack of a better name, and a StreamIndexer as a simple wrapper around a StreamReader to give me an Indexer (i.e. char c = file[12]) over a file.  The indexer was not really required and has nothing to do with linq-ing my LFileInfo class, but it make searching the file easier in Like() algo.  In effect, what we have here is another way to Grep (e.g. search) using Linq.  So we created our own DSL without writing any parser code or coming up with some new syntax.  We leverage the power of linq to build something fairly useful.

Sample usage of LFileInfo below. Other Test methods given in the LFileInfo class:

Console.WriteLine("\nGet number of files and total size under a dir.");
long totSize = 0;
long totFiles = 0;
var q = (from f in new LFileInfo(@"c:\temp").Children(true)
         let x = totSize += f.Length
         let y = totFiles++
         select f);

var list = q.ToList(); // Run the query.

Console.WriteLine("Total Size:{0:N} Total Files: {1}", totSize, totFiles);

 

// LFileInfo. Wraps a FileSystemInfo object to allow us to more simply "linq-a-fi" directory operations.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;

namespace WJS.Utils
{
    public class LFileInfo
    {
        private FileSystemInfo fi;
        private int level;
        private LFileInfo parent;
        private List<LFileInfo> children;

        public LFileInfo(string path)
        {
            if ((File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory)
                fi = new DirectoryInfo(path);
            else
                fi = new FileInfo(path);
        }

        private LFileInfo(FileSystemInfo item)
        {
            fi = item;
        }
        public LFileInfo Parent
        {
            get { return this.parent; }
        }
        public FileSystemInfo FileSystemInfo
        {
            get { return this.fi; }
        }

        IEnumerable<LFileInfo> Children()
        {
            foreach (var c in Children(false))
                yield return c;
        }

        IEnumerable<LFileInfo> Children(bool recurse)
        {
            if (!this.IsDirectory)
                yield break;

            if (this.children != null)
            {
                // enum from from cache.
                foreach (var f in this.children)
                {
                    yield return f;
                    if (recurse && f.IsDirectory)
                    {
                        foreach (var l in f.Children(true))
                            yield return l;
                    }
                }
                yield break;
            }
            else
            {
                // Build cache from disk. Note, this is still lazy. We only
                // continue searching FAT, after yielding. GetFileSystemInfos()
                // is not lazy, so a directory read returns all fileinfos in that dir.
                this.children = new List<LFileInfo>();
                DirectoryInfo di = (DirectoryInfo)fi;
                //if (di.FullName.StartsWith(@"c:\temp\system", StringComparison.OrdinalIgnoreCase))
                //    Debugger.Break();
                var infos = di.GetFileSystemInfos();
                foreach (var f in infos)
                {
                    LFileInfo lfi = new LFileInfo(f);
                    lfi.level = this.level + 1;
                    lfi.parent = this;
                    this.children.Add(lfi);
                    yield return lfi;
                    if (recurse && f is DirectoryInfo)
                    {
                        DirectoryInfo di2 = (DirectoryInfo)f;
                        if (di2 != null)
                        {
                            LFileInfo nfi = new LFileInfo(di2);
                            nfi.level = this.level + 1;
                            nfi.parent = this;
                            var children = nfi.Children(recurse);
                            foreach (var f2 in children)
                                yield return f2;
                        }
                    }
                }
            }
        }

        public string Name
        {
            get { return fi.Name; }
        }

        public string Extension
        {
            get { return fi.Extension; }
        }

        public string FullName
        {
            get { return fi.FullName; }
        }

        public int Level
        {
            get { return this.level; }
        }

        public long Length
        {
            get
            {
                if (IsDirectory)
                    return 0;
                return ((FileInfo)fi).Length;
            }
        }

        public bool IsFile
        {
            get { return !IsDirectory; }
        }

        public bool IsDirectory
        {
            get
            {
                return (fi is DirectoryInfo);
            }
        }

        private bool CanRead
        {
            get
            {
                try
                {
                    using (var f = File.OpenRead(fi.FullName))
                    {
                        return true;
                    }
                }
                catch
                {
                    return false;
                }
            }
        }

        public string ReadAllText()
        {
            if (IsFile)
                return File.ReadAllText(fi.FullName);
            else
                return null;
        }

        public string[] ReadAllLines()
        {
            if (IsFile)
                return File.ReadAllLines(fi.FullName);
            else
                return null;
        }

        public byte[] ReadAllBytes()
        {
            if (IsFile)
                return File.ReadAllBytes(fi.FullName);
            else
                return null;
        }

        public bool Like(string pattern, bool caseSensitive)
        {
            StreamIndexer si = new StreamIndexer(this.fi.FullName);
            int matchIndex = 0;
            bool result = Like(si, pattern, caseSensitive, ref matchIndex);
            return result;
        }

        /// <summary>
        /// Uses a stream wrapper that exposes an iterator to allow reading a file by index.
        /// </summary>
        private static bool Like(StreamIndexer input, string pattern, bool caseSensitive, ref int startIndex)
        {
            if (input == null) throw new ArgumentNullException("input");
            if (pattern == null)
                return false;

            // Setup case function.
            Func<char, char, bool> equal;
            if (caseSensitive)
                equal = (char c1, char c2) =>
                {
                    bool r = c1 == c2;
                    return r;
                };
            else
                equal = (char c1, char c2) =>
                {
                    bool r = char.ToLower(c1) == char.ToLower(c2);
                    return r;
                };

            // Do pattern matching.
            int i = 0;
            int j = 0;
            while (i < input.Length && j < pattern.Length && pattern[j] != '*')
            {
                if ( (!equal(pattern[j], (char)input[i])) && (!equal(pattern[j], '?')) )
                {
                    return false;
                }
                i++;
                j++;
            }

            // If we have reached the end of the pattern without finding a wildcard,
            // the match must fail if the string is longer or shorter than the pattern.
            if (j == pattern.Length)
                return input.Length == pattern.Length;

            int cp = 0;
            int mp = 0;
            while (i < input.Length)
            {
                if (j < pattern.Length && pattern[j] == '*')
                {
                    if (++j >= pattern.Length) //Nothing more in pattern so we match.
                    {
                        startIndex = i-1;
                        return true;
                    }
                    mp = j;
                    cp = i + 1;
                }
                else if (j < pattern.Length && ( equal(pattern[j], (char)input[i]) || pattern[j] == '?'))
                {
                    j++;
                    i++;
                }
                else
                {
                    j = mp;
                    i = cp++;
                }
            }

            while (j < pattern.Length && pattern[j] == '*')
            {
                j++;
            }

            startIndex = i;
            return j >= pattern.Length;
        }
        public static int IndexOfAny(string source, string ofAny)
        {
            if (source == null || ofAny == null) return 0;
            for(int i=0; i<source.Length; i++)
            {
                char c = source[i];
                foreach (char c2 in ofAny)
                {
                    if (c == c2) return i;
                }
            }
            return -1;
        }

        /// <summary>
        /// Return true if input string matches pattern.
        /// </summary>
        public static bool Like(string input, string pattern, bool caseSensitive)
        {
            if (pattern == null && input == null)
                return true;
            if (pattern == null || input == null)
                return false;

            // Convert both string and pattern to lower case for comparison.
            if (!caseSensitive)
            {
                pattern = pattern.ToLower();
                input = input.ToLower();
            }

            // If pattern doesn't actually contain any wildcards, use simple equality.
            if (IndexOfAny(pattern, "*?") == -1)
                return input == pattern;

            // Otherwise do pattern matching.
            int i = 0;
            int j = 0;
            while (i < input.Length && j < pattern.Length && pattern[j] != '*')
            {
                if ((pattern[j] != input[i]) && (pattern[j] != '?'))
                {
                    return false;
                }
                i++;
                j++;
            }

            // If we have reached the end of the pattern without finding a wildcard,
            // the match must fail if the string is longer or shorter than the pattern.
            if (j == pattern.Length)
                return input.Length == pattern.Length;

            int cp = 0;
            int mp = 0;
            while (i < input.Length)
            {
                if (j < pattern.Length && pattern[j] == '*')
                {
                    if (++j >= pattern.Length)
                    {
                        return true;
                    }
                    mp = j;
                    cp = i + 1;
                }
                else if (j < pattern.Length && (pattern[j] == input[i] || pattern[j] == '?'))
                {
                    j++;
                    i++;
                }
                else
                {
                    j = mp;
                    i = cp++;
                }
            }

            while (j < pattern.Length && pattern[j] == '*')
            {
                j++;
            }

            return j >= pattern.Length;
        }

        // Some sample tests. Remove Test methods from final code.
        public static void Test1()
        {
            // Get all directories under a root.
            Console.WriteLine("\nAll directories under a root.");
            var q = from f in new LFileInfo(@"c:\temp").Children()
                    where (f.IsDirectory)
                    select f;
            foreach(var f in q)
                Console.WriteLine(f.FullName);
        }

        public static void Test2()
        {
            // Get directories that don't have any files. The power of linq really shows here.
            Console.WriteLine("\nAll directories with no files.");
            var q = (from f in new LFileInfo(@"c:\temp").Children(true)
                    where f.IsDirectory && !f.Children().Any(e=>e.IsFile)
                    select f);

            foreach(var f in q)
                Console.WriteLine("{0} {1} Level:{2}", f.IsDirectory, f.FullName, f.Level);
        }

        public static void Test3()
        {
            // Get number of files and total size under a dir.
            Console.WriteLine("\nGet number of files and total size under a dir.");
            string dir = @"c:\temp";
            long totSize = 0;
            long totFiles = 0;
            var q = (from f in new LFileInfo(dir).Children(true)
                     let x = totSize += f.Length
                     let y = totFiles++
                     orderby f.Name, f.IsFile, f.Level
                     select f);

            foreach (var f in q)
                Console.WriteLine("{0} {1} Level:{2}", f.IsDirectory, f.FullName, f.Level);

            Console.WriteLine("Total Size:{0:N} Total Files: {1}", totSize, totFiles);
        }
        public static void Test5()
        {
            Console.WriteLine("\nAll files over 1MB:");
            var q = from f in new LFileInfo(@"c:\temp").Children(true)
                    where f.Length > 1024 * 1024
                    select f;
            foreach(var f in q)
                Console.WriteLine("{0}\t\t\tLen: {1:N}", f.FullName, f.Length);
        }

        // Print a dir tree to the console.
        public static void Test6(string path)
        {
            //string currDir = System.AppDomain.CurrentDomain.BaseDirectory;
            //string pDir = new DirectoryInfo(currDir).Parent.Parent.FullName;

            LFileInfo dir = new LFileInfo(path);
            PrintTree(dir);
        }

        // Use Like pattern matching to search files.
        public static void Test7()
        {          
            var q = from f in new LFileInfo(@"c:\temp").Children(true)
                    where LFileInfo.Like(f.Name, "*.txt", false)
                    where f.Like("* where *", true)
                    select f;

            var list = q.ToList();
            Console.WriteLine("\nFile matching pattern: {0}", list.Count());
            foreach (var f in list)
                Console.WriteLine("{0} Ext:{1}", f.Name, f.Extension);
        }

        /// <summary>
        /// Print a dir tree starting from entry.
        /// </summary>
        private static void PrintTree(LFileInfo entry)
        {
            string pad = "";
            if (entry.Level > 0) pad = new string('.', (entry.Level) * 2);
            if (entry.IsDirectory) pad += "+";
            Console.WriteLine(pad + entry.Name);

            if (entry.IsDirectory)
            {
                var children = entry.Children().OrderBy(e => e.IsDirectory);
                foreach (LFileInfo lf in children)
                    PrintTree(lf);
            }
        }
    }
}

 

// StreamIndexer class to wrap a StreamReader and expose an indexer.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace WJS.Utils
{
    public class StreamIndexer
    {
        private StreamReader sr;
        public StreamIndexer(string path)
        {
            sr = new StreamReader(path);
        }

        public long Length
        {
            get { return sr.BaseStream.Length; }
        }

        public void Close()
        {
            sr.Close();
        }

        public IEnumerable<Char> Characters()
        {
            int ch = 0;
            while ((ch = sr.Read()) >= 0)
            {
                yield return (char)ch;
            }
        }

        public int this[int index]
        {
            get
            {
                sr.BaseStream.Seek(index, SeekOrigin.Begin);
                return sr.Read();
            }
        }
    }
}

10月9日

Fiddler with Ado.Net Data Services

To get Fiddler 2.1.8.2 to work with Visual Studio Development server and using Ado.Net Rest services, I found only 1 thing that actually works:

1) Add a new host to your \etc\hosts file:

127.0.0.1 local

The name is not important, but local works. localhost does not work for some reason with Fiddler.

2) Set Fiddler to filter on host name "local".  Just to filter out public traffic on your machine.

3) Change your client library URL to use the new URL.  If you have Application settings in the client project, change the ServiceUri. If you use Client Application services, also change those locations in the application Properties / Services tab.  Use your service name below.

Example: http://local:7777/RestService.svc/

4) Change your Rest service's web.config to listen on "local" name. Use Specific Port instead of Auto-assign port in Web tab of application Properties.

<services>
  <service name="MyRestService">
    <endpoint address="http://local:7777/RestService.svc/" binding="webHttpBinding" bindingConfiguration="higherMessageSize" contract="System.Data.Services.IRequestHandler">
    </endpoint>
  </service>
</services>

5) Verify service starts and listens on URL from a brower and point to http://local:7777/RestService.svc/ to see the service meta data.

Other solutions on the net such as adding a dot to localhost http://localhost.:7777/Service.svc/  Do not seem to work for me (at least on Vista).

The solution above works from .Net client app and IE.