View Łukasz Herman's LinkedIn profileView my profile

statystyki www stat.pl

niedziela, 28 listopada 2010

Myślałem że skorzystam z IsolatedStorage w ASP.NET, ale wychodzi na to że nie działa to gdy korzystamy z WebService. No trudno...

Mając już wszystkie providery OleDB czas na sterowniki ODBC ;) Całą noc nad tym problemem siedziałem to wolę to tutaj utrwalić.

Kod wygląda bardzo niepozornie:

public string getODBC()
{
string ret = "";
string registryPath = @"SOFTWARE\ODBC\ODBCINST.INI";
RegistryKey hklm = Registry.LocalMachine;
hklm = hklm.OpenSubKey(registryPath);
string[] values = hklm.GetSubKeyNames();
foreach (var value in values)
{
ret += value;
}
return ret;
}

Ale w praktyce wyciąga z rejestru dużo więcej niż ja mogłem zobaczyć odpalając regedita.

W moim przypadku kod ten zwraca listę 27 elementów. Ja w rejestrze widzę nie więcej jak 5, więc różnica jest.

Dalej metodą prób, błędów i googla testowałem różne konfiguracje na hostingu. Aż do skutku. Oj bolało, możliwości jest masa!

W tej chwili mój connection string to wyciągania danych z bazy DBF (VisualFoxPro) bez posiadania sterownika VFP jest już kompletny, chociaż w pewnym stopniu hardkodowany. Później go poprawię jak należy, ważne że działa :)

A wygląda on tak:

Driver={Driver do Microsoft dBase (*.dbf)};DriverID=277;Dbq=" + @"dluga_sciezka_do_katalogu\DataManager\";

Ważne jest to, że parametr 'DBQ' wskazuje na KATALOG a nie na plik!

Tagi: DBF ODBC
07:57, hermanluk , Bazy danych
Link Dodaj komentarz »

Najprościej z rejestru. Potrzebowałem poznać jakie dostępne są na serwerze na któym mam wykupiony hosting. Znalazłem kod VB który to robi, przekonwertowałem go na C# (jakimś online'owym konwerterem) i trochę zmodyfikowałem, żeby zwracał mi w miarę czytelnego stringa.

Konwerter.

Oryginalny kod.

using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using Microsoft.Win32;


namespace DataManager
{
class Module1
{

public String CountASAS()
{
int i = 0;
DictionaryEntry[] de = SearchOleDbProviders();
string wynik = "";

// Displays the ProgID and name of each provider
for (i = 0; i <= de.Length - 1; i++)
{
wynik += de[i].Key + " - ";
wynik += de[i].Value + " ; ";
}

//Waits for user input
return wynik;
}

public DictionaryEntry[] SearchOleDbProviders()
{
DictionaryEntry[] Providers = null;
Int16 nProviders = 0;

nProviders = -1;
Providers = new DictionaryEntry[1];

// I am only interested in the CLSID subtree
RegistryKey keyCLSID = Registry.ClassesRoot.OpenSubKey("CLSID", false);
string[] keys = keyCLSID.GetSubKeyNames();
DictionaryEntry de = default(DictionaryEntry);
Int32 i = 0;

// Search through the tree just one level
for (i = 0; i <= keys.Length - 1; i++)
{
RegistryKey key = keyCLSID.OpenSubKey(keys[i], false);

// Search for OLE DB Providers
de = SearchKeys(key);
if ((de.Key != null))
{
// Found one, add it to the Dictionary
nProviders += 1;
Array.Resize(ref Providers, nProviders + 1);
Providers[nProviders] = de;
}
}
return Providers;
}

public DictionaryEntry SearchKeys(RegistryKey key)
{
Int32 i = default(Int32);
DictionaryEntry de = default(DictionaryEntry);

//Tries to find the "OLE DB Provider" key
RegistryKey key2 = key.OpenSubKey("OLE DB Provider");

if ((key2 != null))
{
// Found it, fills the DictionaryEntry
de = new DictionaryEntry();
string[] sValues = key2.GetValueNames();
de.Key = key.OpenSubKey("ProgID", false).GetValue(sValues[0]);
string[] sValues2 = key2.GetValueNames();
de.Value = key2.GetValue(sValues2[0]);
}
return de;
}
}
}

 

czwartek, 25 listopada 2010

Potrzebowałem wyciągnąć z SQLa wszystkie znaki do jakich mam dostęp przez funkcję CHAR.

Wyszło coś takiego:

declare @counter int
set @counter = 0
while @counter <255
begin
set @counter = @counter + 1
print cast(@counter as char) + ' ' + cast(CHAR(@counter) as char)
end

