<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-270199329953633771</id><updated>2011-04-22T00:28:42.019+02:00</updated><title type='text'>Programowanie</title><subtitle type='html'>Moje zmagania z .NET, VB, c#, SQL itd.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>14</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-2441349111953986091</id><published>2008-05-06T11:35:00.001+02:00</published><updated>2008-05-06T11:35:04.212+02:00</updated><title type='text'>Usprawnienia pracy z prostymi providerami ADO.NET</title><content type='html'>&lt;p&gt;Ostatnio musiałem przenieść dane z nowobudowanego systemu do pewnego dość starego systemu, kt&amp;#243;ry korzysta z serwera PostgreSQL. Z tej racji, że system &amp;#243;w jest dość mało znormalizowany podstawowa tabela zawiera mn&amp;#243;stwo kolumn (brrr np. 15 kolumn z poszczeg&amp;#243;lnymi pozycjami planu zajęć). Trochę trwało przygotowanie danych w takim układzie w postaci DataTable, potem pozostało mi tylko napisanie kodu obsługującego zapis danych do bazy postgresowej. W tym celu użyłem darmowego sterownika Npgsql, kt&amp;#243;ry jednak ma małe wsparcie do designera, co nie przeszkadza zbyt bardzo przy pisaniu poleceń sql dla znormalizowanych tabel. Jednak dla tabeli z 76 kolumnami oznaczało to perspektywę wklepywania ogromnego tasiemca z treścią sql, a potem jeszcze dłuższego z definiowaniem parametr&amp;#243;w polecenia. &lt;/p&gt;  &lt;p&gt;Pierwszy problem rozwiązałem przenosząc treść poleceń sql (a właściwie tylko Insert, bo inne nie były mi potrzebne), do osobnego pliku tekstowego znajdującego się w zasobach. Z drugim problemem poradziłem sobie przyjmując założenie, że nazwy parametr&amp;#243;w używanych w sql-u będą takie same jak nazwy kolumn w źr&amp;#243;dłowym obiekcie DataTable, a typ parametru będzie wyznaczany przez typ kolumny z tegoż DataTable. Przy przyjęciu tych założeń wystarczyło w pętli przejść po wszystkich kolumnach tabeli i utworzyć odpowiednie parametry polecenia sql. Kod realizujący ten pomysł umieściłem w następującej klasie:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Collections.Generic;&lt;br /&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Data;&lt;br /&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;Npgsql;&lt;br /&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;NpgsqlTypes;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;namespace &lt;/span&gt;Utils&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;internal class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NpgsqlTableMapping&lt;br /&gt;    &lt;/span&gt;{&lt;br /&gt;        &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Dictionary&lt;/span&gt;&amp;lt;System.&lt;span style="color: #2b91af"&gt;Type&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;NpgsqlDbType&lt;/span&gt;&amp;gt; npgsqlConversions;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public &lt;/span&gt;NpgsqlTableMapping()&lt;br /&gt;        {&lt;br /&gt;            npgsqlConversions = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Dictionary&lt;/span&gt;&amp;lt;System.&lt;span style="color: #2b91af"&gt;Type&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;NpgsqlDbType&lt;/span&gt;&amp;gt;();&lt;br /&gt;&lt;br /&gt;            npgsqlConversions.Add(&lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: blue"&gt;string&lt;/span&gt;), &lt;span style="color: #2b91af"&gt;NpgsqlDbType&lt;/span&gt;.Varchar);&lt;br /&gt;            npgsqlConversions.Add(&lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: blue"&gt;int&lt;/span&gt;), &lt;span style="color: #2b91af"&gt;NpgsqlDbType&lt;/span&gt;.Integer);&lt;br /&gt;            npgsqlConversions.Add(&lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: blue"&gt;short&lt;/span&gt;), &lt;span style="color: #2b91af"&gt;NpgsqlDbType&lt;/span&gt;.Smallint);&lt;br /&gt;            npgsqlConversions.Add(&lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: blue"&gt;byte&lt;/span&gt;), &lt;span style="color: #2b91af"&gt;NpgsqlDbType&lt;/span&gt;.Smallint);&lt;br /&gt;            npgsqlConversions.Add(&lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: blue"&gt;decimal&lt;/span&gt;), &lt;span style="color: #2b91af"&gt;NpgsqlDbType&lt;/span&gt;.Numeric);&lt;br /&gt;            npgsqlConversions.Add(&lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: blue"&gt;bool&lt;/span&gt;), &lt;span style="color: #2b91af"&gt;NpgsqlDbType&lt;/span&gt;.Bit);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public void &lt;/span&gt;AddMappings(&lt;span style="color: #2b91af"&gt;NpgsqlCommand &lt;/span&gt;command, &lt;span style="color: #2b91af"&gt;DataTable &lt;/span&gt;table)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;column &lt;span style="color: blue"&gt;in &lt;/span&gt;table.Columns)&lt;br /&gt;            {&lt;br /&gt;                &lt;span style="color: blue"&gt;string &lt;/span&gt;parameterName = column.ColumnName;&lt;br /&gt;                &lt;span style="color: #2b91af"&gt;NpgsqlDbType &lt;/span&gt;parameterType = npgsqlConversions[&lt;br /&gt;                    column.DataType];&lt;br /&gt;&lt;br /&gt;                &lt;span style="color: #2b91af"&gt;NpgsqlParameter &lt;/span&gt;parameter = command.Parameters.Add(&lt;br /&gt;                    parameterName, parameterType);&lt;br /&gt;                parameter.SourceColumn = column.ColumnName;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Jak widać nie ma tutaj żadnych czar&amp;#243;w, a tylko prosty słownik typ&amp;#243;w .netowych z odpowiadającymi im typami Npgsql (nie wszystkimi, a tylko tymi, kt&amp;#243;re były mi potrzebne. Teraz jednak kod obiektu dal zapisujący dane do bazy postgresowej jest dużo kr&amp;#243;tszy:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Data;&lt;br /&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;Npgsql;&lt;br /&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;NpgsqlTypes;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;Utils;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;namespace &lt;/span&gt;ODDI.VirtualDictionaryDAL&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;LearningOffersDAL&lt;br /&gt;    &lt;/span&gt;{&lt;br /&gt;        &lt;span style="color: blue"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NpgsqlDataAdapter &lt;/span&gt;adapter;&lt;br /&gt;        &lt;span style="color: blue"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NpgsqlConnection &lt;/span&gt;connection;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public &lt;/span&gt;LearningOffersDAL()&lt;br /&gt;        {&lt;br /&gt;            InitPostgreConnection();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;private void &lt;/span&gt;InitPostgreConnection()&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;string &lt;/span&gt;connectionString = System.Configuration.&lt;span style="color: #2b91af"&gt;ConfigurationManager&lt;/span&gt;.&lt;br /&gt;                ConnectionStrings[&lt;span style="color: #a31515"&gt;&amp;quot;PostgreServer&amp;quot;&lt;/span&gt;].ConnectionString;&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;this&lt;/span&gt;.connection = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NpgsqlConnection&lt;/span&gt;(connectionString);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;private bool &lt;/span&gt;initedOffersAdapter = &lt;span style="color: blue"&gt;false&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: blue"&gt;private void &lt;/span&gt;InitOffersAdapter(&lt;span style="color: #2b91af"&gt;DataTable &lt;/span&gt;table)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;if&lt;/span&gt;(!initedOffersAdapter)&lt;br /&gt;            {&lt;br /&gt;                &lt;span style="color: blue"&gt;string &lt;/span&gt;sql = Sgh.ODDI.VirtualDictionaryDAL.Properties.&lt;br /&gt;                    &lt;span style="color: #2b91af"&gt;Resources&lt;/span&gt;.InsertLearningOffer;&lt;br /&gt;                &lt;br /&gt;                &lt;span style="color: #2b91af"&gt;NpgsqlCommand &lt;/span&gt;insertCommand = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NpgsqlCommand&lt;/span&gt;(sql, connection);&lt;br /&gt;                insertCommand.Parameters.Add(&lt;span style="color: #a31515"&gt;&amp;quot;YearSemester&amp;quot;&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;NpgsqlDbType&lt;/span&gt;.Integer);&lt;br /&gt;&lt;br /&gt;                adapter = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NpgsqlDataAdapter&lt;/span&gt;();&lt;br /&gt;                adapter.InsertCommand = insertCommand;&lt;br /&gt;&lt;br /&gt;                &lt;span style="color: #2b91af"&gt;NpgsqlTableMapping &lt;/span&gt;mappings = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NpgsqlTableMapping&lt;/span&gt;();&lt;br /&gt;                mappings.AddMappings(&lt;span style="color: blue"&gt;this&lt;/span&gt;.adapter.InsertCommand, table);&lt;br /&gt;&lt;br /&gt;                initedOffersAdapter = &lt;span style="color: blue"&gt;true&lt;/span&gt;;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public void &lt;/span&gt;Update(&lt;span style="color: #2b91af"&gt;DataTable &lt;/span&gt;table, &lt;span style="color: blue"&gt;int &lt;/span&gt;yearSemester)&lt;br /&gt;        {&lt;br /&gt;            InitOffersAdapter(table);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;this&lt;/span&gt;.adapter.InsertCommand.Parameters[&lt;span style="color: #a31515"&gt;&amp;quot;YearSemester&amp;quot;&lt;/span&gt;].&lt;br /&gt;                Value = yearSemester;&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;this&lt;/span&gt;.adapter.Update(table);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Użycie obiektu &lt;span style="color: #2b91af"&gt;NpgsqlTableMapping &lt;/span&gt;nie przeszkadza w dodaniu dodatkowych parametr&amp;#243;w (tutaj YearSemester). Wadą jest natomiast to, że polecenie może być przygotowane dopiero przy pierwszym update, kiedy znana jest definicja źr&amp;#243;dłowego DataTable.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-2441349111953986091?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/2441349111953986091/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=2441349111953986091' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/2441349111953986091'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/2441349111953986091'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2008/05/usprawnienia-pracy-z-prostymi.html' title='Usprawnienia pracy z prostymi providerami ADO.NET'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-3864447544387987610</id><published>2008-04-17T12:09:00.001+02:00</published><updated>2008-04-17T12:09:54.709+02:00</updated><title type='text'>Pola tekstowe o wysokości zależnej od rodzaju rekordu</title><content type='html'>&lt;p&gt;Ostatnio przygotowując raport w technologii RDLC stanąłem przed koniecznością ustalenia wysokości p&amp;#243;l tekstowych w zależności od typu oferty, tak by rekord&amp;#243;w typu A było tylko cztery na stronie, typu B sześć, a C 10.&lt;/p&gt;  &lt;p&gt;Z tej racji, że raporty rdlc mogą być renderowane w r&amp;#243;żny spos&amp;#243;b, to nie generują zdarzeń pozwalający na bierząco korygować wysokość (albo też generować indeks czy spis treści). Stąd też wybrałem inne rozwiązanie: ustalenie właściwości &amp;quot;CanGrow&amp;quot; pola tekstowego na true oraz zmienianie liczby linii zawartych w odpowiedniej kom&amp;#243;rce tabeli źr&amp;#243;dłowej. Teraz pozostało opracować kod do wyliczania wysokości, aby się łatwiej testował, to najpierw zdefiniowałem pomocniczy interfejs:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public interface &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ITextMeasurer&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;int &lt;/span&gt;NoOfLines(&lt;span style="color: blue"&gt;string &lt;/span&gt;text);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;A potem jego implementacja:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System;&lt;br /&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Drawing;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;namespace &lt;/span&gt;Utilities.Graphics&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;TextMeasurer &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;ITextMeasurer&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;IDisposable&lt;br /&gt;    &lt;/span&gt;{&lt;br /&gt;        &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SizeF &lt;/span&gt;layoutArea;&lt;br /&gt;        &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Font &lt;/span&gt;font;&lt;br /&gt;        &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;System.Drawing.&lt;span style="color: #2b91af"&gt;Graphics &lt;/span&gt;graphics;&lt;br /&gt;        &lt;span style="color: blue"&gt;private readonly float &lt;/span&gt;oneLineHeight;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public &lt;/span&gt;TextMeasurer(&lt;span style="color: blue"&gt;string &lt;/span&gt;fontName, &lt;span style="color: blue"&gt;float &lt;/span&gt;fontSize,&lt;br /&gt;            &lt;span style="color: blue"&gt;float &lt;/span&gt;maxWidthInCm, &lt;span style="color: blue"&gt;float &lt;/span&gt;maxHeightInCm, &lt;br /&gt;            &lt;span style="color: blue"&gt;byte &lt;/span&gt;totalHorizontalPadding) :&lt;br /&gt;            &lt;br /&gt;            &lt;span style="color: blue"&gt;this&lt;/span&gt;(&lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Font&lt;/span&gt;(fontName, fontSize), maxWidthInCm, maxHeightInCm, &lt;br /&gt;                totalHorizontalPadding)&lt;br /&gt;        {}&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public &lt;/span&gt;TextMeasurer(&lt;span style="color: #2b91af"&gt;Font &lt;/span&gt;font, &lt;span style="color: blue"&gt;float &lt;/span&gt;maxWidthInCm, &lt;br /&gt;            &lt;span style="color: blue"&gt;float &lt;/span&gt;maxHeightInCm,&lt;br /&gt;            &lt;span style="color: blue"&gt;byte &lt;/span&gt;totalHorizontalPadding)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;this&lt;/span&gt;.font = font;&lt;br /&gt;            &lt;span style="color: #2b91af"&gt;Bitmap &lt;/span&gt;bmp = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Bitmap&lt;/span&gt;(1, 1);&lt;br /&gt;            graphics = System.Drawing.&lt;span style="color: #2b91af"&gt;Graphics&lt;/span&gt;.FromImage(bmp);&lt;br /&gt;            &lt;span style="color: blue"&gt;float &lt;/span&gt;width = (&lt;span style="color: blue"&gt;float&lt;/span&gt;)(maxWidthInCm / 2.54) * &lt;br /&gt;                bmp.HorizontalResolution - totalHorizontalPadding;&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;float &lt;/span&gt;height = maxHeightInCm * bmp.VerticalResolution;&lt;br /&gt;            layoutArea = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SizeF&lt;/span&gt;(width, height);&lt;br /&gt;&lt;br /&gt;            oneLineHeight = GetTextSize(&lt;span style="color: #a31515"&gt;&amp;quot;W&amp;quot;&lt;/span&gt;).Height;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SizeF &lt;/span&gt;GetTextSize(&lt;span style="color: blue"&gt;string &lt;/span&gt;text)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;return &lt;/span&gt;graphics.MeasureString(text, font, layoutArea,&lt;br /&gt;                &lt;span style="color: #2b91af"&gt;StringFormat&lt;/span&gt;.GenericTypographic);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public int &lt;/span&gt;NoOfLines(&lt;span style="color: blue"&gt;string &lt;/span&gt;text)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;float &lt;/span&gt;textHeight = GetTextSize(text).Height;&lt;br /&gt;            &lt;span style="color: blue"&gt;return &lt;/span&gt;(&lt;span style="color: blue"&gt;int&lt;/span&gt;)(textHeight / oneLineHeight);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;#region &lt;/span&gt;IDisposable Members&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public void &lt;/span&gt;Dispose()&lt;br /&gt;        {&lt;br /&gt;            graphics.Dispose();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;#endregion&lt;br /&gt;    &lt;/span&gt;}&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Jak widać, w powyższym rozwiązaniu korzystam z metody MeasureString pomocniczego obiektu graphics. Tak więc pozostało wykorzystać wyliczenie wysokości tekstu i w razie potrzeby dodać pare linii:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Text;&lt;br /&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;Sgh.Utilities.Graphics;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;namespace &lt;/span&gt;Utilities.Texts&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Corrector&lt;br /&gt;    &lt;/span&gt;{&lt;br /&gt;        &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ITextMeasurer &lt;/span&gt;textMeasurer;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public &lt;/span&gt;Corrector(&lt;span style="color: #2b91af"&gt;ITextMeasurer &lt;/span&gt;textMeasurer)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;this&lt;/span&gt;.textMeasurer = textMeasurer;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public string &lt;/span&gt;CorrectHeight(&lt;span style="color: blue"&gt;string &lt;/span&gt;inputString, &lt;span style="color: blue"&gt;int &lt;/span&gt;outputLinesCount)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;int &lt;/span&gt;currentNoOfLines = textMeasurer.NoOfLines(inputString);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;if &lt;/span&gt;(currentNoOfLines == outputLinesCount)&lt;br /&gt;            {&lt;br /&gt;                &lt;span style="color: blue"&gt;return &lt;/span&gt;inputString;&lt;br /&gt;            }&lt;br /&gt;                &lt;br /&gt;            &lt;span style="color: blue"&gt;return &lt;/span&gt;AddEmptyLinesToString(inputString, &lt;br /&gt;                outputLinesCount - currentNoOfLines);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;private static string &lt;/span&gt;AddEmptyLinesToString(&lt;br /&gt;            &lt;span style="color: blue"&gt;string &lt;/span&gt;inputString, &lt;span style="color: blue"&gt;int &lt;/span&gt;noLinesToAdd)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: #2b91af"&gt;StringBuilder &lt;/span&gt;builder = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StringBuilder&lt;/span&gt;();&lt;br /&gt;            builder.Append(inputString);&lt;br /&gt;            &lt;br /&gt;            &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;int &lt;/span&gt;i = 0; i &amp;lt; noLinesToAdd; i++)&lt;br /&gt;            {&lt;br /&gt;                builder.Append(&lt;span style="color: #a31515"&gt;&amp;quot;\r\n &amp;quot;&lt;/span&gt;);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;return &lt;/span&gt;builder.ToString();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Jednak okazało się, że czasami w danych źr&amp;#243;dłowych jest więcej linii tekstu niż może pomieścić w kom&amp;#243;rcę, tak więc opr&amp;#243;cz dodawania pustych wierszy konieczne jest dodanie przycinanie nadmiarowych, tak więc zmodyfikowałem kod metody CurrentHeight:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public string &lt;/span&gt;CorrectHeight(&lt;span style="color: blue"&gt;string &lt;/span&gt;inputString, &lt;span style="color: blue"&gt;int &lt;/span&gt;outputLinesCount)&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;int &lt;/span&gt;currentNoOfLines = textMeasurer.NoOfLines(inputString);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;if &lt;/span&gt;(currentNoOfLines &amp;gt; outputLinesCount)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: #2b91af"&gt;LineRemover &lt;/span&gt;remover = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;LineRemover&lt;/span&gt;(inputString, &lt;br /&gt;            outputLinesCount, textMeasurer);&lt;br /&gt;&lt;br /&gt;        inputString = remover.OutputString;&lt;br /&gt;        currentNoOfLines = remover.TextHeight;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;if &lt;/span&gt;(currentNoOfLines == outputLinesCount)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: blue"&gt;return &lt;/span&gt;inputString;&lt;br /&gt;    }&lt;br /&gt;        &lt;br /&gt;    &lt;span style="color: blue"&gt;return &lt;/span&gt;AddEmptyLinesToString(inputString, &lt;br /&gt;        outputLinesCount - currentNoOfLines);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Jak widać teraz wykorzystywany jest obiekt klasy LineRemover. Tnąc tekst wzdłuż spacji&amp;#160; / znak&amp;#243;w końca wiersza (za pomocą wyrażenia regularnego), wykorzystuje wyszukiwanie binarne do znaleznienia takiego miejsca, dla kt&amp;#243;rego długość ciągu jest największa, a wysokość napisu jest nie wyższa od wymaganej.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Collections.Generic;&lt;br /&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Text.RegularExpressions;&lt;br /&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;Utilities.Graphics;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;namespace &lt;/span&gt;Utilities.Texts&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;LineRemover&lt;br /&gt;    &lt;/span&gt;{&lt;br /&gt;        &lt;span style="color: blue"&gt;private readonly string &lt;/span&gt;outputString;&lt;br /&gt;        &lt;span style="color: blue"&gt;private int &lt;/span&gt;textHeight;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public &lt;/span&gt;LineRemover(&lt;span style="color: blue"&gt;string &lt;/span&gt;inputString, &lt;span style="color: blue"&gt;int &lt;/span&gt;outputLinesCount,&lt;br /&gt;            &lt;span style="color: #2b91af"&gt;ITextMeasurer &lt;/span&gt;textMeasurer)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;this&lt;/span&gt;.outputString = RemoveLines(inputString, &lt;br /&gt;                outputLinesCount, textMeasurer);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public string &lt;/span&gt;OutputString&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;get &lt;/span&gt;{ &lt;span style="color: blue"&gt;return this&lt;/span&gt;.outputString; }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public int &lt;/span&gt;TextHeight&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;get &lt;/span&gt;{ &lt;span style="color: blue"&gt;return this&lt;/span&gt;.textHeight; }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;private string &lt;/span&gt;RemoveLines(&lt;span style="color: blue"&gt;string &lt;/span&gt;inputString, &lt;br /&gt;            &lt;span style="color: blue"&gt;int &lt;/span&gt;outputLinesCount,&lt;br /&gt;            &lt;span style="color: #2b91af"&gt;ITextMeasurer &lt;/span&gt;textMeasurer)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; whitespaces = FindWhitespaces(inputString);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;int &lt;/span&gt;lowIndex = 0;&lt;br /&gt;            &lt;span style="color: blue"&gt;int &lt;/span&gt;highIndex = whitespaces.Count;&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;while &lt;/span&gt;(highIndex &amp;gt; lowIndex + 1)&lt;br /&gt;            {&lt;br /&gt;                &lt;span style="color: blue"&gt;int &lt;/span&gt;currentIndex = (lowIndex + highIndex) / 2;&lt;br /&gt;                &lt;span style="color: blue"&gt;string &lt;/span&gt;currentText = inputString.Substring(0, &lt;br /&gt;                    whitespaces[currentIndex]);&lt;br /&gt;&lt;br /&gt;                textHeight = textMeasurer.NoOfLines(currentText);&lt;br /&gt;&lt;br /&gt;                &lt;span style="color: blue"&gt;if &lt;/span&gt;(textHeight &amp;gt; outputLinesCount)&lt;br /&gt;                {&lt;br /&gt;                    highIndex = currentIndex;&lt;br /&gt;                }&lt;br /&gt;                &lt;span style="color: blue"&gt;else&lt;br /&gt;                &lt;/span&gt;{&lt;br /&gt;                    lowIndex = currentIndex;&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;return &lt;/span&gt;inputString.Substring(0, whitespaces[lowIndex]);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; FindWhitespaces(&lt;span style="color: blue"&gt;string &lt;/span&gt;inputString)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;string &lt;/span&gt;pattern = &lt;span style="color: #a31515"&gt;@&amp;quot;\s+&amp;quot;&lt;/span&gt;;&lt;br /&gt;            &lt;span style="color: #2b91af"&gt;Regex &lt;/span&gt;regex = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Regex&lt;/span&gt;(pattern, &lt;span style="color: #2b91af"&gt;RegexOptions&lt;/span&gt;.Compiled);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: #2b91af"&gt;MatchCollection &lt;/span&gt;matches = regex.Matches(inputString);&lt;br /&gt;            &lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; whitespaces = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt;();&lt;br /&gt;            whitespaces.Add(0); &lt;span style="color: green"&gt;// first char&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;Match &lt;/span&gt;match &lt;span style="color: blue"&gt;in &lt;/span&gt;matches)&lt;br /&gt;            {&lt;br /&gt;                whitespaces.Add(match.Index);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            whitespaces.Add(inputString.Length);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;return &lt;/span&gt;whitespaces;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Przykład wykorzystania:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;float &lt;/span&gt;widthInCm = 8f;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;TextMeasurer &lt;/span&gt;textMeasurer = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;TextMeasurer&lt;/span&gt;(&lt;br /&gt;    &lt;span style="color: #a31515"&gt;&amp;quot;Arial&amp;quot;&lt;/span&gt;, 8f, widthInCm, 20f, 4))&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: #2b91af"&gt;Corrector &lt;/span&gt;textCorrector = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Corrector&lt;/span&gt;(textMeasurer);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;foreach&lt;/span&gt;(&lt;span style="color: #2b91af"&gt;FramesDataSet&lt;/span&gt;.&lt;span style="color: #2b91af"&gt;OffersListRow &lt;/span&gt;oneOffer &lt;span style="color: blue"&gt;in &lt;/span&gt;offers)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: blue"&gt;string &lt;/span&gt;authorsString = oneOffer.AuthorsString;&lt;br /&gt;        &lt;span style="color: blue"&gt;int &lt;/span&gt;linesCount = textMeasurer.NoOfLines(oneOffer.AuthorsString);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;if &lt;/span&gt;(oneOffer.OfferType == 3)&lt;span style="color: green"&gt;&lt;br /&gt;        &lt;/span&gt;{&lt;br /&gt;            authorsString = textCorrector.CorrectHeight(authorsString, 2);&lt;br /&gt;        }&lt;br /&gt;        &lt;span style="color: blue"&gt;else if &lt;/span&gt;(oneOffer.Level == 1 || oneOffer.Level == 2)&lt;br /&gt;        {&lt;br /&gt;            authorsString = textCorrector.CorrectHeight(authorsString, 27);&lt;br /&gt;        }&lt;br /&gt;        &lt;span style="color: blue"&gt;else&lt;br /&gt;        &lt;/span&gt;{&lt;br /&gt;            authorsString = textCorrector.CorrectHeight(authorsString, 13);&lt;br /&gt;        }&lt;br /&gt;        oneOffer.AuthorsString = authorsString;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Raport prawie gotowy, pozostał tylko problem z niedziałającym zgodnie z oczekiwaniami PageBreak (zamiast nowej strony jest nowa kolumna). Ale to temat na kolejny artykuł ;).&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-3864447544387987610?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/3864447544387987610/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=3864447544387987610' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/3864447544387987610'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/3864447544387987610'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2008/04/pola-tekstowe-o-wysokoci-zalenej-od.html' title='Pola tekstowe o wysokości zależnej od rodzaju rekordu'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-1525913526363656780</id><published>2008-04-08T10:31:00.001+02:00</published><updated>2008-04-08T10:31:05.374+02:00</updated><title type='text'>Sumowanie danych w obiekcie DataTable</title><content type='html'>&lt;p&gt;Miałem ostatnio potrzebę sumowania danych szczeg&amp;#243;łowych. I nie chodziło tu tylko o dane numeryczne, co można byłoby załatwić wyrażeniami typu Sum(Childs!NazwaRelacji.NazwaPola), lecz także tekstowe, np. nazwy autor&amp;#243;w, tak, by każdy znalazł się w osobnym wierszu, czy też sumowanie symboli kierunk&amp;#243;w, tak by zaprezentować wszystkie kierunki dla danej oferty oddzielone przecinkami.&lt;/p&gt;  &lt;p&gt;Będąc jeszcze w erze pre-LINQ, stąd skorzystałem z rozwiązania tradycyjnego, opartego na obiektach DataTable: sumując dane z jednej tabeli (źr&amp;#243;dłowej) i aktualizując wartości w drugiej tabeli (wyjściowej).&lt;/p&gt;  &lt;p&gt;Oparłem się na pomocniczych obiektach sumujących implementujących prosty interfejs:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public interface &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ISummator&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;void &lt;/span&gt;AddOneValue(&lt;span style="color: #2b91af"&gt;DataRow &lt;/span&gt;inputRow);&lt;br /&gt;    &lt;span style="color: blue"&gt;void &lt;/span&gt;SaveResult(&lt;span style="color: #2b91af"&gt;DataRow &lt;/span&gt;outputRow);&lt;br /&gt;    &lt;span style="color: blue"&gt;void &lt;/span&gt;Clear();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Przykładowy summator tekstowy:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SimpleStringSummator &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;ISummator&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;private readonly string &lt;/span&gt;separator;&lt;br /&gt;    &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;inputColumn;&lt;br /&gt;    &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;outputColumn;&lt;br /&gt;    &lt;span style="color: blue"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StringBuilder &lt;/span&gt;stringBuilder;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public &lt;/span&gt;SimpleStringSummator(&lt;span style="color: blue"&gt;string &lt;/span&gt;separator,&lt;br /&gt;        &lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;inputColumn, &lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;outputColumn)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;.separator = separator;&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;.inputColumn = inputColumn;&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;.outputColumn = outputColumn;&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;.stringBuilder = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StringBuilder&lt;/span&gt;();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public void &lt;/span&gt;AddOneValue(&lt;span style="color: #2b91af"&gt;DataRow &lt;/span&gt;inputRow)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: blue"&gt;if &lt;/span&gt;(stringBuilder.Length &amp;gt; 0)&lt;br /&gt;        {&lt;br /&gt;            stringBuilder.Append(separator);&lt;br /&gt;        }&lt;br /&gt;        stringBuilder.Append(inputRow[inputColumn]);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public void &lt;/span&gt;SaveResult(&lt;span style="color: #2b91af"&gt;DataRow &lt;/span&gt;outputRow)&lt;br /&gt;    {&lt;br /&gt;        outputRow[outputColumn] = stringBuilder.ToString();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public void &lt;/span&gt;Clear()&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;.stringBuilder = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StringBuilder&lt;/span&gt;();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Jak widać summator w konstruktorze dostaje kolumny, z kt&amp;#243;rych będzie pobierał dane i potem przy każdym wywołaniu metody AddOneValue dodaje bieżącą wartość do wewnętrznego bufora, kt&amp;#243;rym jest obiekt klasy StringBuilder.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Przykład summatora numerycznego:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IntSummator &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;ISummator&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;inputColumn;&lt;br /&gt;    &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;outputColumn;&lt;br /&gt;    &lt;span style="color: blue"&gt;private int &lt;/span&gt;value;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public &lt;/span&gt;IntSummator(&lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;inputColumn,&lt;br /&gt;        &lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;outputColumn)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;.inputColumn = inputColumn;&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;.outputColumn = outputColumn;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public void &lt;/span&gt;AddOneValue(&lt;span style="color: #2b91af"&gt;DataRow &lt;/span&gt;inputRow)&lt;br /&gt;    {&lt;br /&gt;        value += (&lt;span style="color: blue"&gt;int&lt;/span&gt;)inputRow[inputColumn];&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public void &lt;/span&gt;SaveResult(&lt;span style="color: #2b91af"&gt;DataRow &lt;/span&gt;outputRow)&lt;br /&gt;    {&lt;br /&gt;        outputRow[outputColumn] = &lt;span style="color: blue"&gt;this&lt;/span&gt;.value;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public void &lt;/span&gt;Clear()&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;.value = 0;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;A sama operacja sumowania odbywa się w obiekcie klasy DataTableJoiner:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataTableJoiner&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;groupByColumn;&lt;br /&gt;    &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ISummator&lt;/span&gt;[] summators;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public &lt;/span&gt;DataTableJoiner(&lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;groupByColumn,&lt;br /&gt;        &lt;span style="color: #2b91af"&gt;ISummator&lt;/span&gt;[] summators)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;.groupByColumn = groupByColumn;&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;.summators = summators;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;public &lt;/span&gt;DataTableJoiner(&lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;groupByColumn, &lt;span style="color: #2b91af"&gt;ISummator &lt;/span&gt;summator) :&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;(groupByColumn, &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ISummator&lt;/span&gt;[]{summator})&lt;br /&gt;    {}&lt;br /&gt;    &lt;br /&gt;    &lt;span style="color: blue"&gt;public void &lt;/span&gt;JoinData(&lt;span style="color: #2b91af"&gt;DataRow&lt;/span&gt;[] inputRows, &lt;span style="color: #2b91af"&gt;DataTable &lt;/span&gt;outputTable)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: blue"&gt;if &lt;/span&gt;(inputRows.Length == 0)&lt;br /&gt;            &lt;span style="color: blue"&gt;return&lt;/span&gt;;&lt;br /&gt;     &lt;br /&gt;        &lt;span style="color: blue"&gt;object &lt;/span&gt;previousGroupByColumnValue = &lt;span style="color: blue"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;DataRow &lt;/span&gt;oneRow &lt;span style="color: blue"&gt;in &lt;/span&gt;inputRows)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;object &lt;/span&gt;currentGroupByColumnValue = oneRow[groupByColumn];&lt;br /&gt;            &lt;span style="color: blue"&gt;bool &lt;/span&gt;newValue = !currentGroupByColumnValue.&lt;br /&gt;                                 Equals(previousGroupByColumnValue);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;ISummator &lt;/span&gt;summator &lt;span style="color: blue"&gt;in &lt;/span&gt;summators)&lt;br /&gt;            {&lt;br /&gt;                &lt;span style="color: blue"&gt;if &lt;/span&gt;(newValue &amp;amp;&amp;amp; previousGroupByColumnValue != &lt;span style="color: blue"&gt;null&lt;/span&gt;)&lt;br /&gt;                {&lt;br /&gt;                    &lt;span style="color: #2b91af"&gt;DataRow &lt;/span&gt;outputRow = outputTable.Rows.Find(&lt;br /&gt;                        previousGroupByColumnValue);&lt;br /&gt;                    summator.SaveResult(outputRow);&lt;br /&gt;                    summator.Clear();&lt;br /&gt;                }&lt;br /&gt;                summator.AddOneValue(oneRow);&lt;br /&gt;            }&lt;br /&gt;            &lt;span style="color: blue"&gt;if &lt;/span&gt;(newValue)&lt;br /&gt;            {&lt;br /&gt;                previousGroupByColumnValue = currentGroupByColumnValue;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #2b91af"&gt;DataRow &lt;/span&gt;lastRow = outputTable.Rows.Find(previousGroupByColumnValue);&lt;br /&gt;        &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;ISummator &lt;/span&gt;summator &lt;span style="color: blue"&gt;in &lt;/span&gt;summators)&lt;br /&gt;        {&lt;br /&gt;            summator.SaveResult(lastRow);                &lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Przykład wykorzystania:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public void &lt;/span&gt;MultiColumnsJoin()&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: green"&gt;//definicja tabeli źr&amp;#243;dłowej&lt;br /&gt;    &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataTable &lt;/span&gt;offersTable = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataTable&lt;/span&gt;();&lt;br /&gt;    &lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;offerIdColumn = offersTable.Columns.Add(&lt;br /&gt;        &lt;span style="color: #a31515"&gt;&amp;quot;OfferId&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: blue"&gt;int&lt;/span&gt;));&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;directionColumn = offersTable.Columns.Add(&lt;br /&gt;        &lt;span style="color: #a31515"&gt;&amp;quot;DirectionCode&amp;quot;&lt;/span&gt;);&lt;br /&gt;    &lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;hoursCountColumn = offersTable.Columns.Add(&lt;br /&gt;        &lt;span style="color: #a31515"&gt;&amp;quot;HoursCount&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: blue"&gt;int&lt;/span&gt;));&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: green"&gt;//dodanie rekord&amp;#243;w w tabeli źr&amp;#243;dłowej&lt;br /&gt;    &lt;/span&gt;offersTable.Rows.Add(1, &lt;span style="color: #a31515"&gt;&amp;quot;Eko&amp;quot;&lt;/span&gt;, 5);&lt;br /&gt;    offersTable.Rows.Add(1, &lt;span style="color: #a31515"&gt;&amp;quot;Fin&amp;quot;&lt;/span&gt;, 10);&lt;br /&gt;    offersTable.Rows.Add(1, &lt;span style="color: #a31515"&gt;&amp;quot;Geo&amp;quot;&lt;/span&gt;, 1);&lt;br /&gt;&lt;br /&gt;    offersTable.Rows.Add(2, &lt;span style="color: #a31515"&gt;&amp;quot;Fin&amp;quot;&lt;/span&gt;, 3);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: green"&gt;//definicja tabeli wyjściowej&lt;br /&gt;    &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataTable &lt;/span&gt;outputTable = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataTable&lt;/span&gt;();&lt;br /&gt;    &lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;pk = outputTable.Columns.Add(&lt;span style="color: #a31515"&gt;&amp;quot;OfferId&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: blue"&gt;int&lt;/span&gt;));&lt;br /&gt;    &lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;outputDirectionColumn = outputTable.Columns.Add(&lt;br /&gt;        &lt;span style="color: #a31515"&gt;&amp;quot;SumDirections&amp;quot;&lt;/span&gt;);&lt;br /&gt;    &lt;span style="color: #2b91af"&gt;DataColumn &lt;/span&gt;outputHoursColumn = outputTable.Columns.Add(&lt;br /&gt;        &lt;span style="color: #a31515"&gt;&amp;quot;TotalHours&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;typeof&lt;/span&gt;(&lt;span style="color: blue"&gt;int&lt;/span&gt;));&lt;br /&gt;&lt;br /&gt;    outputTable.PrimaryKey = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataColumn&lt;/span&gt;[] { pk };&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: green"&gt;//dodanie rekord&amp;#243;w w tabeli wyjściowej&lt;br /&gt;    &lt;/span&gt;outputTable.Rows.Add(1);&lt;br /&gt;    outputTable.Rows.Add(2);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #2b91af"&gt;ISummator &lt;/span&gt;directionSummator = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SimpleStringSummator&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;,&amp;quot;&lt;/span&gt;,&lt;br /&gt;        directionColumn, outputDirectionColumn);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #2b91af"&gt;ISummator &lt;/span&gt;hoursSummator = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IntSummator&lt;/span&gt;(&lt;br /&gt;        hoursCountColumn, outputHoursColumn);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #2b91af"&gt;DataTableJoiner &lt;/span&gt;joiner = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;DataTableJoiner&lt;/span&gt;(offerIdColumn,&lt;br /&gt;        &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ISummator&lt;/span&gt;[] { directionSummator, hoursSummator });&lt;br /&gt;&lt;br /&gt;    joiner.JoinData(offersTable.Select(), outputTable);&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;DataRow &lt;/span&gt;oneRow &lt;span style="color: blue"&gt;in &lt;/span&gt;outputTable.Rows)&lt;br /&gt;    {&lt;br /&gt;        System.&lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;br /&gt;            &lt;span style="color: #a31515"&gt;&amp;quot;Key: {0}, direction string: {1}, hours: {2}&amp;quot;&lt;/span&gt;,&lt;br /&gt;            oneRow[pk], oneRow[outputDirectionColumn],&lt;br /&gt;            oneRow[outputHoursColumn]);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Teraz muszę zastanowić się jak to zrobić w LINQu :)&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-1525913526363656780?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/1525913526363656780/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=1525913526363656780' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/1525913526363656780'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/1525913526363656780'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2008/04/sumowanie-danych-w-obiekcie-datatable.html' title='Sumowanie danych w obiekcie DataTable'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-62270836061745839</id><published>2008-03-17T09:32:00.001+01:00</published><updated>2008-03-17T10:54:07.033+01:00</updated><title type='text'>Hurtowe pobieranie rekordów podrzędnych z bazy Oracle</title><content type='html'>&lt;p&gt;Mając chwilę czasu, spr&amp;#243;bowałem znaleźć rozwiązanie tegoż samego problemu, co w poprzednim poście, ale już w oparciu o metody specyficzne dla serwera Oracle.&lt;/p&gt;  &lt;p&gt;Po pobieżnej analizie link&amp;#243;w wyszukanych na Google postanowiłem skorzystać z istniejącej w providerze Oracle.DataAccess możliwości przekazywania do procedury PLSQL tablic asocjacyjnych. Sw&amp;#243;j kod oparłem na rozwiązaniu opublikowanego w &lt;a href="http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework.adonet/topic3295.aspx"&gt;artykule Nicka N.&lt;/a&gt; Gł&amp;#243;wną modyfikacją było przeniesienie definicji funkcji konwertującej tablice SQL i PLSQL do osobnego pakietu, tak by tego kodu nie trzeba było powielać używając w innych procedurach.&lt;/p&gt;  &lt;p&gt;Definicja pomocniczego typu SQL:&lt;/p&gt;  &lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;Create&lt;/span&gt; &lt;span class="kwrd"&gt;Or&lt;/span&gt; Replace Type TableOfInts &lt;span class="kwrd"&gt;Is&lt;/span&gt; &lt;span class="kwrd"&gt;Table&lt;/span&gt; &lt;span class="kwrd"&gt;Of&lt;/span&gt; &lt;span class="kwrd"&gt;Int&lt;/span&gt;;&lt;br /&gt;/&lt;/pre&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt;	font-size: small;&lt;br /&gt;	color: black;&lt;br /&gt;	font-family: consolas, "Courier New", courier, monospace;&lt;br /&gt;	background-color: #ffffff;&lt;br /&gt;	/*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt;	background-color: #f4f4f4;&lt;br /&gt;	width: 100%;&lt;br /&gt;	margin: 0em;&lt;br /&gt;}&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Definicja pakietu z definicją typu PLSQL i funkcją konwertującą dane z tego typu na typ SQL:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;Create&lt;/span&gt; &lt;span class="kwrd"&gt;Or&lt;/span&gt; Replace PACKAGE HelpersPack &lt;span class="kwrd"&gt;AS&lt;/span&gt;&lt;br /&gt;  TYPE AssociativeArray &lt;span class="kwrd"&gt;is&lt;/span&gt; &lt;span class="kwrd"&gt;table&lt;/span&gt; &lt;span class="kwrd"&gt;of&lt;/span&gt; &lt;span class="kwrd"&gt;Int&lt;/span&gt; &lt;span class="kwrd"&gt;Index&lt;/span&gt; &lt;span class="kwrd"&gt;By&lt;/span&gt; Binary_Integer;&lt;br /&gt;  &lt;span class="kwrd"&gt;Function&lt;/span&gt; AssociativeArray2Tbl(p_arr &lt;span class="kwrd"&gt;In&lt;/span&gt; AssociativeArray) &lt;br /&gt;    &lt;span class="kwrd"&gt;Return&lt;/span&gt; TableOfInts;&lt;br /&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;END&lt;/span&gt; HelpersPack;&lt;br /&gt;/&lt;br /&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;Create&lt;/span&gt; &lt;span class="kwrd"&gt;Or&lt;/span&gt; Replace Package Body HelpersPack &lt;span class="kwrd"&gt;As&lt;/span&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;Function&lt;/span&gt; AssociativeArray2Tbl(p_Arr &lt;span class="kwrd"&gt;In&lt;/span&gt; AssociativeArray) &lt;br /&gt;    &lt;span class="kwrd"&gt;Return&lt;/span&gt; TableOfInts&lt;br /&gt;    &lt;span class="kwrd"&gt;As&lt;/span&gt;&lt;br /&gt;        l_Data TableOfInts := TableOfInts();&lt;br /&gt;    &lt;span class="kwrd"&gt;Begin&lt;/span&gt;&lt;br /&gt;        &lt;span class="kwrd"&gt;For&lt;/span&gt; i &lt;span class="kwrd"&gt;In&lt;/span&gt; 1..p_Arr.&lt;span class="kwrd"&gt;Last&lt;/span&gt;&lt;br /&gt;        Loop&lt;br /&gt;            l_Data.Extend;&lt;br /&gt;            l_Data(l_Data.&lt;span class="kwrd"&gt;count&lt;/span&gt; ) := p_Arr(i);&lt;br /&gt;        &lt;span class="kwrd"&gt;End&lt;/span&gt; Loop;&lt;br /&gt;        &lt;span class="kwrd"&gt;Return&lt;/span&gt; l_Data;&lt;br /&gt;  &lt;span class="kwrd"&gt;End&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;END&lt;/span&gt; HelpersPack;&lt;/pre&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt;	font-size: small;&lt;br /&gt;	color: black;&lt;br /&gt;	font-family: consolas, "Courier New", courier, monospace;&lt;br /&gt;	background-color: #ffffff;&lt;br /&gt;	/*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt;	background-color: #f4f4f4;&lt;br /&gt;	width: 100%;&lt;br /&gt;	margin: 0em;&lt;br /&gt;}&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Sama zaś procedura ściągająca dane dla wybranych wartości klucza gł&amp;#243;wnego może wyglądać tak:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;Create&lt;/span&gt; &lt;span class="kwrd"&gt;Or&lt;/span&gt; Replace &lt;span class="kwrd"&gt;Procedure&lt;/span&gt; GetLearningOffersPrograms(&lt;br /&gt;  p_OffersIds &lt;span class="kwrd"&gt;In&lt;/span&gt; HelpersPack.AssociativeArray,&lt;br /&gt;  p_Cursor &lt;span class="kwrd"&gt;Out &lt;/span&gt;Sys_RefCursor)&lt;br /&gt;&lt;span class="kwrd"&gt;As&lt;/span&gt;&lt;br /&gt;  v_tmparray TableOfInts;&lt;br /&gt;&lt;span class="kwrd"&gt;Begin&lt;/span&gt;&lt;br /&gt;  v_tmpArray := HelpersPack.AssociativeArray2Tbl(p_OffersIds);&lt;br /&gt;&lt;br /&gt;  &lt;span class="kwrd"&gt;Open&lt;/span&gt; p_Cursor &lt;span class="kwrd"&gt;For&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="kwrd"&gt;Select&lt;/span&gt;&lt;br /&gt;      *&lt;br /&gt;    &lt;span class="kwrd"&gt;From&lt;/span&gt;&lt;br /&gt;      LearningOffersPrograms&lt;br /&gt;    &lt;span class="kwrd"&gt;Where&lt;/span&gt;&lt;br /&gt;      OfferId &lt;span class="kwrd"&gt;In&lt;/span&gt; (&lt;span class="kwrd"&gt;Select&lt;/span&gt; column_value &lt;span class="kwrd"&gt;From&lt;/span&gt; &lt;span class="kwrd"&gt;Table&lt;/span&gt;(v_tmpArray))&lt;br /&gt;    &lt;span class="kwrd"&gt;Order&lt;/span&gt; &lt;span class="kwrd"&gt;By&lt;/span&gt;&lt;br /&gt;      &lt;span class="kwrd"&gt;Position&lt;/span&gt;;&lt;br /&gt;&lt;span class="kwrd"&gt;END&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt;	font-size: small;&lt;br /&gt;	color: black;&lt;br /&gt;	font-family: consolas, "Courier New", courier, monospace;&lt;br /&gt;	background-color: #ffffff;&lt;br /&gt;	/*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt;	background-color: #f4f4f4;&lt;br /&gt;	width: 100%;&lt;br /&gt;	margin: 0em;&lt;br /&gt;}&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;/style&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;A jej uruchomienie po stronie klienta .netowego:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public void &lt;/span&gt;LoadProgramsWithArray(&lt;span style="color: #2b91af"&gt;DataSet &lt;/span&gt;data, &lt;span style="color: blue"&gt;int&lt;/span&gt;[] offersIds, &lt;br /&gt;    &lt;span style="color: blue"&gt;string &lt;/span&gt;tableName)&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;string &lt;/span&gt;connectionString = System.Configuration.&lt;span style="color: #2b91af"&gt;ConfigurationManager&lt;/span&gt;.&lt;br /&gt;            ConnectionStrings[&lt;span style="color: #a31515"&gt;&amp;quot;OracleServer&amp;quot;&lt;/span&gt;].ConnectionString;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;OracleConnection &lt;/span&gt;con = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;OracleConnection&lt;/span&gt;(connectionString))&lt;br /&gt;    {&lt;br /&gt;        con.Open();&lt;br /&gt;        &lt;br /&gt;        &lt;span style="color: #2b91af"&gt;OracleCommand &lt;/span&gt;cmd = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;OracleCommand&lt;/span&gt;(&lt;br /&gt;            &lt;span style="color: #a31515"&gt;&amp;quot;GetLearningOffersPrograms&amp;quot;&lt;/span&gt;, con);&lt;br /&gt;        cmd.CommandType = &lt;span style="color: #2b91af"&gt;CommandType&lt;/span&gt;.StoredProcedure;&lt;br /&gt;&lt;br /&gt;        cmd.Parameters.Add(&lt;span style="color: #a31515"&gt;&amp;quot;p_OffersIds&amp;quot;&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;OracleDbType&lt;/span&gt;.Int32, &lt;br /&gt;            offersIds, &lt;span style="color: #2b91af"&gt;ParameterDirection&lt;/span&gt;.Input);&lt;br /&gt;&lt;br /&gt;        cmd.Parameters.Add(&lt;span style="color: #a31515"&gt;&amp;quot;p_Cursor&amp;quot;&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;OracleDbType&lt;/span&gt;.RefCursor, &lt;br /&gt;            &lt;span style="color: #2b91af"&gt;ParameterDirection&lt;/span&gt;.Output);&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: #2b91af"&gt;OracleDataAdapter &lt;/span&gt;adapter = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;OracleDataAdapter&lt;/span&gt;(cmd);&lt;br /&gt;        adapter.Fill(data, tableName);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Pobieżne testy powyższego podejścia pokazują, że wzrost wydajności w stosunku do poprzedniego rozwiązania (opartego na dynamicznym budowaniu zapytania po stronie klienta) jest relatywnie mały. Przypuszczam jednak, że w innych warunkach (np. większej liczbie rekord&amp;#243;w) korzyści z wykonywania całego kodu po stronie serwera będą większe.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-62270836061745839?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/62270836061745839/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=62270836061745839' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/62270836061745839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/62270836061745839'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2008/03/hurtowe-pobieranie-rekordw-podrzdnych-z.html' title='Hurtowe pobieranie rekordów podrzędnych z bazy Oracle'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-7939291534618739296</id><published>2008-03-12T14:49:00.001+01:00</published><updated>2008-03-13T09:33:44.051+01:00</updated><title type='text'>Hurtowe pobieranie z bazy rekordów podrzędnych</title><content type='html'>&lt;p&gt;W aktualnie budowanej przeze mnie aplikacji dość często występuje następujący schemat działania:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;pobranie określonych danych z podstawowych tabel wg rozbudowanych kryteri&amp;#243;w, np. wyszukanie określonych ofert, &lt;/li&gt;    &lt;li&gt;dla każdergo rekordu z podstawowych tabel pobranie danych z tabel pomocniczych, czyli np. program&amp;#243;w dla wszystkich wyszukanych ofert. &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;Pobieranie danych z tabel podrzędnych można zrealizować na r&amp;#243;żne sposoby:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;użycie widok&amp;#243;w parametrycznych - nie ma czegoś takiego w SQL Serverze albo Oracle, ale jest np. w Accessie - zapytanie może pobierać dane z innego zapytania parametrycznego. &lt;/li&gt;    &lt;li&gt;pobieranie danych w pętli dla każdego rekordu podstawowego - rozwiązanie mało wydajne, &lt;/li&gt;    &lt;li&gt;użycie do pobierania danych z tabel pomocniczych zapytań zawierających takie same kryteria jak zapytania zasadnicze - dużo pisania i trudniejszy w utrzymaniu kod (poprawki należy nanosić w wielu miejscach), &lt;/li&gt;    &lt;li&gt;wykorzystanie tymczasowych tabel z wartościami kluczy z tabel podstawowych, dla kt&amp;#243;rych należy pobrać dane z tabel szczeg&amp;#243;łowych, &lt;/li&gt;    &lt;li&gt;hurtowe pobranie danych z tabel podrzędnych dla wszystkich rekord&amp;#243;w jakie występują w tabeli nadrzędnej i to rozwiązanie wydaje mi się najwygodniejsze. &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;Hurtowe pobieranie danych z bazy realizowałem do tej pory (na SQL Serverze) przez przekazywanie do procedury składowanej parametru typu Varchar z listą kluczy, np.: Exec GetAllProducts @ProductsIds ='1,2,103,54,89'. A procedura przechowywana z tego varchara wyciągała tabelę wartości, kt&amp;#243;rą już bezproblemowo można było wykorzystać w zapytaniu. Począwszy od SQL Servera 2005 można używać jako parametr&amp;#243;w xml-i, tak więc zamiast stringa z wartościami kluczy można przekazać do niej odpowiedni string xml, ale tego pomysłu nie miałem jeszcze okazji testować w praktyce.&lt;/p&gt;  &lt;p&gt;Z tej racji, że system, kt&amp;#243;ry aktualnie rozwijam, korzysta z serwera Oracle, a Oracle na razie znam w małym zakresie, to na szybko postanowiłem skorzystać z innego pomysłu: dynamicznego budowania po stronie aplikacji SQL, tak by w klauzuli Where zawierał on wszystkie potrzebne wartości kluczy, czyli Select * From Products Where ProductId In(1,2,103,54,89). &lt;/p&gt;  &lt;p&gt;Sprawa jest prosta - wystarczy użyć obiekt StringBuilder i posklejać wszystkie klucze w jeden ciąg, a potem wykonać wygenerowane zapytanie. Jednak szybko napotkałem w Oracle ograniczenie do 1000 warunk&amp;#243;w w In (i być może w całym Where). Stąd też zmodyfikowałem sw&amp;#243;j kod, tak by ładowanie danych przebiegało w pakietach po 1000 kluczy.&lt;/p&gt;  &lt;p&gt;Poniżej zamieszczam finalny kod klasy, kt&amp;#243;ra zajmuje się ładowaniem danych w ten spos&amp;#243;b. Aby nie ograniczać się do jednego serwera (Oracle), skorzystałem z uniwersalnych obiekt&amp;#243;w, czyli IDbConnection i metody Load DataTable przyjmującej jako źr&amp;#243;dło obiekt IDataReader: &lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Data;&lt;br /&gt;&lt;span style="color: blue"&gt;using &lt;/span&gt;System.Text;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;namespace &lt;/span&gt;GdnKonsulting.Helpers&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MultipleValuesLoader&lt;br /&gt;    &lt;/span&gt;{&lt;br /&gt;        &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IDbConnection &lt;/span&gt;connection;&lt;br /&gt;        &lt;span style="color: blue"&gt;private readonly int &lt;/span&gt;maxExpressionsCount;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public &lt;/span&gt;MultipleValuesLoader(&lt;br /&gt;            &lt;span style="color: #2b91af"&gt;IDbConnection &lt;/span&gt;connection,&lt;br /&gt;            &lt;span style="color: blue"&gt;int &lt;/span&gt;maxExpressionsCount)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: blue"&gt;this&lt;/span&gt;.connection = connection;&lt;br /&gt;            &lt;span style="color: blue"&gt;this&lt;/span&gt;.maxExpressionsCount = maxExpressionsCount;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;public void &lt;/span&gt;Load(&lt;span style="color: #2b91af"&gt;DataTable &lt;/span&gt;table, &lt;br /&gt;            &lt;span style="color: blue"&gt;string &lt;/span&gt;sqlFormatString, &lt;span style="color: blue"&gt;int&lt;/span&gt;[] inputParameters)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: #2b91af"&gt;ConnectionState &lt;/span&gt;state = connection.State;&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;try&lt;br /&gt;            &lt;/span&gt;{&lt;br /&gt;                connection.Open();&lt;br /&gt;&lt;br /&gt;                &lt;span style="color: blue"&gt;int &lt;/span&gt;currentIndex = 0;&lt;br /&gt;                &lt;span style="color: blue"&gt;int &lt;/span&gt;totalCount = inputParameters.Length;&lt;br /&gt;&lt;br /&gt;                table.DataSet.EnforceConstraints = &lt;span style="color: blue"&gt;false&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;                &lt;span style="color: blue"&gt;while &lt;/span&gt;(currentIndex &amp;lt; totalCount)&lt;br /&gt;                {&lt;br /&gt;                    &lt;span style="color: blue"&gt;int &lt;/span&gt;endTo = currentIndex + maxExpressionsCount;&lt;br /&gt;                    &lt;span style="color: blue"&gt;if &lt;/span&gt;(endTo &amp;gt; totalCount)&lt;br /&gt;                        endTo = totalCount;&lt;br /&gt;&lt;br /&gt;                    &lt;span style="color: blue"&gt;string &lt;/span&gt;sqlString = GetSqlString(sqlFormatString,&lt;br /&gt;                        inputParameters, currentIndex, endTo);&lt;br /&gt;&lt;br /&gt;                    &lt;span style="color: #2b91af"&gt;IDbCommand &lt;/span&gt;command = connection.CreateCommand();&lt;br /&gt;                    command.CommandText = sqlString;&lt;br /&gt;&lt;br /&gt;                    &lt;span style="color: #2b91af"&gt;IDataReader &lt;/span&gt;reader = command.ExecuteReader();&lt;br /&gt;                    table.Load(reader);&lt;br /&gt;&lt;br /&gt;                    currentIndex = endTo;&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                table.DataSet.EnforceConstraints = &lt;span style="color: blue"&gt;true&lt;/span&gt;;&lt;br /&gt;            }&lt;br /&gt;            &lt;span style="color: blue"&gt;finally&lt;br /&gt;            &lt;/span&gt;{&lt;br /&gt;                &lt;span style="color: blue"&gt;if&lt;/span&gt;(state != &lt;span style="color: #2b91af"&gt;ConnectionState&lt;/span&gt;.Open)&lt;br /&gt;                    connection.Close();&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: blue"&gt;private static string &lt;/span&gt;GetSqlString(&lt;span style="color: blue"&gt;string &lt;/span&gt;sqlFormatString,&lt;br /&gt;            &lt;span style="color: blue"&gt;int&lt;/span&gt;[] inputParameters, &lt;span style="color: blue"&gt;int &lt;/span&gt;startFrom, &lt;span style="color: blue"&gt;int &lt;/span&gt;endTo)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: #2b91af"&gt;StringBuilder &lt;/span&gt;sql = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;StringBuilder&lt;/span&gt;();&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;for &lt;/span&gt;(&lt;span style="color: blue"&gt;int &lt;/span&gt;i = startFrom; i &amp;lt; endTo; i++)&lt;br /&gt;            {&lt;br /&gt;                sql.Append(inputParameters[i]);&lt;br /&gt;                sql.Append(&lt;span style="color: #a31515"&gt;&amp;quot;,&amp;quot;&lt;/span&gt;);&lt;br /&gt;            }&lt;br /&gt;            &lt;span style="color: green"&gt;//remove last seperator&lt;br /&gt;            &lt;/span&gt;sql.Remove(sql.Length - 1, 1);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: blue"&gt;return string&lt;/span&gt;.Format(sqlFormatString, sql.ToString());&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Wkr&amp;#243;tce i ta implementacja ulegnie zmianie, po widzę, że będę miał potrzebę ściągania danych nie tylko dla tabel z kluczem opartym na polu int, ale też na stringach.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Przykład wykorzystania:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public void &lt;/span&gt;LoadLearningOffersPrograms(&lt;span style="color: #2b91af"&gt;DataSet &lt;/span&gt;data, &lt;span style="color: blue"&gt;int&lt;/span&gt;[] offersIds)&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: blue"&gt;string &lt;/span&gt;sqlFormatString = &lt;span style="color: #a31515"&gt;&amp;quot;Select * From LearningOffersPrograms &amp;quot; &lt;/span&gt;+&lt;br /&gt;        &lt;span style="color: #a31515"&gt;&amp;quot;Where OfferId In({0}) Order By OfferId, Position&amp;quot;&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: #2b91af"&gt;MultipleValuesLoader &lt;/span&gt;loader = &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;MultipleValuesLoader&lt;/span&gt;(&lt;br /&gt;        &lt;span style="color: blue"&gt;this&lt;/span&gt;.oracleConnection1, 1000);&lt;br /&gt;&lt;br /&gt;    loader.Load(data.Tables[&lt;span style="color: #a31515"&gt;&amp;quot;Programs&amp;quot;&lt;/span&gt;], sqlFormatString, offersIds);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Por&amp;#243;wnanie wydajności: dla około 2 tys. ofert czas ściągania w pętli wszystkich program&amp;#243;w dla każdej oferty osobno wyni&amp;#243;sł 9 sekund, natomiast po wykorzystaniu zaproponowanej powyżej metody czas spadł do około 0.4 sekundy, czyli około 20 razy szybciej :). Wariant nieco mniej generyczny, z wykorzystaniem OracleConnection i OracleDataAdaptera, kt&amp;#243;ry był pierwszą implementacją pomysłu był nieco szybszy - ok. 0.35 sekundy.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-7939291534618739296?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/7939291534618739296/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=7939291534618739296' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/7939291534618739296'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/7939291534618739296'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2008/03/hurtowe-pobieranie-z-bazy-rekordw.html' title='Hurtowe pobieranie z bazy rekordów podrzędnych'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-3992648523256297457</id><published>2008-03-06T00:03:00.001+01:00</published><updated>2008-03-07T01:49:51.068+01:00</updated><title type='text'>Kopia bazy ODBC do pliku mdb</title><content type='html'>&lt;p&gt;Ostatnio miałem potrzebę zrobienia kopii bazy systemu, nad którym aktualnie pracuję. Docelowo chciałbym do wszystkich tabel dodać logowanie zmian, ale do tego czasu chciałem mieć czytelne migawki danych. Z tej racji, że baza źródłowa jest na razie dość mała, oraz ze względu na powszechność jako format docelowy wybrałem mdb, czyli bazę accessową.&lt;/p&gt; &lt;p&gt;Archiwizacja danych odbywa się w dwóch fazach:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;pobranie listy tabel przy użyciu metody GetSchema i ODBC,  &lt;li&gt;utworzenie w pętli tabel wynikowych za pomocą accessowej składni Select * Into TabelaWynikowa From TabelaZrodlowa IN '' [ODBC;CiagPolaczenia];&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;Kod nieco uproszczonej wersji:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System.Data;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System.Data.Odbc;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System.Data.OleDb;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;namespace&lt;/span&gt; GdnKonsulting.BackupToMdbLogic&lt;br /&gt;{&lt;br /&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;TransferData&lt;br /&gt;&lt;/span&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;readonly&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; connectionString;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;readonly&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; schemaName;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;readonly&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; tmpFile;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;readonly&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; outputFileName;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; TransferData(&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; connectionString, &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; schemaName,&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; outputFileName)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.connectionString = connectionString;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.schemaName = schemaName;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.outputFileName = outputFileName;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.tmpFile = System.IO.&lt;span style="color: rgb(43,145,175)"&gt;Path&lt;/span&gt;.GetTempFileName();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; CreateDbAndTransferAllTables()&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;AdoxWorker&lt;/span&gt;.CreateDatabase(&lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.tmpFile);&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;&amp;gt; tablesList = ReadTablesList();&lt;br /&gt;            ImportDataToDb(&lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.tmpFile, tablesList);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;&amp;gt; ReadTablesList()&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; (&lt;span style="color: rgb(43,145,175)"&gt;OdbcConnection&lt;/span&gt; connection = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;OdbcConnection&lt;/span&gt;(connectionString))&lt;br /&gt;            {&lt;/pre&gt;&lt;pre class="code"&gt;                connection.Open();&lt;br /&gt;                &lt;span style="color: rgb(43,145,175)"&gt;DataTable&lt;/span&gt; tables = connection.GetSchema(&lt;span style="color: rgb(163,21,21)"&gt;"TABLES"&lt;/span&gt;, &lt;br /&gt;                    &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;[] { &lt;span style="color: rgb(0,0,255)"&gt;null&lt;/span&gt;, schemaName });&lt;br /&gt;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;pre class="code"&gt;                &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;&amp;gt; result = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;&amp;gt;();&lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;foreach&lt;/span&gt; (&lt;span style="color: rgb(43,145,175)"&gt;DataRow&lt;/span&gt; row &lt;span style="color: rgb(0,0,255)"&gt;in&lt;/span&gt; tables.Rows)&lt;br /&gt;                {&lt;br /&gt;                    result.Add(tableName);                    &lt;br /&gt;                }&lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; result;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; ImportDataToDb(&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; outputFileName, &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;&amp;gt; tablesList)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; connectionString = &lt;span style="color: rgb(43,145,175)"&gt;String&lt;/span&gt;.Format(&lt;span style="color: rgb(163,21,21)"&gt;"Provider=Microsoft.Jet.OLEDB.4.0;"&lt;/span&gt; +&lt;br /&gt;                &lt;span style="color: rgb(163,21,21)"&gt;"Data Source={0}"&lt;/span&gt;, outputFileName);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; (&lt;span style="color: rgb(43,145,175)"&gt;OleDbConnection&lt;/span&gt; connection = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;OleDbConnection&lt;/span&gt;(connectionString))&lt;br /&gt;            {&lt;br /&gt;                &lt;span style="color: rgb(43,145,175)"&gt;OleDbCommand&lt;/span&gt; command = connection.CreateCommand();&lt;br /&gt;&lt;br /&gt;                connection.Open();&lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;foreach&lt;/span&gt; (&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; tableName &lt;span style="color: rgb(0,0,255)"&gt;in&lt;/span&gt; tablesList)&lt;br /&gt;                {&lt;br /&gt;                    &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; commandText = &lt;span style="color: rgb(43,145,175)"&gt;String&lt;/span&gt;.Format(&lt;br /&gt;                        &lt;span style="color: rgb(163,21,21)"&gt;"Select * Into {0} From {1} IN '' [ODBC;{2}];"&lt;/span&gt;,&lt;br /&gt;                        tableName, tableName, &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.connectionString);&lt;br /&gt;&lt;br /&gt;                    command.CommandText = commandText;&lt;br /&gt;                    command.ExecuteNonQuery();&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;I przykładowe wywołanie:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; connectionString = &lt;span style="color: rgb(163,21,21)"&gt;"Driver={Microsoft ODBC for Oracle};"&lt;/span&gt; +&lt;br /&gt;    &lt;span style="color: rgb(163,21,21)"&gt;"Server=ORCL;Uid=userName;Pwd=hasło;"&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(43,145,175)"&gt;TransferData&lt;/span&gt; transfer = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;TransferData&lt;/span&gt;(&lt;br /&gt;    connectionString, &lt;span style="color: rgb(163,21,21)"&gt;"userName"&lt;/span&gt;, &lt;span style="color: rgb(163,21,21)"&gt;"C:\Backup.mdb"&lt;/span&gt;);&lt;br /&gt;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;W wersji produkcyjnej mam kilka ulepszeń:&lt;/p&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;kompresję pliku accessowego. &lt;br /&gt;&lt;li&gt;dodanie zdarzenia wywoływanego po udanym zapisie tabeli do pliku wyjściowego, by niezwłocznie informować o tym użytkownika, &lt;br /&gt;&lt;li&gt;filtrowanie tabel (np. z oraclowych tabel znajdujących się w koszu, czyli o nazwach zaczynających się od "BIN$"), &lt;br /&gt;&lt;li&gt;dodawanie do nazw tabel wynikowych nazwy schematu - na przewidywaną ewentualność importu danych z kilku schematów.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;Aha, dodam jeszcze kod tworzący nowy plik mdb:&lt;/p&gt;&lt;pre class="code"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;AdoxWorker&lt;br /&gt;&lt;/span&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; CreateDatabase(&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; fileName)&lt;br /&gt;        {&lt;br /&gt;            ADOX.&lt;span style="color: rgb(43,145,175)"&gt;CatalogClass&lt;/span&gt; cat = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; ADOX.&lt;span style="color: rgb(43,145,175)"&gt;CatalogClass&lt;/span&gt;();&lt;br /&gt;&lt;br /&gt;            cat.Create(&lt;span style="color: rgb(163,21,21)"&gt;"Provider=Microsoft.Jet.OLEDB.4.0;"&lt;/span&gt; +&lt;br /&gt;                 &lt;span style="color: rgb(163,21,21)"&gt;"Data Source="&lt;/span&gt; + fileName + &lt;span style="color: rgb(163,21,21)"&gt;";"&lt;/span&gt; +&lt;br /&gt;                 &lt;span style="color: rgb(163,21,21)"&gt;"Jet OLEDB:Engine Type=5"&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Jak widać jest to wersja wymagająca wcześniejszego podłączenia bibliotek ADOX &lt;/p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;To byłoby na tyle :).&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-3992648523256297457?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/3992648523256297457/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=3992648523256297457' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/3992648523256297457'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/3992648523256297457'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2008/03/kopia-bazy-odbc-do-pliku-mdb.html' title='Kopia bazy ODBC do pliku mdb'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-5736408865311553199</id><published>2008-02-12T07:44:00.001+01:00</published><updated>2008-02-12T07:50:03.828+01:00</updated><title type='text'>Obsługa złożonych uprawnień użytkowników</title><content type='html'>&lt;p&gt;W artykule "Definiowanie złożonych uprawnień użytkowników" opisałem koncepcję struktury danych służącej do przechowywania informacji o uprawnieniach użytkowników. Dalszą częścią pracy nad uprawnieniami było zaprojektowanie komponentu .netowego do wyliczania uprawnienień użytkownika do określonych komponentów.&lt;/p&gt; &lt;p&gt;Wymagania:&lt;/p&gt; &lt;p&gt;a. szybkie wyszukiwanie danych, tak by narzut określania uprawnień nie był zbyt duży,&lt;/p&gt; &lt;p&gt;b. stosunkowo małe wymagania pamięciowe, tak by możliwe było przechowywanie w pamięci (stanie Sesji) kilkuset profili zabezpieczeń (na razie nie przewiduję większego obciążenia systemu:).&lt;/p&gt; &lt;p&gt;Pierwszym pomysłem, na który zwróciłem uwagę, było załadowanie danych z bazy danych do pliku xml, a następnie przeszukiwanie tegoż pliku za pomocą zapytań XPath. Niewątpliwą zaletą takiego rozwiązania byłoby mała zajętość pamięci - w stanie sesji można byłoby przechowwywać tylko ten xml. Jednak przyjęta koncepcja uprawnień oznaczałaby konieczność wielokrotnego przeszukiwania wszystkich węzłów, co niewątpliwie odbiłoby się na wydajności.&lt;/p&gt; &lt;p&gt;W końcu zdecydowałem się na wykorzystanie struktury drzewiastej:&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.gdnkonsulting.waw.pl/Downloads/Misc/Obsugazoonychuprawnieuytkownikw_5F2/SecurityDiagram.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="496" alt="SecurityDiagram" src="http://www.gdnkonsulting.waw.pl/Downloads/Misc/Obsugazoonychuprawnieuytkownikw_5F2/SecurityDiagram_thumb.png" width="763" border="0"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Na najniższym poziomie jest obiekt typu CRUD, który zawiera informację o uprawnieniach użytkownika do poszczególnych czynności (create, read, update i delete). Uprawnienia są przechowywane w formie trójwartościowych enumów AuthorizationType, którę we właściwościach publicznych typu CanCreate…CanDelete są konwertowane na wartości true/false (grant =&amp;gt; true, pozostałe =&amp;gt; false).  &lt;p&gt;Obiekty AuthorizationNode przechowują informację o poszczególnych węzłach, każdy z nich ma kolekcję obiektów typu Condition, które definiują warunki i posiadają określone uprawnienia jeśli warunki są spełnione. Dla określonych danych wejściowych można określić które warunki są spełnione i używając arytmetyki uprawnień zaprezentowanej w poprzednim artykule można wyliczyć uprawnienia sumaryczne. Do wyliczania uprawnień użyłem biblioteki ecalc z &lt;a href="http://www.codeproject.com/KB/cs/ExpressionEval.aspx"&gt;http://www.codeproject.com/KB/cs/ExpressionEval.aspx&lt;/a&gt;.  &lt;p&gt;Dla poprawy wydajności uprawnienia wyliczane są raz, poza tym uprawnienia wyliczane są tylko dla tych gałęzi, dla których taka informacja jest pobierana (zazwyczaj też z gałęzi nadrzędnych). Oczywiście wiąże to się z konicznością kasowane poprzednich wyliczeń przy zmianie danych (metoda SetCurrentData ustawia wszystkie uprawnienia rekurencyjnie w dół dla całego drzewa na null).  &lt;p&gt;Na początku jako dane źródłowe wykorzystywałem obiekty typu DataRow, jednak by umożliwić wykorzystywanie innych typów danych utworzyłem interfejs IDataContainer, który zawiera jedynie indeksator:&lt;pre class="code"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;interface&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;IDataContainer&lt;br /&gt;&lt;/span&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;object&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;[&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; fieldName] {&lt;span style="color: rgb(0,0,255)"&gt;get&lt;/span&gt;; }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;Dodałem dwie jego implementacje SimpleDataContainer korzystająca z obiektu Dictionary&amp;lt;string, object&amp;gt; oraz DataRowDataContainer wykorzystującą DataRow, kod tego ostatniego:&lt;pre class="code"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;internal&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;DataRowDataContainer&lt;/span&gt; : &lt;span style="color: rgb(43,145,175)"&gt;IDataContainer&lt;br /&gt;&lt;/span&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;DataRow&lt;/span&gt; dataRow;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; DataRowDataContainer(&lt;span style="color: rgb(43,145,175)"&gt;DataRow&lt;/span&gt; row)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.dataRow = row;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;DataRow&lt;/span&gt; DataRow&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;get&lt;/span&gt; { &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; dataRow; }&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;set&lt;/span&gt; { dataRow = &lt;span style="color: rgb(0,0,255)"&gt;value&lt;/span&gt;; }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;object&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;[&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; fieldName]&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;get&lt;/span&gt; &lt;br /&gt;            { &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; dataRow[fieldName]; }&lt;br /&gt;        }&lt;br /&gt;    }&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;Dla uproszczenia najczęściej wykonywanych zadań rozszerzyłem klasę AuthorizationNode o kilka dodatkowych metod. &lt;br /&gt;&lt;p&gt;&lt;a href="http://www.gdnkonsulting.waw.pl/Downloads/Misc/Obsugazoonychuprawnieuytkownikw_5F2/TestClassDiagram.png"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="376" alt="TestClassDiagram" src="http://www.gdnkonsulting.waw.pl/Downloads/Misc/Obsugazoonychuprawnieuytkownikw_5F2/TestClassDiagram_thumb.png" width="211" border="0"&gt;&lt;/a&gt; &lt;br /&gt;&lt;p&gt;Np. metoda GetNonInsertableChilds zwraca kolekcję nazw wszystkich dzieci, które nie są dostępne przy dodawaniu nowych danych. &lt;br /&gt;&lt;p&gt;Przykłady wykorzystania uprawnień:&lt;pre class="code"&gt;        [&lt;span style="color: rgb(43,145,175)"&gt;Test&lt;/span&gt;]&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; CheckJamesBondsRightsToOffers()&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,128,0)"&gt;// additional method that set up current &lt;br /&gt;&lt;/span&gt;            &lt;span style="color: rgb(0,128,0)"&gt;// user&lt;br /&gt;&lt;/span&gt;            &lt;span style="color: rgb(43,145,175)"&gt;Identity&lt;/span&gt;.SetCurrentIdentiy(&lt;span style="color: rgb(163,21,21)"&gt;"james bond"&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,128,0)"&gt;// componente that load privileages data &lt;br /&gt;&lt;/span&gt;            &lt;span style="color: rgb(0,128,0)"&gt;// for current user &lt;br /&gt;&lt;/span&gt;            &lt;span style="color: rgb(43,145,175)"&gt;ComponentAuthorizationBL&lt;/span&gt; logic = &lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;ComponentAuthorizationBL&lt;/span&gt;();&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;ComponentAuthorization&lt;/span&gt; authorization =&lt;br /&gt;                logic.GetComponentAuthorization();&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,128,0)"&gt;// load some data &lt;br /&gt;&lt;/span&gt;            &lt;span style="color: rgb(43,145,175)"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;, &lt;span style="color: rgb(0,0,255)"&gt;object&lt;/span&gt;&amp;gt; dictionary = &lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;, &lt;span style="color: rgb(0,0,255)"&gt;object&lt;/span&gt;&amp;gt;();&lt;br /&gt;            dictionary.Add(&lt;span style="color: rgb(163,21,21)"&gt;"OwnOffer"&lt;/span&gt;, &lt;span style="color: rgb(0,0,255)"&gt;false&lt;/span&gt;);&lt;br /&gt;            dictionary.Add(&lt;span style="color: rgb(163,21,21)"&gt;"ReferenceBookId"&lt;/span&gt;, 1);&lt;br /&gt;            dictionary.Add(&lt;span style="color: rgb(163,21,21)"&gt;"OfferType"&lt;/span&gt;, (&lt;span style="color: rgb(0,0,255)"&gt;short&lt;/span&gt;)&lt;span style="color: rgb(43,145,175)"&gt;OfferType&lt;/span&gt;.Standard);&lt;br /&gt;&lt;br /&gt;            authorization.SetCurrentData(dictionary);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,128,0)"&gt;// evaluate privelages for one node&lt;br /&gt;&lt;/span&gt;            &lt;span style="color: rgb(43,145,175)"&gt;CRUD&lt;/span&gt; crud = authorization.GetCRUD(&lt;span style="color: rgb(163,21,21)"&gt;"LearningOffers/MainData"&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;Assert&lt;/span&gt;.IsTrue(crud.CanRead);&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;Assert&lt;/span&gt;.IsFalse(crud.CanCreate);&lt;br /&gt;        }&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;&amp;nbsp; &lt;p&gt;cdn :).&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-5736408865311553199?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/5736408865311553199/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=5736408865311553199' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/5736408865311553199'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/5736408865311553199'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2008/02/obsuga-zoonych-uprawnie-uytkownikw.html' title='Obsługa złożonych uprawnień użytkowników'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-5413620536358503193</id><published>2008-02-05T01:23:00.001+01:00</published><updated>2008-02-05T01:23:44.232+01:00</updated><title type='text'>Zamykanie splash screena z innego wątku</title><content type='html'>&lt;p&gt;Od jakiegoś czasu dodaję do swoich produkcji splash screen wyświetlany w czasie uruchamiania aplikacji. Do tej pory było to statyczne okienko z jakimś miłym do oka obrazkiem oraz komunikatem proszącym użytkownika o chwilę cierpliwości. Kod wyglądał mniej więcej tak:&lt;/p&gt;&lt;pre class="code"&gt;        &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; Main()&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;SplashForm&lt;/span&gt; startForm = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;SplashForm&lt;/span&gt;();&lt;br /&gt;            startForm.Show();&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;Application&lt;/span&gt;.DoEvents();&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;MainForm&lt;/span&gt;.Run(startForm);&lt;br /&gt;        }&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Natomiast w kodzie głównego formularza aplikacji splash form był zamykany:&lt;/p&gt;&lt;pre class="code"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;MainForm&lt;/span&gt; : System.Windows.Forms.&lt;span style="color: rgb(43,145,175)"&gt;Form&lt;br /&gt;&lt;/span&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; MainForm()&lt;br /&gt;        {&lt;br /&gt;            InitializeComponent();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;IDisposable&lt;/span&gt; splashObject;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;IDisposable&lt;/span&gt; SplashObject&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;set &lt;/span&gt;{ &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.splashObject = &lt;span style="color: rgb(0,0,255)"&gt;value&lt;/span&gt;; }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; Run(&lt;span style="color: rgb(43,145,175)"&gt;IDisposable&lt;/span&gt; splashObject)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;MainForm&lt;/span&gt; frm = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;MainForm&lt;/span&gt;();&lt;br /&gt;            frm.SplashObject = splashObject;&lt;/pre&gt;&lt;pre class="code"&gt;            &lt;span style="color: rgb(43,145,175)"&gt;Application&lt;/span&gt;.Run(frm);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; MainForm_Shown(&lt;span style="color: rgb(0,0,255)"&gt;object&lt;/span&gt; sender, &lt;span style="color: rgb(43,145,175)"&gt;EventArgs&lt;/span&gt; e)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;if&lt;/span&gt; (splashObject != &lt;span style="color: rgb(0,0,255)"&gt;null&lt;/span&gt;)&lt;br /&gt;            {&lt;br /&gt;                splashObject.Dispose();&lt;br /&gt;            }&lt;br /&gt;        }&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Jak widać otwarty formularz startowy był przekazywany do głównego formularza aplikacji, a dopiero po załadowaniu tegoż był niszczony.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Przy okazji realizowania ostatniej aplikacji zapragnąłem zdynamizowania splash screena. Po paru eksperymentach uzyskałem satysfakcjonujące mnie rozwiązanie, w którym wykorzystałem oddzielny wątek w którym tworzony jest formularz startowy wyposażony w oddzielną pętlę komunikatów:&lt;/p&gt;&lt;pre class="code"&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; ShowSplashScreenInOtherThread()&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;ThreadStart&lt;/span&gt; starter = &lt;span style="color: rgb(0,0,255)"&gt;delegate&lt;br /&gt;&lt;/span&gt;            {&lt;br /&gt;                &lt;span style="color: rgb(43,145,175)"&gt;SplashForm&lt;/span&gt; startForm = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;SplashForm&lt;/span&gt;();&lt;br /&gt;                &lt;span style="color: rgb(43,145,175)"&gt;Application&lt;/span&gt;.Run(startForm);&lt;br /&gt;            };&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Thread&lt;/span&gt;(starter).Start();&lt;br /&gt;        }&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;Teraz pozostało tylko rozwiązać problem jak zamknąć niepotrzebny już formularz startowy z poziomu głównego formularza aplikacji? Próba przekazania do głównego formularza aplikacji referencji do wątku, w którym działa splash screen, a potem użycie Abort - skuteczne ale kilkusekundowa zwłoka.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Rozwiązanie:&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Utworzyłem pomocniczy obiekt DisposeTrigger, który implementuje standardowy interfejs IDisposable w ten sposób, że przy Dispose generuje zdarzenie Disposing:&lt;/p&gt;&lt;pre class="code"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;DisposeTrigger&lt;/span&gt; : &lt;span style="color: rgb(43,145,175)"&gt;IDisposable&lt;br /&gt;&lt;/span&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;event&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;EventHandler&lt;/span&gt; Disposing;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;protected&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;virtual&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; OnDisposing(&lt;span style="color: rgb(43,145,175)"&gt;EventArgs&lt;/span&gt; e)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;if&lt;/span&gt; (Disposing != &lt;span style="color: rgb(0,0,255)"&gt;null&lt;/span&gt;)&lt;br /&gt;            {&lt;br /&gt;                Disposing(&lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;, e);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;        #region&lt;/span&gt; IDisposable Members&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; Dispose()&lt;br /&gt;        {&lt;br /&gt;            OnDisposing(&lt;span style="color: rgb(43,145,175)"&gt;EventArgs&lt;/span&gt;.Empty);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;        #endregion&lt;br /&gt;&lt;/span&gt;    }&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;Obiekt ten stał się łącznikiem między wątkiem splash screena, a głównym wątkiem aplikacji. W kodzie splash screena dodałem obsługę zdarzenia Disposing:&lt;/p&gt;&lt;pre class="code"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;SplashForm&lt;/span&gt; : &lt;span style="color: rgb(43,145,175)"&gt;Form&lt;br /&gt;&lt;/span&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; SplashForm(&lt;span style="color: rgb(43,145,175)"&gt;DisposeTrigger&lt;/span&gt; trigger)&lt;br /&gt;        {&lt;br /&gt;            InitializeComponent();&lt;br /&gt;            trigger.Disposing += &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;EventHandler&lt;/span&gt;(trigger_Disposing);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; trigger_Disposing(&lt;span style="color: rgb(0,0,255)"&gt;object&lt;/span&gt; sender, &lt;span style="color: rgb(43,145,175)"&gt;EventArgs&lt;/span&gt; e)&lt;br /&gt;        {&lt;br /&gt;            CloseSplashScreen();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;delegate&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;CloseHandler&lt;/span&gt;();&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; CloseSplashScreen()&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;if&lt;/span&gt; (&lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.InvokeRequired)&lt;br /&gt;            {&lt;br /&gt;                &lt;span style="color: rgb(43,145,175)"&gt;CloseHandler&lt;/span&gt; handler = CloseSplashScreen;&lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.Invoke(handler);&lt;br /&gt;            }&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;else&lt;br /&gt;&lt;/span&gt;            {&lt;br /&gt;                &lt;span style="color: rgb(43,145,175)"&gt;Application&lt;/span&gt;.ExitThread();&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;Natomiast w metodzie Main tworzony jest obiekt DisposeTrigger, przekazywany zarówno do konstruktora splash screena jak i do głównego formularza aplikacji:&lt;/p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;pre class="code"&gt;        &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; Main()&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;DisposeTrigger&lt;/span&gt; splashDisposing = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;DisposeTrigger&lt;/span&gt;();&lt;br /&gt;            ShowSplashScreenInOtherThread(splashDisposing);&lt;br /&gt;                &lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;MainForm&lt;/span&gt;.Run(splashDisposing);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; ShowSplashScreenInOtherThread(&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;DisposeTrigger&lt;/span&gt; trigger)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;ThreadStart&lt;/span&gt; starter = &lt;span style="color: rgb(0,0,255)"&gt;delegate&lt;br /&gt;&lt;/span&gt;            {&lt;br /&gt;                &lt;span style="color: rgb(43,145,175)"&gt;SplashForm&lt;/span&gt; startForm = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;SplashForm&lt;/span&gt;(trigger);&lt;br /&gt;                &lt;span style="color: rgb(43,145,175)"&gt;Application&lt;/span&gt;.Run(startForm);&lt;br /&gt;            };&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Thread&lt;/span&gt;(starter).Start();&lt;br /&gt;        }&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;Jak widać wcześniej napisane aplikacje korzystające z mojego frameworka będą dalej działały bez konieczności jakiejkolwiek modyfikacji.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-5413620536358503193?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/5413620536358503193/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=5413620536358503193' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/5413620536358503193'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/5413620536358503193'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2008/02/zamykanie-splash-screena-z-innego-wtku_04.html' title='Zamykanie splash screena z innego wątku'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-4194858728206159306</id><published>2008-01-30T04:03:00.001+01:00</published><updated>2008-02-05T01:25:53.929+01:00</updated><title type='text'>Definiowanie złożonych uprawnień użytkowników</title><content type='html'>&lt;p&gt;Ostatnio zmagałem się z problemem w miarę eleganckiego opisu uprawnień użytkowników do aplikacji webowej. Chciałem skorzystać z wbudowanych mechanizmów Role Base Security, ale okazało się, że potrzebuję większej ziarnistości uprawnień. &lt;/p&gt; &lt;p&gt;Przykłady potrzebnych mi uprawnień: &lt;/p&gt; &lt;ul&gt; &lt;li&gt;"użytkownicy powinni mieć dostęp do wszystkich ofert złożonych przez siebie, lecz aktualnie mogą edytować tylko oferty określonego typu, natomiast inne mogą tylko czytać",  &lt;li&gt;"zwykli użytkownicy nie mogą ustalać sygnatur złożonych ofert - to mogą czynić tylko managerowie",  &lt;li&gt;"różne typy ofert udostępniają do edycji różne zestawy pól" itd.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Przez pewien czas zastanawiałem się w jakiej postaci opisać uprawnienia. Pierwszy pomysł był taki, zdefiniować je w pliku xml, np.:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Permisions&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;  &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;LearningOffers&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;C&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;True&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;R&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;True&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;U&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;True&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;D&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;True&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;MainData&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;      &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;OfferId&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;C&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;False&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;U&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;False&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;      &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Signature&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;C&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;False&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;U&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;False&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;      &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Title&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;OfferType&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;Standardowa&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;C&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;False&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;U&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;False&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;    &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;MainData&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Teams&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;OfferType&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;'&lt;span style="color: rgb(0,0,255)"&gt;Konkursowa,Standardowa&lt;/span&gt;'&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;TemplateOffers&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;OfferType&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;'&lt;span style="color: rgb(0,0,255)"&gt;Szablon&lt;/span&gt;'&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;    &lt;br /&gt;  &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;LearningOffers&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;  &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;LearningOffers&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;OwnOffer&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;True&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;R&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;True&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; /&amp;gt;&lt;br /&gt;  &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;LearningOffers&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;OfferType&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;Konkursowa&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;C&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;False&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;U&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;False&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;/&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Permisions&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;To nawet sensownie wygląda, choć powtarzanie gałęzi LearningOffers, tak by wpisać wszystkie możliwe warunki jest IMHO mało xml-owe. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;Ale sprawa się komplikuje jak uwzględni się role, do których może przynależeć użytkownik. Wówczas dla każdej roli należałoby zdefiniować osobne uprawnienia, co znacząco wydłuży rozmiar definicji uprawnień dla wszystkich. Poza tym część użytkowników będzie należeć do kilku ról, więc dla nich należałoby wybrać największe dostępne uprawnienia.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Tak więc, nie przekreślając całkiem xml w samej aplikacji, postanowiłem definiować uprawnienia w innej postaci. &lt;/p&gt;&lt;pre class="csharpcode"&gt;&lt;span class="kwrd"&gt;Create&lt;/span&gt; &lt;span class="kwrd"&gt;Table&lt;/span&gt; ComponentAuthorizations(&lt;br /&gt;  ComponentPath &lt;span class="kwrd"&gt;Varchar&lt;/span&gt;(1000) &lt;span class="kwrd"&gt;Not&lt;/span&gt; &lt;span class="kwrd"&gt;Null&lt;/span&gt;,&lt;br /&gt;  &lt;span class="kwrd"&gt;Role&lt;/span&gt; &lt;span class="kwrd"&gt;Varchar&lt;/span&gt;(30) &lt;span class="kwrd"&gt;Not&lt;/span&gt; &lt;span class="kwrd"&gt;Null&lt;/span&gt;,&lt;br /&gt;  Condition &lt;span class="kwrd"&gt;Varchar&lt;/span&gt;(200),&lt;br /&gt;  CanCreate &lt;span class="kwrd"&gt;Numeric&lt;/span&gt;(1) &lt;span class="kwrd"&gt;Not&lt;/span&gt; &lt;span class="kwrd"&gt;Null&lt;/span&gt;,&lt;br /&gt;  CanRead &lt;span class="kwrd"&gt;Numeric&lt;/span&gt;(1) &lt;span class="kwrd"&gt;Not&lt;/span&gt; &lt;span class="kwrd"&gt;Null&lt;/span&gt;,&lt;br /&gt;  CanUpdate &lt;span class="kwrd"&gt;Numeric&lt;/span&gt;(1) &lt;span class="kwrd"&gt;Not&lt;/span&gt; &lt;span class="kwrd"&gt;Null&lt;/span&gt;,&lt;br /&gt;  CanDelete &lt;span class="kwrd"&gt;Numeric&lt;/span&gt;(1) &lt;span class="kwrd"&gt;Not&lt;/span&gt; &lt;span class="kwrd"&gt;Null&lt;/span&gt;,&lt;br /&gt;&lt;br /&gt;  &lt;span class="kwrd"&gt;Constraint&lt;/span&gt; Uq_ComponentAuthorizations &lt;span class="kwrd"&gt;Unique&lt;/span&gt;(&lt;br /&gt;    ComponentPath, &lt;span class="kwrd"&gt;Role&lt;/span&gt;, Condition)&lt;br /&gt;)&lt;/pre&gt;&lt;br /&gt;&lt;style type="text/css"&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt;	font-size: small;&lt;br /&gt;	color: black;&lt;br /&gt;	font-family: consolas, "Courier New", courier, monospace;&lt;br /&gt;	background-color: #ffffff;&lt;br /&gt;	/*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt;	background-color: #f4f4f4;&lt;br /&gt;	width: 100%;&lt;br /&gt;	margin: 0em;&lt;br /&gt;}&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I potem przykładowe dane:&lt;/p&gt;&lt;br /&gt;&lt;table cellspacing="0" cellpadding="2" width="749" border="0"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="187"&gt;ComponentPath&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="75"&gt;Role&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="167"&gt;Condition&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="70"&gt;CanCreate&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="66"&gt;CanRead&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="91"&gt;CanUpdate&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="86"&gt;CanDelete&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="188"&gt;Teachers&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="75"&gt;&amp;nbsp;&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="167"&gt;&amp;nbsp;&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="70"&gt;0&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="69"&gt;0&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="91"&gt;0&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="86"&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="188"&gt;Teachers&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="75"&gt;Manager&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="167"&gt;&amp;nbsp;&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="70"&gt;0&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="71"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="91"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="86"&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="188"&gt;Teachers&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="75"&gt;Teacher&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="167"&gt;[OwnOffer] = true&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="70"&gt;0&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="72"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="91"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="86"&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="188"&gt;Teachers/OldTeacherCode&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="75"&gt;Teacher&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="167"&gt;&amp;nbsp;&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="70"&gt;0&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="73"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="91"&gt;0&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="86"&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="187"&gt;Teachers/OldTeacherCode&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="75"&gt;Manager&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="167"&gt;&amp;nbsp;&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="70"&gt;0&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="74"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="91"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="86"&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="188"&gt;LearningOffers&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="75"&gt;Teacher&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="167"&gt;([OfferType] = 2) and ([OwnOffer] = true)&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="70"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="74"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="91"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="86"&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="188"&gt;LearningOffers&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="75"&gt;Teacher&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="167"&gt;[OfferType] = 1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="70"&gt;0&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="74"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="91"&gt;0&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="86"&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="188"&gt;LearningOffers&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="75"&gt;Manager&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="167"&gt;&amp;nbsp;&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="70"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="75"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="91"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="87"&gt;1&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;Założenia wyliczania uprawnień dla określonego komponentu:&lt;/p&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;jeżeli użytkownik przynależy do kilku grup, to dla danego poziomu i warunku wybierane są najwyższe uprawnienia (CRUD), &lt;br /&gt;&lt;li&gt;dla każdego poziomu uprawnienia są sumowane binarnie, czyli jeśli spełniony jest jakikolwiek warunek dla którego uprawnienia są typu 1, to ostateczne uprawnienia też będą wynosić 1, &lt;br /&gt;&lt;li&gt;uprawnienia komponentów niższego rzędu (dzieci) nie mogą być wyższe niż uprawnienia rodziców.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;Potem przemyślałem jeszcze powyższy schemat i postanowiłem obok poziomów uprawnień 0 (Absent, czyli brak uprawnień) i 1 (Grant, czyli są uprawnienia), dodać trzeci poziom uprawnień: -1 (Revoke), który będzie oznaczał wycofanie uprawnień: w przypadku spełnienia jakiegokolwiek warunku z poziomem -1 ostatecznie użytkownik nie będzie miał uprawnień do danego komponentu.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Ostateczna arytmetyka uprawnień:&lt;/p&gt;&lt;br /&gt;&lt;table cellspacing="0" cellpadding="2" width="443" border="0"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="53"&gt;Lp.&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="135"&gt;Warunek pierwszy&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="99"&gt;Warunek drugi&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="154"&gt;Łączne uprawnienia&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="53"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="135"&gt;Absent&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="98"&gt;Absent&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="154"&gt;Absent&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="54"&gt;2&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="134"&gt;Absent&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="98"&gt;Grant&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="154"&gt;Grant&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="54"&gt;3&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="134"&gt;Grant&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="98"&gt;Absent&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="154"&gt;Grant&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="54"&gt;4&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="134"&gt;Grant&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="98"&gt;Grant&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="154"&gt;Grant&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="54"&gt;5&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="134"&gt;Grant&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="98"&gt;Revoke&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="154"&gt;Revoke&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="54"&gt;6&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="134"&gt;Absent&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="99"&gt;Revoke&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="154"&gt;Revoke&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;I potem przykład wycofania uprawnień:&lt;/p&gt;&lt;br /&gt;&lt;table cellspacing="0" cellpadding="2" width="594" border="0"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="155"&gt;LearningOffers&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="76"&gt;&amp;nbsp;&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="169"&gt;[ReferenceBookId] &amp;lt; 5&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="51"&gt;-1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="49"&gt;-1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="48"&gt;-1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="44"&gt;-1&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td valign="top" width="152"&gt;LearningOffers&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="78"&gt;Manager&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="169"&gt;[ReferenceBookId] &amp;lt; 5&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="51"&gt;-1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="49"&gt;1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="48"&gt;-1&lt;/td&gt;&lt;br /&gt;&lt;td valign="top" width="45"&gt;-1&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;Powyższe warunki zabierają wszystkim uprawnienia do odczytu i edycji ofert z ReferenceBookId mniejszym od 5, natomiast Managerowie mają w dalszym ciągu prawa do odczytu tychże ofert.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;W następnym artykule napiszę o tym jak wykorzystałem tę definicję uprawnień na poziomie aplikacji.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-4194858728206159306?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/4194858728206159306/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=4194858728206159306' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/4194858728206159306'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/4194858728206159306'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2008/01/definiowanie-zoonych-uprawnie.html' title='Definiowanie złożonych uprawnień użytkowników'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-2535066418083569199</id><published>2008-01-22T08:53:00.001+01:00</published><updated>2008-01-22T10:55:41.943+01:00</updated><title type='text'>MsAccCorrector</title><content type='html'>&lt;p&gt;Większość użytkowników Accessa zauważyła, że Access w tworzonych nowych obiektach, np. formularzach, raportach, używa zlokalizowanych nazw, np. "szczegóły", "nagłówek", itp. Póki uruchamiamy takową aplikację na komputerze z zainstalowaną polską wersją Accessa, w dodatku na polskim systemie Windows, to nie ma żadnego problemu. Jednak jeśli zaczynamy wyślemy aplikację dla osoby mającej np. wersję angielską, to pojawiają się problemy. W tej sytuacji należy usunąć wszystkie polskie literki.&lt;/p&gt; &lt;p&gt;Przy okazji realizacji dla jednego z klientów modernizacji rozwijanej od długiego czasu aplikacji accessowej, tak by działała poprawnie z runtime accessowym, opracowałem przy współpracy z Maciejem Tokarzem aplikację konwertującą polskie znaki. Potem oprócz zamiany polskich znaków dodaliśmy także wyszukiwanie określonych fraz w całym pliku accessowym.&lt;/p&gt; &lt;p&gt;Obsługa&lt;/p&gt; &lt;p&gt;Aplikacja jest prosta w obsłudze, choć fakt, że korzysta z wyrażeń regularnych może odstraszyć potencjalnych użytkowników. Dla początkujących regexowców polecam instalację świetnego edytora &lt;a href="http://www.ultrapico.com/Expresso.htm"&gt;Expresso&lt;/a&gt;, który udostępnia łatwy w obsłudzę interfejs do projektowania wyrażeń regularnych.&lt;/p&gt; &lt;p&gt;Setup aplikacji można ściągnąć &lt;a href="http://www.gdnkonsulting.waw.pl/Downloads/Misc/MsAccCorrectorSetup.zip"&gt;stąd.&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Aplikacja jest napisana w VB.NET i jeśli znajdą się chętni, by ją rozwijać, to mogę wrzucić projekt na jakiś publiczny serwer, np.: &lt;a href="http://code.google.com/hosting/"&gt;http://code.google.com/hosting/&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-2535066418083569199?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/2535066418083569199/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=2535066418083569199' title='Komentarze (1)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/2535066418083569199'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/2535066418083569199'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2008/01/msacccorrector.html' title='MsAccCorrector'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-6437982899484910062</id><published>2007-08-18T11:36:00.001+02:00</published><updated>2007-08-18T11:36:11.805+02:00</updated><title type='text'>Słownie złotych w c#</title><content type='html'>&lt;p&gt;Swoją przygodę z programowaniem zacząłem od napisania w VBA excelowym potrzebnej mi funkcji konwertującej liczby z postaci numerycznej do słownej - stąd darzę tę funkcję dużym sentymentem :-). &lt;/p&gt; &lt;p&gt;Ostatnio potrzebowałem takiej funkcjonalności w aplikacji c#, nie chcąc bezpośrednio korzystać z dość starego kodu, napisałem nową fukcją, aczkolwiek opartą na niemal tym samym algorytmie, co 10 lat wcześniej.&lt;/p&gt; &lt;p&gt;Zasadnicza różnica w stosunku do poprzedniej implementacji, polega na tym, że dane tekstowe umieściłem w pliku xml, a nie bezpośrednio w kodzie programu, co moim zdaniem poprawiło czytelność kodu.&lt;/p&gt; &lt;p&gt;Dane te są dwojga rodzaju: liczebniki słownie oraz odmiana końcówek. &lt;/p&gt; &lt;p&gt;Fragment pliku xml z liczebnikami słownie:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;NumbersToText&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;        &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Number&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;value&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;1&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;jeden&lt;span style="color: rgb(0,0,255)"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Number&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Number&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;value&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;2&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;dwa&lt;span style="color: rgb(0,0,255)"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Number&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Number&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;value&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;3&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;trzy&lt;span style="color: rgb(0,0,255)"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Number&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;    &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;NumbersToText&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;Fragment pliku xml z końcówkami:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;    &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Declinations&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Noun&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;position&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;0&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;NominativeSingular&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;grosz&lt;span style="color: rgb(0,0,255)"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;NominativeSingular&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;NominativePlural&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;grosze&lt;span style="color: rgb(0,0,255)"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;NominativePlural&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;GenitivePlural&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;grosze&lt;span style="color: rgb(0,0,255)"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;GenitivePlural&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Noun&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Noun&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt; &lt;/span&gt;&lt;span style="color: rgb(255,0,0)"&gt;position&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;=&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;1&lt;/span&gt;"&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;NominativeSingular&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;złoty&lt;span style="color: rgb(0,0,255)"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;NominativeSingular&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;NominativePlural&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;złote&lt;span style="color: rgb(0,0,255)"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;NominativePlural&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;            &amp;lt;&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;GenitivePlural&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;złotych&lt;span style="color: rgb(0,0,255)"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;GenitivePlural&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;        &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Noun&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;    &amp;lt;/&lt;/span&gt;&lt;span style="color: rgb(163,21,21)"&gt;Declinations&lt;/span&gt;&lt;span style="color: rgb(0,0,255)"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;Oczywiście w kodzie samej funkcji musiałem dodać przeszukiwanie pliku xml, do czego użyłem xpath:&lt;/p&gt;&lt;pre class="code"&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; OneAmount(&lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; number)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; xpath = &lt;span style="color: rgb(43,145,175)"&gt;String&lt;/span&gt;.Format(&lt;br /&gt;                &lt;span style="color: rgb(163,21,21)"&gt;@"/Data/NumbersToText/Number[@value={0}]"&lt;/span&gt;, number);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;XmlNode&lt;/span&gt; node = doc.SelectSingleNode(xpath);&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; (node != &lt;span style="color: rgb(0,0,255)"&gt;null&lt;/span&gt; ? node.InnerText : &lt;span style="color: rgb(0,0,255)"&gt;null&lt;/span&gt;);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; OneDeclination(&lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; position, &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; declination)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; xpath = &lt;span style="color: rgb(43,145,175)"&gt;String&lt;/span&gt;.Format(&lt;br /&gt;                &lt;span style="color: rgb(163,21,21)"&gt;@"/Data/Declinations/Noun[@position={0}]/{1}"&lt;/span&gt;, &lt;br /&gt;                position, declination);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;XmlNode&lt;/span&gt; node = doc.SelectSingleNode(xpath);&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; (node != &lt;span style="color: rgb(0,0,255)"&gt;null&lt;/span&gt; ? node.InnerText : &lt;span style="color: rgb(0,0,255)"&gt;null&lt;/span&gt;);&lt;br /&gt;        }&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;Potem mając już wyciągnięte odpowiednie wartości słowne&amp;nbsp;mogłem zrobić z nich użytek konwertując poszczególne segmenty trzycyfrowe:&lt;/p&gt;&lt;pre class="code"&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;&amp;gt; Number3DigitToTextList(&lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; number, &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; position)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;if&lt;/span&gt; (number &amp;lt;= 1 || (number &amp;gt;= 10 &amp;amp;&amp;amp; number &amp;lt;= 19))&lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; SimpleNumber(number, position);&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;else&lt;br /&gt;&lt;/span&gt;                &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; ComplicatedNumber(number, position);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;&amp;gt; SimpleNumber(&lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; number, &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; position)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;&amp;gt; result = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;&amp;gt;();&lt;br /&gt;            result.Add(OneAmount(number));&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;if&lt;/span&gt; (number == 1)&lt;br /&gt;                result.Add(OneDeclination(position, &lt;span style="color: rgb(163,21,21)"&gt;"NominativeSingular"&lt;/span&gt;));&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;else&lt;br /&gt;&lt;/span&gt;                result.Add(OneDeclination(position, &lt;span style="color: rgb(163,21,21)"&gt;"GenitivePlural"&lt;/span&gt;));&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; result;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;&amp;gt; ComplicatedNumber(&lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; number, &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; position)&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;&amp;gt; result = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;&amp;gt;();&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;if&lt;/span&gt; (number &amp;gt;= 100)&lt;br /&gt;            {&lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; hundreds = (number / 100) * 100;&lt;br /&gt;                result.Add(OneAmount(hundreds));&lt;br /&gt;                number = number - hundreds;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; tens = (number / 10) * 10;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;if&lt;/span&gt; (tens != 0)&lt;br /&gt;            {&lt;br /&gt;                result.Add(OneAmount(tens));&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;int&lt;/span&gt; ones = number - tens;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;if&lt;/span&gt; (ones != 0)&lt;br /&gt;            {&lt;br /&gt;                result.Add(OneAmount(ones));&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;if&lt;/span&gt; (ones == 1 || ones == 0 || ones &amp;gt; 4)&lt;br /&gt;                result.Add(OneDeclination(position, &lt;span style="color: rgb(163,21,21)"&gt;"GenitivePlural"&lt;/span&gt;));&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;else&lt;br /&gt;&lt;/span&gt;                result.Add(OneDeclination(position, &lt;span style="color: rgb(163,21,21)"&gt;"NominativePlural"&lt;/span&gt;));&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; result;&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;W celu zwiększenia czytelności funkcję Number3DigitToTextList rozbiłem na&amp;nbsp;dwie: konwertującą 0, 1&amp;nbsp;i liczby -naście oraz&amp;nbsp;drugą konwertującą pozostałe liczby.&amp;nbsp;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Teraz tylko pozostało podzielić liczbę wejściową na segmenty trzycyfrowe i obrobić powyższą funkcją. By nie zanudzać, zainteresowanych zachęcam do pobrania&amp;nbsp;&lt;a href="http://www.gdnkonsulting.waw.pl/Downloads/Misc/AmountToTextPl.zip"&gt;przykładowego projektu w VS2005 z funkcją AmountToTextPL&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-6437982899484910062?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/6437982899484910062/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=6437982899484910062' title='Komentarze (4)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/6437982899484910062'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/6437982899484910062'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2007/08/sownie-zotych-w-c.html' title='Słownie złotych w c#'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-7841941337247810147</id><published>2007-07-09T16:28:00.001+02:00</published><updated>2007-07-09T16:28:53.579+02:00</updated><title type='text'>Używanie DataSetów globalnych</title><content type='html'>&lt;p&gt;Czasami w wielu miejscach aplikacji używane są te same dane, przykładem mogą być słowniki, czy też listy klientów. Jeśli danych w tychże tabelach jest stosunkowo niewiele, a dodatkowo dane te są rzadko modyfikowane, to warto pokusić się o przechowywanie w pamięci wszystkich rekordów w takiej postaci by były one dostępne dla wszystkich komponentów aplikacji.&lt;/p&gt; &lt;p&gt;Sigletony&amp;nbsp;- tak, ale nie do końca... Czasami jednak istnieje potrzeba pozostawienia możliwości utworzenia nowej instancji - przykład: DataSet zawierający dane klientów. Dane klientów pochodzą z zewnętrznego systemu, jednak w prawie&amp;nbsp;wszystkich polach wyboru w aplikacji interesuje nas tylko pewna wybrana grupa klientów, natomiast&amp;nbsp;w kontrolce służącej do wyboru klientów z systemu zewnętrznego kryteria wyboru mogą być zupełnie inne.&lt;/p&gt; &lt;p&gt;Stąd koncepcja globalnej instancji, która jest zwracana przez DataSet. Przykład:&lt;/p&gt;&lt;pre class="code"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;partial&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;DictionariesDataSet&lt;br /&gt;&lt;/span&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;DictionariesDataSet&lt;/span&gt; globalInstance;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;DictionariesDataSet&lt;/span&gt; GlobalInstance&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;get&lt;br /&gt;&lt;/span&gt;            {&lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;if&lt;/span&gt; (globalInstance == &lt;span style="color: rgb(0,0,255)"&gt;null&lt;/span&gt;)&lt;br /&gt;                {&lt;br /&gt;                    globalInstance = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;DictionariesDataSet&lt;/span&gt;();&lt;br /&gt;                }&lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; globalInstance;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;Jak widać umieściłem ten kod w kodzie używanego datasetu, stąd też użycie takiego globalnego obiektu jest dość intuicyjne, przykład:&lt;/p&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.dictionariesDataSet = &lt;span style="color: rgb(43,145,175)"&gt;DictionariesDataSet&lt;/span&gt;.GlobalInstance;&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;Wadą rozwiązania jest to, że w osobnym miejscu trzeba załadować wszystkie globalne datasety, ale nie chciałem rezygnować z wykorzystywanego od dłuższego&amp;nbsp;czasu wydzielenia kodu DAL i typowanych DataSetów do osobnych bibliotek. I o ile&amp;nbsp;dopuszczam, że DAL może wiedzieć o konkretnych DataSetach, do których ładuje dane, to nie chciałbym wprowadzać odwrotnej zależności, czyli udostępniania komponentowi z DataSetami wiedzy (i referencji) o konkretnym DAL. Z kolei zastanawiałem się nad umieszczeniem powyższego singletonu na poziomie DAL, ale też nie mogłem się przekonać do tego pomysłu.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-7841941337247810147?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/7841941337247810147/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=7841941337247810147' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/7841941337247810147'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/7841941337247810147'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2007/07/uywanie-datasetw-globalnych.html' title='Używanie DataSetów globalnych'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-3290444107486528684</id><published>2007-07-08T20:32:00.001+02:00</published><updated>2007-07-09T18:26:27.334+02:00</updated><title type='text'>Prosta separacja logiki od prezentacji przy użyciu DataSetów</title><content type='html'>&lt;p&gt;ADO.NET wprowadziło nowe kontenery danych: DataSety i to w dwóch wersjach: zwykłe i typowane. Szczególnie te drugie przypadły mi do gustu i właśnie z nich najczęściej korzystam.&lt;/p&gt; &lt;p&gt;W pierwszych aplikacjach ładowałem dane do tych datasetów bezpośrednio w kodzie kontrolek prezentujących dane dla użytkownika. Takie rozwiązanie ma trochę zalet, np. szybsza implementacja prostych kontrolek, łatwiejsze grupowanie kontrolek w większe komponenty (bo każda kontrolka sama ładuje dane). Jednak mieszanie warstwy prezentacji z warstwą logiki biznesowej zaczęło mi coraz bardziej doskwierać, stąd zacząłem się zastanawiać nad zmianą koncepcji.&lt;/p&gt; &lt;p&gt;Zainteresowałem się&amp;nbsp;&amp;nbsp;wzorcem Model View Controller, jednak do tej pory nie jestem w stanie się do niego przekonać (ale niewykluczone, że kiedyś tak się stanie). Jeśli dobrze rozumiem MVC, to kontroler jest obiektem, który pobiera&amp;nbsp;dane z modelu i prezentuje w widoku. Ale przecież .NET Framework obsługuje&amp;nbsp;wiązanie kontrolek do źródeł danych (obiektów, w tym datasetów).&lt;/p&gt; &lt;p&gt;Na początek postanowiłem podzielić kod&amp;nbsp;tylko na odrębne klasy widoki i kontrolera oraz z datasetów przenoszących dane między nimi:&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.gdnkonsulting.waw.pl/Downloads/Misc/Prostaseparacjalogikiodprezentacjiprzyuy_BC16/MyController.jpg" atomicselection="true"&gt;&lt;img height="235" alt="MyController" src="http://www.gdnkonsulting.waw.pl/Downloads/Misc/Prostaseparacjalogikiodprezentacjiprzyuy_BC16/MyController_thumb.jpg" width="314" border="0"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;W tej koncepcji kontroler ładuje dataset i przekazuje go do widoku,&amp;nbsp;zajmuje się też zapisem danych oraz wykonywaniem innych poleceń, natomiast widok odpowiada za prezentację danych.&lt;/p&gt; &lt;p&gt;Gdy zaczynałem korzystać z tego podejścia miałem do dyspozycji VS2003. Po położeniu na formę odpowiedniego datasetu (np. o nazwie myDataSet) można było do niego przybindować poszczególne kontrolki wizualne (datagridy, textboxy, comboboxy itp). Dopóki ładowanie danych odbywało się w samej kontrolce, to nie było problemu, jednak po separacji widoku i kontrolera pojawił się problem z przekazaniem danych do widoku. &lt;/p&gt; &lt;p&gt;Jednak proste przypisanie this.myDataSet = value nie aktualizowało bindingu, a jedynie zmieniało zawartość prywatnej zmiennej myDataSet - teraz wskazywała ona inny obiekt, jednak kontrolki w dalszym ciągu były zbindowane do obiektu tej samej klasy, ale utworzonego przez kod wygenerowany przez designera. Aby rozwiązać ten problem najpierw zacząłem kopiować z designera kod bindujący i ponawiałem bindowanie po każdorazowej zmianie datasetu źródłowego. &lt;/p&gt; &lt;p&gt;Potem znalazłem inne rozwiązanie w postaci wykorzystania metody Merge. A więc kontroler przekazywał do widoku swoją instancję klasy MyDataSet, a widok kasował zawartość&amp;nbsp;swojego&amp;nbsp;datasetu źródłowego i dołączał wszystkie rekordy z datasetu przekazanego przez kontroler. Oczywiście przy zapisie danych sekwencja zdarzeń musiała być odwrotna: kontroler pobierał dataset źródłowy wiew i dołączał go wyczyszczonego swojego datasetu, a potem zapisywał dane do bazy danych.&lt;/p&gt; &lt;p&gt;Ta gimnastyka z danymi jest na pewno nieunikniona przy aplikacji gdzie warstwy są wyodrębnione i np. znajdują się na osobnych maszynach, natomiast w przypadku mniejszych aplikacji miałem wrażenie, że tylko zaciemniają zrozumienie kodu, poza tym dane się rozmnożyły - kontroler miał jedną wersję danych, a widoki inną, dodatkowo z tych&amp;nbsp;samych danych może korzystać kilka widoków np. komponent obsługujący dane klientów oraz komponent do obsługi faktur, a równocześnie istniała potrzeba by wszystkie widoki prezentowały tą samą wersję danych, a więc&amp;nbsp;po wprowadzeniu nowego klienta powinien on być od razu dostępny w komponencie faktur. Zatem, pojawiają się problemy z synchronizacją danych.&lt;/p&gt; &lt;p&gt;Na ratunek przyszedł .NET 2.0 wraz z nowym obiektem&amp;nbsp;BindingSource. Teraz kontrolki można bindować nie bezpośrednio do datasetu, lecz&amp;nbsp;do myBindingSource, a dopiero do tegoż obiekt można przypiąć dataset. Kontroler przy otwarciu widoku ustawia jego właściwość MyDataSet, a w kodzie tej właściwości ustawiana jest nowa wartość DataSource obiektu myBindingSource. I to&amp;nbsp;tyle - wszystko działa jak należy&amp;nbsp;- kodu jest znacznie mniej, nie ma konieczności przebindowywanie kontrolek, a równocześnie wszystkie widoki&amp;nbsp;obsługiwane przez tego samego kontrolera mają takie same dane.&lt;/p&gt; &lt;p&gt;A czy teraz metoda Merge pozostaje u mnie bez pracy? Nie, przydaje się przy asynchronicznym ładowaniu danych do datasetu. Kontroler ładuje dane w osobnym wątku do nowo utworzonego datasetu, a po ich załadowaniu za pomocą Invoke przechodzi na wątek GUI i tam metodą Merge dodaje załadowane dane.&lt;/p&gt; &lt;p&gt;Na koniec taka refleksja: już niedługo wchodzi Orcas, a w nim zupełnie nowe ADO.NET Entity Framework, a więc konieczność porzucenia starych koncepcji (w tym tej powyższej) i żmudne wypracowanie nowych rozwiązań&amp;nbsp;:-).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-3290444107486528684?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/3290444107486528684/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=3290444107486528684' title='Komentarze (1)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/3290444107486528684'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/3290444107486528684'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2007/07/prosta-separacja-logiki-od-prezentacji.html' title='Prosta separacja logiki od prezentacji przy użyciu DataSetów'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-270199329953633771.post-6598231274003944796</id><published>2007-07-06T04:43:00.001+02:00</published><updated>2007-07-06T11:43:37.803+02:00</updated><title type='text'>Proste szablony do formatowania napisów</title><content type='html'>&lt;p&gt;Pisanie własnego bloga zaczynam od opisu rozwiązania problemu, z którym borykał się jeden z moich znajomych. Otóż chciał on dać użytkownikom swojej aplikacji możliwość wyboru formatu numeru faktury, bo jak zapewne wiecie: jedni chcą mieć „FV-0102”, inni „FV-000102/2007”, a jeszcze inni „Faktura nr 102/2007/07”.  &lt;p&gt;Ostatnimi czasy eksperymentowałem trochę z biblioteką &lt;a href="http://www.stringtemplate.org/"&gt;String Template&lt;/a&gt;. Jest to narzędzie, które się świetnie nadaje do rozwiązania tego typu problemów, aczkolwiek po dość powierzchownym rozpoznaniu wydaje mi się, że byłyby kłopoty z określaniem różnego formatu dla różnych danych tego samego typu.  &lt;p&gt;Jednak biblioteka ta jest dość skomplikowana, więc postanowiłem zaproponować wykorzystanie wyrażeń regularnych i napisałem mu szkic w VB.NET. Dziś przerobiłem tamten kod na c# i&amp;nbsp;w tej postaci zamieszczam poniżej: &lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System.Collections.Generic;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System.Text.RegularExpressions;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;FormatString&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt;    &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; templateString;&lt;br /&gt;    &lt;span style="color: rgb(0,0,255)"&gt;private&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Regex&lt;/span&gt; regex;&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; FormatString(&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; templateString)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.templateString = templateString;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; pattern = &lt;span style="color: rgb(163,21,21)"&gt;@"\$(?&amp;lt;Source&amp;gt;\w+)"&lt;/span&gt; + &lt;br /&gt;            &lt;span style="color: rgb(163,21,21)"&gt;@"(?:;\sFormat\((?&amp;lt;Format&amp;gt;.*?)\))?\$"&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;this&lt;/span&gt;.regex = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Regex&lt;/span&gt;(pattern, &lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;RegexOptions&lt;/span&gt;.IgnoreCase | &lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;RegexOptions&lt;/span&gt;.CultureInvariant | &lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;RegexOptions&lt;/span&gt;.IgnorePatternWhitespace);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; Transform(&lt;span style="color: rgb(43,145,175)"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;, &lt;span style="color: rgb(0,0,255)"&gt;object&lt;/span&gt;&amp;gt; valueList)&lt;br /&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; regex.Replace(templateString, &lt;span style="color: rgb(0,0,255)"&gt;delegate&lt;/span&gt;(&lt;span style="color: rgb(43,145,175)"&gt;Match&lt;/span&gt; match){&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; fieldName = match.Groups[&lt;span style="color: rgb(163,21,21)"&gt;"Source"&lt;/span&gt;].Value;&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,128,0)"&gt;// jeśli nie ma parametru w źródle to zwróć pusty string&lt;br /&gt;&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;if&lt;/span&gt; (!valueList.ContainsKey(fieldName))&lt;br /&gt;            {&lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;String&lt;/span&gt;.Empty;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;object&lt;/span&gt; sourceValue = valueList[fieldName];&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; format = match.Groups[&lt;span style="color: rgb(163,21,21)"&gt;"Format"&lt;/span&gt;].Value;&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;return&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;String&lt;/span&gt;.Format(&lt;span style="color: rgb(163,21,21)"&gt;"{0:"&lt;/span&gt; + format + &lt;span style="color: rgb(163,21,21)"&gt;"}"&lt;/span&gt;, sourceValue);&lt;br /&gt;        });&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;br /&gt;&lt;p&gt;Jak widać kod jest naprawdę prosty, a najtrudniejszym elementem jest samo wyrażenie regularne, które wyszukuje we wzorcu wystąpienie wyrażenia $NazwaPola; Format(ciąg formatujący)$, przy czym ciąg formatujący jest opcjonalny. A potem każde takie wyrażenie jest zastępowane bieżącą wartością odpowiedniego parametru przekazanego w postaci słownika nazwa / wartość parametru.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Aha, pora na testy - przykładowy kod:&lt;/p&gt;&lt;pre class="code"&gt;    &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Test&lt;br /&gt;&lt;/span&gt;    {&lt;br /&gt;        &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; Main()&lt;br /&gt;        {&lt;br /&gt;            &lt;span style="color: rgb(0,128,0)"&gt;// proste formatowanie &lt;br /&gt;&lt;/span&gt;            &lt;span style="color: rgb(0,128,0)"&gt;//string templateString = "$NrFaktury$ / $Data; Format(yy)$";&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;            &lt;span style="color: rgb(0,128,0)"&gt;// formatowanie bardziej złożone&lt;br /&gt;&lt;/span&gt;            &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; templateString = &lt;span style="color: rgb(163,21,21)"&gt;"FV $NrFaktury; Format(00000)$ / "&lt;/span&gt; +&lt;br /&gt;                &lt;span style="color: rgb(163,21,21)"&gt;@"$Data; Format(yyyy\/MM)$ $nic$"&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;FormatString&lt;/span&gt; format = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;FormatString&lt;/span&gt;(templateString);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(43,145,175)"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;, &lt;span style="color: rgb(0,0,255)"&gt;object&lt;/span&gt;&amp;gt; currentValues = &lt;br /&gt;                &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Dictionary&lt;/span&gt;&amp;lt;&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;, &lt;span style="color: rgb(0,0,255)"&gt;object&lt;/span&gt;&amp;gt;();&lt;br /&gt;            currentValues.Add(&lt;span style="color: rgb(163,21,21)"&gt;"NrFaktury"&lt;/span&gt;, 102);&lt;br /&gt;            currentValues.Add(&lt;span style="color: rgb(163,21,21)"&gt;"Data"&lt;/span&gt;, &lt;span style="color: rgb(43,145,175)"&gt;DateTime&lt;/span&gt;.Now);&lt;br /&gt;&lt;br /&gt;            &lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt; result = format.Transform(currentValues);&lt;br /&gt;            System.&lt;span style="color: rgb(43,145,175)"&gt;Console&lt;/span&gt;.WriteLine(result);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Przy pisaniu wyrażenia regularnego korzystałem&amp;nbsp;z pomocy &lt;a href="http://www.ultrapico.com/Expresso.htm"&gt;Expresso&lt;/a&gt;, które gorącą polecam wszystkim miłośnikom regexów :).&amp;nbsp;&amp;nbsp;A &lt;a href="http://www.gdnkonsulting.waw.pl/Downloads/Misc/FormatString.zip"&gt;tu&lt;/a&gt; wrzuciłem demo&amp;nbsp;w VB.NET, tutaj kod był nieco dłuższy, bo VB.NET nie ma metod anonimowych.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/270199329953633771-6598231274003944796?l=gdanowski.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://gdanowski.blogspot.com/feeds/6598231274003944796/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=270199329953633771&amp;postID=6598231274003944796' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/6598231274003944796'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/270199329953633771/posts/default/6598231274003944796'/><link rel='alternate' type='text/html' href='http://gdanowski.blogspot.com/2007/07/proste-szablon-do-formatowania-stringw.html' title='Proste szablony do formatowania napisów'/><author><name>Grzegorz Danowski</name><uri>http://www.blogger.com/profile/13154835174231085036</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='25' height='32' src='http://www.gdnkonsulting.waw.pl/Downloads/Misc/GDPhoto.jpg'/></author><thr:total>0</thr:total></entry></feed>
