{"id":359,"date":"2007-12-13T12:32:47","date_gmt":"2007-12-13T11:32:47","guid":{"rendered":"http:\/\/soci.hu\/blog\/index.php\/2007\/12\/13\/sql-server-2008-ujdonsagok-5-streaming-adatok-kezelese-kliens-oldalrol\/"},"modified":"2008-07-21T11:08:09","modified_gmt":"2008-07-21T10:08:09","slug":"sql-server-2008-ujdonsagok-5-streaming-adatok-kezelese-kliens-oldalrol","status":"publish","type":"post","link":"https:\/\/soci.hu\/blog\/index.php\/2007\/12\/13\/sql-server-2008-ujdonsagok-5-streaming-adatok-kezelese-kliens-oldalrol\/","title":{"rendered":"SQL Server 2008 \u00fajdons\u00e1gok 5. &#8211; streaming adatok kezel\u00e9se kliensoldalr\u00f3l"},"content":{"rendered":"<p>Az <a href=\"http:\/\/soci.hu\/blog\/index.php\/2007\/12\/12\/sql-server-2008-ujdonsagok-4-streaming-adatok-tarolasa\/\">el\u0151z\u0151 r\u00e9szben<\/a> elk\u00e9sz\u00fclt a t\u00e1bl\u00e1nk, ami streaming adatokat tud t\u00e1rolni. Itt az ideje p\u00e1r fot\u00f3t belesz\u00f3rni az adatb\u00e1zisba (illetve a f\u00e1jlrendszerbe).<\/p>\n<p>Az adatokat bek\u00fcldhetj\u00fck SQL parancsk\u00e9nt is, a megszokott TDS csatorn\u00e1t felhaszn\u00e1lva. Azonban pont az\u00e9rt rakt\u00e1k ki filerendszerbe ezeket a nagy adatokat, hogy NE TDS-en kereszt\u00fcl, hanem SMB-vel, a windows file megoszt\u00e1s\u00e1n kereszt\u00fcl \u00e9rj\u00fck el \u0151ket. Ez kicsit szokatlan lesz, hisz lesz ugyan SQL INSERT, de lesz sima f\u00e1jlkezel\u00e9s is a megold\u00e1sban. Mondom, menne sima SQL-lel is, de az nem lenne t\u00fal hat\u00e9kony.<\/p>\n<p>No, a megold\u00e1s el\u00e9g bizarr lesz. Nincs ugyanis managed fel\u00fclet az adatok kezel\u00e9s\u00e9re! Legal\u00e1bbis most, 2007 v\u00e9g\u00e9n nincs. Nat\u00edv API van, azt lehet rugdosni .NET-b\u0151l, interopon kereszt\u00fcl, ha kell.<\/p>\n<p>A megold\u00e1s menete v\u00e1zlatosan a k\u00f6vetkez\u0151.<br \/>\n1. Hozz\u00e1kapcsol\u00f3dunk a szerverhez SqlConnectionnel, tranzakci\u00f3t ind\u00edtunk, hozz\u00e1rendelj\u00fck a haszn\u00e1land\u00f3 SqlCommandunkhoz. Eddig semmi \u00faj.<\/p>\n<p>2. Besz\u00farjuk a stream metaadatait, a sima adatokat, egy insert-tel, hagyom\u00e1nyos m\u00f3don, ADO.NET-tel. <\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\ninsert into Kepek (Id, Name, Photo) values (@Id, @Name, cast (&#039;&#039; as varbinary(max)))\r\n<\/pre>\n<p>A furcsa \u00fcres stringet kasztol\u00f3 iz\u00e9 az\u00e9rt van a parancsban, hogy meg\u00e1gyazzunk az adatoknak a filerendszerben. E n\u00e9lk\u00fcl, ha null maradna az oszlop \u00e9rt\u00e9ke nem j\u00f6nne l\u00e9tre f\u00e1jl a diszken, \u00edgy a k\u00f6vetkez\u0151 l\u00e9p\u00e9s se menne.<\/p>\n<p>3. Visszaolvassuk a sorunkhoz tartoz\u00f3 stream mint f\u00e1jl el\u00e9r\u00e9si \u00fatj\u00e1t \u00e9s egy tranzakci\u00f3 azonos\u00edt\u00f3t, ez kell majd a k\u00f6vetkez\u0151 l\u00e9p\u00e9sben. Mindkett\u0151re van egy \u00faj f\u00fcggv\u00e9ny illetve met\u00f3dus (kiemelve).<\/p>\n<pre class=\"brush: sql; title: ; notranslate\" title=\"\">\r\nselect Photo.&lt;strong&gt;PathName()&lt;\/strong&gt; PathName, &lt;strong&gt;get_filestream_transaction_context()&lt;\/strong&gt; TranCtx from Kepek where Id = @Id\r\n<\/pre>\n<p>4. Most j\u00f6n a bizarrabb r\u00e9sz. Kapunk egy nat\u00edv f\u00fcggv\u00e9nyt, azzal lehet megnyitni trazakcion\u00e1lisan a stream\u00fcnket mint f\u00e1jlt: OpenSqlFilestream. Mivel \u0151 nat\u00edv cucc, kell hozz\u00e1 interop deklar\u00e1ci\u00f3, kb. \u00edgy:<\/p>\n<p>[DllImport(&#8220;sqlncli10.dll&#8221;, SetLastError = true, CharSet = CharSet.Unicode)]<br \/>\npublic static extern SafeFileHandle OpenSqlFilestream(<br \/>\n    string FilestreamPath,<br \/>\n    UInt32 DesiredAccess,<br \/>\n    UInt32 OpenOptions,<br \/>\n    byte[] FilestreamTransactionContext,<br \/>\n    UInt32 FilestreamTransactionContextLength,<br \/>\n    LARGE_INTEGER_SQL AllocationSize<br \/>\n    );<\/p>\n<p>[StructLayout(LayoutKind.Sequential)]<br \/>\npublic struct LARGE_INTEGER_SQL<br \/>\n{<br \/>\n    public Int64 QuadPart;<br \/>\n    public LARGE_INTEGER_SQL(Int64 quadPart) { QuadPart = quadPart; }<br \/>\n}<\/p>\n<p>Ronda, de az interop m\u00e1r csak ilyen. \u00d6r\u00fclj\u00fcnk, hogy m\u0171k\u00f6dik. Vagy tess\u00e9k haszn\u00e1lni C++-t.<\/p>\n<p>A param\u00e9terek \u00e9rt\u00e9k\u00e9t az el\u0151z\u0151 pont select-je szolg\u00e1ltatja, amit egy reader-rel \u00e9rek el:<\/p>\n<p>byte[] tranCtx = (byte[])reader[&#8220;TranCtx&#8221;];<\/p>\n<p>SafeFileHandle h = OpenSqlFilestream(<br \/>\n(string)reader[&#8220;PathName&#8221;],<br \/>\nDESIRED_ACCESS_WRITE,<br \/>\n0,<br \/>\ntranCtx,<br \/>\n(uint)tranCtx.Length,<br \/>\nnew LARGE_INTEGER_SQL(0))<\/p>\n<p>Kapunk egy sz\u00e9p Handle-t, amit az interop r\u00e9teg egyb\u0151l be is csomagol egy SafeFileHandle-be, mert azt \u00fagy illik (aki nem hallott a safehandle-\u00f6kr\u0151l, most \u00e1lljon meg, \u00e9s <a href=\"http:\/\/blogs.msdn.com\/bclteam\/archive\/2005\/03\/16\/396900.aspx\">s\u00fcrg\u0151sen olvasson ut\u00e1na<\/a>, nem hossz\u00fa a cikk).<\/p>\n<p>5. A Win32 handle j\u00f3 dolog, de nem kezd\u00fcnk neki WriteFile API-val baromkodni, hanem kihaszn\u00e1ljuk, hogy a FileStream k\u00f6nnyen \u00f6sszebar\u00e1tkozik valahonnan szerzett File Handle-\u00f6kkel:<\/p>\n<p>FileStream fsWrite = new FileStream(h, FileAccess.Write)<\/p>\n<p>6. Most m\u00e1r csak \u00edrni kell b\u0151szen az fsWrite-ba. Figyelj\u00fck meg, hogy ez a l\u00e9nyege pont a streaming el\u00e9r\u00e9snek, hogy nem \u00f6sszerakunk egy 34 GByte-os ojjektumot, mondjuk byte[]-\u00f6t, \u00e9s odav\u00e1gjuk a szervenek (eleve, a kliens belehalna ebbe), hanem apr\u00e1nk\u00e9nt lap\u00e1toljuk be az adatokat. R\u00e1\u00e9r\u0151sen, \u00f6regesen. Valahogy \u00edgy pl.<\/p>\n<p>int readed;<br \/>\nbyte[] buff = new byte[4096];<br \/>\ndo<br \/>\n{<br \/>\n    readed = fsRead.Read(buff, 0, buff.Length);<br \/>\n    fsWrite.Write(buff, 0, readed);<br \/>\n}<br \/>\nwhile (readed > 0);<\/p>\n<p>Az fsRead a helyi k\u00e9pre van megnyitva, amit betolunk a szervernek. Teccik l\u00e1tni, kicsi kis darabk\u00e1kban megy be a hatalmas k\u00e9p.<\/p>\n<p>Sz\u00e1mos k\u00e9rd\u00e9s, mint izol\u00e1ci\u00f3s szint, mi l\u00e1tszik ebb\u0151l a f\u00e1jlrendszerben, kihaszn\u00e1lja-e a tranzakcion\u00e1lis NTFS-t, stb. mer\u00fcl m\u00e9g fel, ezekkel a k\u00f6vetkez\u0151 r\u00e9szben foglalkozok.<\/p>\n<p>Z\u00e1r\u00e1sul berakom a teljes k\u00f3dot, \u00e9lvezz\u00e9tek. :)<\/p>\n<p>(Tud valaki valami norm\u00e1lis, k\u00e9sz, sz\u00e9p wordpress theme-et, ami \u00fagy van bel\u0151ve, hogy olyan sz\u00e9lesen form\u00e1zza meg a sz\u00f6veget, mint amekkora az ablak m\u00e9rete? Elkezdtem \u00e1tszabni ezt a Kubrick theme-et, de ez hal\u00e1l,  nekem bonyolult meg\u00e9rteni \u00e9s \u00e1t\u00edrni. A k\u00f3dok miatt kellene.)<\/p>\n<p>using System;<br \/>\nusing System.Data.SqlClient;<br \/>\nusing System.IO;<br \/>\nusing System.Data;<br \/>\nusing System.Runtime.InteropServices;<br \/>\nusing Microsoft.Win32.SafeHandles;<\/p>\n<p>namespace FSTest<br \/>\n{<br \/>\n    class Program<br \/>\n    {<br \/>\n        static void Main(string[] args)<br \/>\n        {<br \/>\n            SqlTransaction tran = null;<br \/>\n            try<br \/>\n            {<br \/>\n                using (SqlConnection conn = new SqlConnection(<br \/>\n                    @&#8221;Data Source=.\\sql2008;Initial Catalog=FSTeszt;Integrated Security=true;&#8221;))<br \/>\n                {<br \/>\n                    using (SqlCommand cmd = new SqlCommand())<br \/>\n                    {<br \/>\n                        conn.Open();<br \/>\n                        tran = conn.BeginTransaction();<br \/>\n                        cmd.Connection = conn;<br \/>\n                        cmd.Transaction = tran;<\/p>\n<p>                        foreach (string f in Directory.GetFiles(@&#8221;E:\\ment\\kepek\\Hivatni&#8221;, &#8220;*.jpg&#8221;))<br \/>\n                        {<br \/>\n                            \/\/K\u00e9p alapadatok besz\u00far\u00e1sa<br \/>\n                            cmd.CommandText = @&#8221;insert into Kepek (Id, Name, Photo)<br \/>\n                                values (@Id, @Name, CAST (&#8221; as varbinary(max)))&#8221;;<br \/>\n                            cmd.Parameters.Clear();<br \/>\n                            cmd.Parameters.Add(&#8220;@Name&#8221;, SqlDbType.NVarChar).Value = Path.GetFileName(f);<br \/>\n                            Guid id = Guid.NewGuid();<br \/>\n                            cmd.Parameters.Add(&#8220;@Id&#8221;, SqlDbType.UniqueIdentifier).Value = id;<br \/>\n                            cmd.ExecuteNonQuery();<\/p>\n<p>                            \/\/Maga a k\u00e9p ment\u00e9se mint streaming adat<br \/>\n                            cmd.CommandText = @&#8221;select Photo.PathName() PathName,<br \/>\n                                get_filestream_transaction_context() TranCtx from Kepek where Id = @Id&#8221;;<br \/>\n                            using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow))<br \/>\n                            {<br \/>\n                                if (reader.Read())<br \/>\n                                {<br \/>\n                                    byte[] tranCtx = (byte[])reader[&#8220;TranCtx&#8221;];<\/p>\n<p>                                    using (SafeFileHandle h = OpenSqlFilestream(<br \/>\n                                        (string)reader[&#8220;PathName&#8221;],<br \/>\n                                        DESIRED_ACCESS_WRITE,<br \/>\n                                        0,<br \/>\n                                        tranCtx,<br \/>\n                                        (uint)tranCtx.Length,<br \/>\n                                        new LARGE_INTEGER_SQL(0)))<br \/>\n                                    {<\/p>\n<p>                                        if (!h.IsInvalid)<br \/>\n                                        {<br \/>\n                                            using (FileStream fsWrite = new FileStream(h, FileAccess.Write))<br \/>\n                                            using (FileStream fsRead = File.OpenRead(f))<br \/>\n                                            {<br \/>\n                                                int readed;<br \/>\n                                                byte[] buff = new byte[4096];<br \/>\n                                                do<br \/>\n                                                {<br \/>\n                                                    readed = fsRead.Read(buff, 0, buff.Length);<br \/>\n                                                    fsWrite.Write(buff, 0, readed);<br \/>\n                                                }<br \/>\n                                                while (readed > 0);<br \/>\n                                            }<br \/>\n                                        }<br \/>\n                                        else<br \/>\n                                        {<br \/>\n                                            int errno = Marshal.GetLastWin32Error();<br \/>\n                                            Console.WriteLine(errno);<br \/>\n                                            Environment.Exit(-1);<br \/>\n                                        }<br \/>\n                                    }<br \/>\n                                }<br \/>\n                            }<\/p>\n<p>                        }<br \/>\n                    }<br \/>\n                    tran.Commit();<br \/>\n                }<br \/>\n            }<br \/>\n            catch (Exception e)<br \/>\n            {<br \/>\n                tran.Rollback();<br \/>\n                Console.WriteLine(e);<br \/>\n            }<br \/>\n        }<\/p>\n<p>        public const UInt32 DESIRED_ACCESS_READ = 0x00000000;<br \/>\n        public const UInt32 DESIRED_ACCESS_WRITE = 0x00000001;<br \/>\n        public const UInt32 DESIRED_ACCESS_READWRITE = 0x00000002;<\/p>\n<p>        [DllImport(&#8220;sqlncli10.dll&#8221;, SetLastError = true, CharSet = CharSet.Unicode)]<br \/>\n        public static extern SafeFileHandle OpenSqlFilestream(<br \/>\n            string FilestreamPath,<br \/>\n            UInt32 DesiredAccess,<br \/>\n            UInt32 OpenOptions,<br \/>\n            byte[] FilestreamTransactionContext,<br \/>\n            UInt32 FilestreamTransactionContextLength,<br \/>\n            LARGE_INTEGER_SQL AllocationSize<br \/>\n            );<\/p>\n<p>        [StructLayout(LayoutKind.Sequential)]<br \/>\n        public struct LARGE_INTEGER_SQL<br \/>\n        {<br \/>\n            public Int64 QuadPart;<br \/>\n            public LARGE_INTEGER_SQL(Int64 quadPart) { QuadPart = quadPart; }<br \/>\n        }<\/p>\n<p>        [DllImport(&#8220;kernel32.dll&#8221;)]<br \/>\n        private extern static void CloseHandle(IntPtr handle);<br \/>\n    }<br \/>\n}<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Az el\u0151z\u0151 r\u00e9szben elk\u00e9sz\u00fclt a t\u00e1bl\u00e1nk, ami streaming adatokat tud t\u00e1rolni. Itt az ideje p\u00e1r fot\u00f3t belesz\u00f3rni az adatb\u00e1zisba (illetve a f\u00e1jlrendszerbe)&#8230;.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8,25,6,4,30,58],"tags":[],"class_list":["post-359","post","type-post","status-publish","format-standard","hentry","category-net","category-adonet","category-adatbazisok","category-szakmai-elet","category-sql-server","category-sql-server-2008"],"_links":{"self":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/359","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=359"}],"version-history":[{"count":0,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/posts\/359\/revisions"}],"wp:attachment":[{"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=359"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=359"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/soci.hu\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=359"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}