Tagi: Sql
01:51, hermanluk , Bazy danych
Link Dodaj komentarz »
wtorek, 23 listopada 2010

Taki komunikat otrzymuję tworząc paczkę SSIS. Rozwiązanie robocze to zmiana we właściwościach OLEDB Source:

1. AlwaysUseDefaultCodePage=true

2. Zmiany DefaultCodePage na 1252

Po tym wystąpił kolejny błąd (jakby mnie to zdziwiło...)

SQL Server Destination] Error: Unable to prepare the SSIS bulk insert for data insertion.
[SSIS.Pipeline] Error: component "SQL Server Destination" failed the pre-execute phase and returned error code 0xC0202071.

Tutaj jest już jasne rozwiązanie z MS Knowledge Base: You may get "Unable to prepare the SSIS bulk insert for data insertion" error on UAC enabled systems

Ok. Nie jest źle. SSIS działa. Importuje nawet dane. Tylko muszę powalczyć z kodowaniem znaków bo mam krzaki w bazie.

Jak robiłem Selecta bezpośrednio z Visual FoxPro, to musiałem jeszcze przekonwertować dane za pomocą:

SELECT CPCONVERT(620, 1250,kolumna) FROM KOLUMNA.DBF

Dziwne jest to, że ze źródła wychodzą dobre kodowanie, a podczas zapisywania do bazy docelowej kodowanie się rozjeżdża.

Po wielu kombinacjach (w zasadzie to chyba wszystkie przetestowałem), doszedłem do tego że kodowanie wszędzie ma być ustawione na 1250. Z pliku DBF dane wyciągam korzystając z kodu SQL i robię od razu konwersję za pomocą CPConvert (patrz wyżej).

Następnie dane wędrują do bazy MSSQL, ale nie jako standardowe połączenie z MSSQLem, ale korzystając z OleDB. Tylko w ten sposób kodowanie pozostaje prawidłowe.

Bardzo fajny opis jak dobrać się do danych zapisanych w formacie XML z poziomu SQLa:

Generating Relational data from xml datatype Part 1

Generating Relational data from xml datatype Part 2

Tagi: Sql xml
01:28, hermanluk , Bazy danych
Link Dodaj komentarz »
niedziela, 21 listopada 2010

Tworzę paczkę SSIS (SQL Server Integration Services) żeby wgrać pliki z bazy Visual FoxPro (.DBF) do MSSQL. Niestety nic nie działa tak jak powinno 'by default'.

Pierwsze co, to w wizardzie nie ma możliwości wyboru odpowiedniego Providera do odczytu plików DBF.

Stworzyłem więc paczkę od zera.

1. Utworzenie nowego Data Flow

2. Dodanie OLEDB Source i stworzenie połączenia do pliku DBF

2. Dodanie SQL Server Destination i wskazanie bazy docelowej (MSSQL)

3. Połączyłem je razem, ustawiłem mapowanie kolumn.

Pierwszy błąd jak w tytule. Kliknąłem więc prawym klawiszem myszki na OLEDB Source i wybrałem Show Advanced Editor -> Input and Output Properties -> Source Output -> Output Columns, wybrałem kolumnę która zgłasza błąd i zmieniłem jej DataType na four-byte signed integer (DT_I4).

Przeszło :)

Teraz zgłasza mi błąd "cannot be processed because more than one code page (1250 and 1252) are specified for it."...

Tagi: DBF Sql SSIS
14:27, hermanluk , Bazy danych
Link Dodaj komentarz »

Zapamiętać: Server project zawsze modyfikuje bazę Master.

Już myślałem, że z moim Visual Studio jest coś nie tak, bo usilnie zmieniał mi bazę na Master, zamiast korzystać z tej ustawionej przeze mnie. Po prostu za szybko przeczytałem opis czym te dwa projekty się różnią.

czwartek, 18 listopada 2010

ASP.NET oferuje nam specjalną izolatkę (przestrzeń dyskową) dla naszych aplikacji.

Potrzebując wrzucić na serwer jakiś plik, nie musimy przejmować się poziomem uprawnień użytkownika na którym działa proces IISa, tylko zapisujemy plik korzystając z tej funkcjonalności.

Taki krótki kodzik, który zapisuje przekazany plik, zwracając ścieżkę do katalogu gdzie został zapisany:

public string SaveFile(byte[] binaryFile, string path)
{
 IsolatedStorageFile isf = IsolatedStorageFile.GetStore(
IsolatedStorageScope.User | IsolatedStorageScope.Domain |
IsolatedStorageScope.Assembly, null, null);
 try
 {
 IsolatedStorageFileStream file = new IsolatedStorageFileStream(path, 
FileMode.Create, isf);
 file.Write(binaryFile, 0, binaryFile.Length);
 file.Close();
 }
 catch (Exception ex)
 {
 }

 string path2 = isf.GetType().GetField("m_RootDir", 
BindingFlags.Instance | BindingFlags.NonPublic).GetValue(isf).ToString();
 return path2;

Wrrrr, kto wymyślił wyświetlanie wyjątków w języku polskim? Przecież to się do niczego nie nadaje...

Jak się tego pozbyć? Chyba się nie da. Zmieniać ustawień regionalnych nie mam zamiarua

A zmiana ustawień z poziomu kodu jak to zostało przedstawione w wątku C# - Exception messages in English?, przy każdym wywołaniu przeraża mnie. No trudno, trzeba nauczyć się czytać polskie błędy.

PS. Błąd z tytułu oznacza, że korzystam ze starej wersji sterownika, która nie jest już kompatybilna z tym co chcę zrobić.

środa, 17 listopada 2010

Fajny i bardzo prosty sposób jak zbudować kontrolkę Menu, wraz z implementacją dodatkowych atrybutów które deklarujemy sobie w pliku web.sitemap

W tym wypadku jest to atrybut odpowiedzialny za wyświetlanie, bądź nie, pozycji z pliku mapy strony. Ja dopisałem sobie atrybut odpowiedzialny za klasę CSS. Działa wyśmienicie :)

I bet you didn't know that adding custom attributes to your web.sitemap was this easy!

poniedziałek, 15 listopada 2010

W pisanej aplikacji potrzebuję prostego sprawdzenia czy użytkownik wpisał dobrą zawartość pola. Dokładniej to robię upload plików, chcąc mieć pewność, że podano prawidłową nazwę pliku (lub dobry typ pliku).

Pomyślałem żeby zrobić to poprzez Visual Studio i kontrolkę Validate i sprawdzać wyrażenie regularne. Zalet jest sporo, nie martwimy się o to, jak działa to na różnych przeglądarkach, oraz mamy sprawdzanie kodu po stronie klienta (jeżeli sobie tego zażyczymy) oraz sprawdzanie po stronie serwera (czyli to co najważniejsze).

Minusy?

  • Sprawdzanie po stronie klienta wymaga umieszczania na stronie kodu JavaScript. Tylko kontrolka dodaje cały swój kod, a nie tylko ten, który będziemy wykorzystywać. Po co mi sprawdzanie czy wprowadza się dobrą liczbę, skoro ja sprawdzam tylko wyrażenie regularne?
  • Nawet jak wyłączymy sprawdzanie po stronie klienta, dodawanych jest mnóstwo JavaScriptów, które raczej nie są wykorzystywane.

Napisałem własnego JavaScripta sprawdzającego wyrażenie regularne. Cały kod związany z validacją po stronie klienta to ok. 20 linijek. Nie widzę żadnego sensu wrzucania masy niepotrzebnego kodu na stronę.

Swoją drogą, sprawdzenie tego samego wyrażenia regularnego po stronie serwera to ok. 5 linijek kodu, czyli mniej niż potrzeba na dodanie kontrolki Validator...

Bardzo obszerny post na jednym z blogów. Masa linków powiązanych z tematem uwierzytelniania w aplikacjach ASP.NET, wszystko co związane z tematami: Membership, Roles, Forms Authentication i Security Resources.

ASP.NET 2.0 Membership, Roles, Forms Authentication, and Security Resources

niedziela, 14 listopada 2010

Bardzo ciekawy wpis na jednym z SQLowych blogów. Dlaczego nie powinniśmy korzystać z typu money?

Otóż pociąga za sobą ogromne błędy obliczeniowe. Co prawda nie zawsze spotka to nas, ale lepiej dmuchać na zimne i korzystać ze sprawdzonego typu decimal ;)

Throw Your MONEY Away

Znalazłem fajny opis w jaki sposób dodać zewnętrzny kod JavaScriptowy do mojej kontrolki.

Jest kilka sposobów w jaki możemy odwoływać się do skryptów JS. Ja potrzebowałem aby mój zewnętrzny plik był ładowany zawsze tam, gdzie używam mojej kontrolki.

Rozwiązanie: Creating Custom ASP.NET Server Controls with Embedded JavaScript

Jedyna różnica jest taka, że musiałem ręcznie dodać "[assembly..."  na początku mojej kontrolki, żeby kod JS był widoczny. A dokładniej:

[assembly: WebResource("WI_Controls.Scripts.FileExtensionController.js", "java-script")]

zaraz przed namespace.

 

piątek, 05 listopada 2010

Jakiś czas temu w pracy natknąłem się na dziwną przypadłość w VB Scriptcie. Tworząc procedury lub funkcje, w żaden sposób interpreter nas nie pilnuje czy nie zadeklarowaliśmy tej samej procedury dwa (lub więcej) razy. Pamięta ostatnio zadeklarowaną wersję i z niej możemy korzystać.

Chyba, że skorzystamy z funkcji Execute. Przydaje sięona, gdy chcemy korzystać z funkcji zawartych w zewnętrznych plikach (repozytorium procedur lub coś w tym stylu). Wtedy za każdym razem VB Script wykonuje ostatnio wykonaną funkcję zadeklarowaną przez Execute.

Dim X ' Declare X in global scope.
X = "Global" ' Assign global X a value.

Sub Proc1
Dim X ' Declare X in local scope.
X = "Local" ' Assign local X a value.
' The Execute statement here creates a
' procedure that, when invoked, prints X.
' It print the global X because Proc2
' inherits everything in global scope.
Execute "Sub Proc2: WScript.Echo X: End Sub"
WScript.Echo Eval("X") ' Print local X.
Proc2 ' Invoke Proc2 in Proc1's scope.
End Sub

'Proc2 ' This line causes an error since
' Proc2 is unavailable outside Proc1.
Proc1 ' Invoke Proc1.
Execute "Sub Proc2: WScript.Echo X: End Sub"
Proc2 ' This invocation succeeds because Proc2
' is now available globally.

Warto sobie przeanalizować ten przykład (i trochę pomodyfikować).

Stanąłem przed zastanawiającą kwestią. Potrzebuje uploudować dwa pliki z dysku lokalnego na serwer poprzez WebServices. Wariantów jest sporo. Można skorzystać z kontrolekFileUpload, dodać ręcznie pola wyboru i przyciski, skorzystać z gotowych kontrolek (NeatUpload), albo napisać coś własnego.

Wybrałem (na razie) rozwiązanie bezpośrednie. Napiszę własną kontrolkę (warto się tego nauczyć), która będzie zbudowana z dwóch kontrolek FileUpload.

Teoretycznie najprostszym sposobem byłoby rozszerzenie kontrolki FileUpload o potrzebną mi funkcjonalność, ale potrzebuję co najmniej dwóch takich kontrolek. Wpadłem więc na pomysł, żeby stworzyć własną kontrolkę, która będzie zawierała w sobie inne kontrolki.

Po zrobieniu małego researchu udało mi się dojść do tego, że muszę stworzyć kontrolkę rozszerzając klasę CompositeControl zamiast WebControl (jak to się robi pisząc najprostszą kontrolkę) i to działa bardzo fajnie :)

Wystarczy nadpisać procedurę CreateChildControls i możemy dodawać do własnej kontrolki cokolwiek zapragniemy.

Więcej: http://www.joe-stevens.com/2010/04/16/creating-a-composite-server-control-with-asp-net/

poniedziałek, 01 listopada 2010

Które lepsze? Początkowo planowałem wykorzystać XML WebServices, bo już wcześniej miałem z nimi kontakt. Największą zaletą jest udostępnienie wszystkich danych w czytelnym formacie XML, który interpretować możemy w zasadzie dowolną technologią.

XML pomaga nam również utworzyć kilka różnych wersji aplikacji działających w różnych środowiskach, a opierających się na tych samych danych.

Niestety wadą jest szybkość. Generowanie kodu XML dla każdej, nawet najmniejszej odpowiedzi pociąga za sobą tworzenie wszystkich znaczników, które później trzeba przesłać przez sieć. Wyda się że nie jest tego dużo, ale może się uzbierać. Ponadto gdy pisałem projekt w Adobe FLEX z wykorzystaniem XML WebServices, działało to przerażająco wolno (chociaż łatwo było to oprogramować).

ASP.NET Web Services, albo raczej AJAX WebServices, bo w tym kierunku będę dążył, umożliwiają przesyłanie samych obiektów (więc narzut na przepustowość łącza dużo mniejszy) i od razu mogę działać na danych wyjściowych.

Ponadto przesyłanie plików jest prostsze. W XML WebServices aby przesłać plik, najpierw musiałem przekonwertować go do postaci base64. A później w drugą stronę żeby z tego pliku korzystać. Niestety funkcje dostępne w System.Convert mają ograniczenia, tak że konwersja z base64 dla dużych plików generowała błąd związany z brakiem pamięci. We FLEXie miałem ręcznie napisany algorytm i w ten sposób to działało, teraz nie chce mi się tego pisać na nowo.

Stanęło więc na ASP.NET (AJAX) WebServices.