Pokazywanie postów oznaczonych etykietą stackoverlow. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą stackoverlow. Pokaż wszystkie posty

niedziela, 4 października 2015

Plunker - web edytor on-line

Pluner, czyli http://plnkr.co/ to darmowy, kompletny edytor html/css/javascript on-line, w którym możemy stworzyć strony www, w oparciu o wyżej wymienione technologie (i ich pochodne), możemy zapisać nasze prace, połączyć się z https://github.com/ oraz wykorzystać nasze konto.

Całkiem fajne i sensowne narzędzie, do tworzenia prototypów, dzielenia się swoją pracą w zdalnym zespole, lub np. z ludźmi ze stackoverflow.com (w przypadku napotkania jakiś problemów).


Edit: UWAGA!!! Na chwilę obecną nie ma bezpośredniej synchronizacji pomiędzy plunkerem, a githubem :(  http://stackoverflow.com/questions/19615717/can-plunker-save-to-github

niedziela, 21 grudnia 2014

Wielowątkowy dostęp do zasobów "po polsku" :D

Jak każdy wie, samo programowanie jest wystarczająco trudne, a programowanie wielowątkowe jest trudne do kwadratu. O tym, że jest u nas z tym "krucho" można się było przekonać podczas tegorocznego DevDay 2014 w Krk, podczas którego Dan North wytknął to polskim programistom (głównie tym uczestniczącym na jego warsztatach).

Może ze świadomością wielowątkowości nie jest jakoś super, ale jednak pewna świadomość jest, co pokazała choćby sesja nt. "Modelu aktorów" na Warszawskiej grupie .NET.

A gdzie ja się z tym ost. spotkałem? Ano w aplikacji webowej, którą stworzyłem, a która do generowania raportów wykorzystuje SQL Server Reporting Services w trybie "local reports", tzn. do generowania raportów wykorzystywane są pliki ".rdlc" umieszczone na tym samym serwerze co serwer IIS hostujący stronę web (w moim konkretnym przypadku framework nancyfx).

I o ile przez większość czasu, te raporty generowały się poprawnie, o tyle przed wystawieniem aplikacji dla naszych doradców (pierwsza faza testów objęła ok 50-60 docelowych użytkowników systemu) razem z Adamem K. (osoba z "biznesu") postanowiliśmy przetestować to na x raportów wygenerowanych równocześnie. Zrobiliśmy tak i... klops. Pojawiły się błędy, jedne, te związane z użyciem słówka "static" udało się szybko wyeliminować, to pozostał jeszcze "concurrent access to file".

Stackoverflow.com  zaproponowało synchroniczny dostęp do plików za pomocą "lock". O ile na "początek" to miało sens, tj. wrzucam 'lock'a' "na szybko" i oddaję pierwszą wersję aplikacji użytkownikom do testów, o tyle w docelowej wersji (i kilka razy większej ilości użytkowników) taka wersja systemu była by bardzo niewygodna.

Z pomocą i pomysłem przyszedł mi Michał Kwiatos, który zaproponował rozwiązanie pt. "dla każdego requesta wygeneruj GUID, utwórz katalog z nazwą GUIDa, przekopiuj tam swoje pliki, wykorzystaj je do generowania raportu, a na koniec skasuj niepotrzebny już, śmieciowy katalog", co też zrobiłem i co... rozwiązało mój problem.

I tak sobie teraz myślę, że... najprostsze rozwiązania zwykle bywają najskuteczniejsze.

p.s. mój system docelowo będzie miał mniej niż 500 userów, czyli takie rozwiązanie wydaje się być optymalne. Gdybym tworzył system na +2k userów, to myślę, że na poważnie zastanowił bym się nad implementacją "modelu aktorów" w tym systemie.
p.s.2 wszelkie uwagi mile widziane

środa, 14 maja 2014

Korzystanie z kilku plików konfiguracyjnych (*.config)

Jak powszechnie wiadomo, w jednej solucji możemy mieć wiele projektów. Każdy z tych projektów może posiadać własny plik konfiguracyjny. Problem może się pojawić w sytuacji, gdy w jednej solucji chcemy korzystać z wielu plików konfiguracyjnych. Przyjrzyjmy się pewnemu realnemu scenariuszowi.

Mamy nasza przykładową aplikację konsolową, która poprawnie działa. Zaszła jednak potrzeba wykorzystania web serwisu (WCF). Zgodnie z dobrymi praktykami, do obsługi WCF tworzymy nowy, osobny projekt jako bibliotekę DLL ('class library'). Aplikacja konsolowa, posiadała już swój plik konfiguracyjny (App.config), więc próba odwołania się konfiguracji naszej biblioteki za pomocą obiektu "ConfigurationManager" spowodowała przeglądanie pliku konfiguracyjnego aplikacji. Ponieważ chcemy oddzielić konfigurację biblioteki od konfiguracji aplikacji potrzebujemy mieć oba pliki rozdzielone oraz możliwość czytania z obu plików.

Jak to należy zrobić?
Rozwiązanie można podzielić na dwa etapy. Konfiguracyjny oraz programistyczny:

W projekcie:
a) zmieniamy nazwę pliku konfiguracyjnego biblioteki, aby oba pliki konfiguracyjne miały różne nazwy
b) we właściwościach pliku konfiguracyjnego biblioteki ustawiamy właściwość "Copy to output Directory" na "Copy always"
c) w projekcie z aplikacją ustawiamy referencję na projekt z biblioteką

W kodzie biblioteki, będziemy się odwoływać do nowego pliku konfiguracyjnego w "niestandardowy" sposób. Pisząc "niestandardowy" mam na myśli w sposób inny niż za pomocą obiektu "ConfigurationManager".

W kodzie, wykorzystujemy obiekt Configuration (System.ServiceModel.Configuration) poprzez wykorzystanie jego właściwości. Przykładowa klasa, za pomocą której będziemy korzystali z nowego pliku konfiguracyjnego wygląda tak:

    public class ConfigurationManagerWrapper
    {
        private Configuration cfg;
        public ConfigurationManagerWrapper()
        {
            var path = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
            var fileMap = new ExeConfigurationFileMap
            {
                ExeConfigFilename = string.Concat(path, "\\mail.config")
            };
            cfg = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
        }
        public string GetSetting(string key)
        {
            AppSettingsSection section = (cfg.GetSection("appSettings") as AppSettingsSection);
            var value = section.Settings[key].Value;
            return value;
        }
        public ChannelEndpointElement GetClient(string key)
        {
            var serviceModel = ServiceModelSectionGroup.GetSectionGroup(cfg);
            var endpoints = serviceModel.Client.Endpoints;
            if (endpoints.Count > 0)
                return endpoints[0];
            return null;
        }
        public T GetSection<T>(string sectionName)
        {
            return (T)ConfigurationManager.GetSection(sectionName);
        }
    }
Bardziej zaawansowany przykład wykorzystania takiego pliku .config można zobaczyć w przeznaczonym do tego projekcie na github mojego autorstwa stworzonym w celu demonstracji kodu.

Pomocne linki:
stackoverflow.com - how-do-i-retrieve-appsettings-from-the-assembly-config-file
weblogs.asp.net - cibrax - getting-wcf-bindings-and-behaviors-from-any-config-source.aspx

wtorek, 28 stycznia 2014

Rysowanie diagramów UML z ArgoUML

Czym jest UML wie zapewne każdy. Czy, a raczej kiedy należy go stosować zależy w dużej mierze od projektu oraz zdrowego rozsądku. Są sytuacje, gdy stworzenie kilku diagramów UML może stanowić ważny element dokumentacji projektowej, a są też sytuacje, gdy tworzenie zbyt dużej ilości może się okazać zwykłą stratą czasu.

W momencie, w którym zdecydujemy, że warto wzbogacić naszą dokumentację techniczną o diagram(y) UML warto użyć do tego odpowiedniego narzędzia, szczególnie, że niektóre z nich posiadają takie opcje, jak np. autogenerowanie części kodu źródłowego.

Przy wyborze narzędzia, ważne jest to, jakiej funkcjonalności oczekujemy od narzędzia, tj. czy chcemy rysować, modelować, eksportować wyniki do metamodeli, a może jednak najważniejsze jest generowanie fragmentów kodu z diagramu? Bardzo dobry przegląd narzędzi, ze względu na rodzaje funkcjonalności znajduje się w jednym z pytań na forum stackoverflow.com.

W moim przypadku, właśnie skończyłem pracę nad rozbudowanym architektonicznie projektem, w którym mieliśmy 4 nasze serwery, z czego 3 schowane za "firewallem" oraz 6-7 usług zewnętrznych, z którymi porozumiewały się różne serwery. Diagram wdrożeniowy pokazał istniejące zależności w sposób jasny i klarowny, pozwalając zrozumieć istniejące zależności nawet nowym osobom w projekcie.

Do narysowania diagramu użyłem darmowego programu ArgoUML dostępnego na licencji Eclipse Public License - v 1.0.

ArgoUML w porównaniu ze swoim głównym konkurentem Visio:
- jest darmowy
- jest prosty, a szata graficzna lekko spartańska
- można za jego pomocą wygenerować tylko 6 typów diagramów (klas, przypadków użycia, sekwencji, maszyny stanowej, czynności, wdrożeniowy)
- nie do końca spełnia (implementuje) wszystkie standardy języka UML
- posiada możliwość generowania kodu w językach takich jak java, c++, c#, itp.
- posiada możliwość inżynierii wstecznej plików JAR

dodatkowo, wynik naszej pracy możemy eksportować do:
- XML
- pliku graficznego

Pełny opis na http://en.wikipedia.org/wiki/ArgoUML

Mnie osobiście ArgoUML przekonało funkcjonalnością, oraz darmowością. Program, mimo iż nie jest w 100% zgodny ze standardami UML, oraz posiada nieco spartańską oprawę graficzną, to jednak, Moim zdaniem jest wystarczająco dobry do zobrazowania "clou problemu" i zwyczajnie 'robi swoją robotę'.

niedziela, 27 października 2013

NLog, czyli logowanie błędów w .NET

Jakiś czas temu, opisywałem na moim blogu bardzo popularny logger dla .NET, o nazwie log4net. Sam log4net jest bardzo ok, i w zasadzie całkiem nieźle się sprawdza w swojej roli, jednak jak to mówią "lepsze jest wrogiem dobrego", a tym lepszym, a przynajmniej wygodniejszym narzędziem jest nlog.

Główne różnice, wady/zalety?
- NLog jest wygodniejszy. Co musimy zrobić? Ddokładamy bibliotekę do projektu,  za pomocą Packer Menager Console pobieramy plik konfiguracyjny, odkomentowywujemy ustawienia i... działa. W przypadku log4net trzeba pamiętać o sekcji inicjalizacji w 'global.asax' oraz odpowiednim ustawianiu pliku konfiguracyjnego
- zaawansowana konfiguracja -> tutaj akurat nie miałem okazji ostro sprawdzać obu narzędzi, jednak czytając różne blogi, zazwyczaj ludzie chwalą sobie intuicyjność oraz bardziej elastyczną konfigurację NLoga
- aktualizacje -> ost. aktualizacja nlog wyszła 09.10.2013, i jest w miarę często aktualizowany (projekt żyje), natomiast odnośnie log4net spotkałem się z opiniami, że ost. aktualizacja była ponad 5 lat temu
- autor nLog'a jkowalski.com na jednym ze spotkań Warszawskiej grupy .NET wykazywał niewielkią, ale jednak wyższą wydajność swojego rozwiązania nad rozwiązaniem log4net (i zdecydowanie wyższą nad standardowym System.Diagnostics).

Osobiście uważam, że nlog jest zwyczajnie wygodniejszy, oraz "nowszy", przez co niewiele, ale jednak lepszy niż log4net.

Całkiem fajnie opisał ten temat Maciej 'procent' Aniserowicz na swoim blogu.
Dobry post z porównaniem jest też na stackoverflow.com - log4net vs nlog
Opis NLog na codeproject.com


p.s. przykładowa konfiguracja, którą stoduje w klasie Bootstrapper projektu Nancy:
        protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
        {
            base.ApplicationStartup(container, pipelines);
            #region event logger (nLog)
            FileTarget fileTarget = CreateNlogFileConfiguration();
            SimpleConfigurator.ConfigureForTargetLogging(fileTarget, LogLevel.Info);
            LogAllRequests(pipelines);
            LogAllResponseCodes(pipelines);
            LogUnhandledExceptions(pipelines);
            #endregion
        }
        private static FileTarget CreateNlogFileConfiguration()
        {
            //SimpleConfigurator.ConfigureForTargetLogging(new AsyncTargetWrapper(new EventLogTarget()));
            FileTarget fileTarget = new FileTarget();
            fileTarget.Name = "CeidgCheckLogger";
            fileTarget.CreateDirs = true;
            fileTarget.Layout = "${date} ${level} ${callsite:className=true:includeSourcePath=false:methodName=true} ${message}";
            //fileTarget.FileName = @"${basedir}/logs/CeidgCheckNancy.${date:format=yyyy.MM.dd}.log"; //for catalog in solition
            fileTarget.FileName = @"D:/logs/CeidgCheckNancy/CeidgCheckNancy.${date:format=yyyy.MM.dd}.log";
            fileTarget.KeepFileOpen = false;
            fileTarget.Encoding = Encoding.UTF8;
            return fileTarget;
        }
        private void LogAllRequests(IPipelines pipelines)
        {
            pipelines.BeforeRequest += ctx =>
            {
                log.Info("Handling request {0} \"{1}\"",
                ctx.Request.Method, ctx.Request.Path);
                return null;
            };
        }
        private void LogAllResponseCodes(IPipelines pipelines)
        {
            pipelines.AfterRequest += ctx =>
            log.Info("Responding {0} to {1} \"{2}\"",
            ctx.Response.StatusCode, ctx.Request.Method,
            ctx.Request.Path);
        }
        private void LogUnhandledExceptions(IPipelines pipelines)
        {
            pipelines.OnError.AddItemToStartOfPipeline
            ((ctx, err) =>
            {
            log.ErrorException(string.Format("Request {0}\"{1}\" failed", ctx.Request.Method, ctx.Request.Path), err);
            return null;
            });
        }

czwartek, 24 października 2013

.NET Client consuming Java IBM WebSphere Web Service

W dwóch moich poprzednich postach opisywałem sposób podłączenia się aplikacji .NET do web serwisów napisanych w PHP z pomocą Zend Framework (link1 link 2 ) i związanymi z tym problemami. W tym wpisie skupie się na podłączeniu się do web service utworzonego za pomocą IBM WebSphere wykorzystującym standardy technologii web services napisanych dla środowiska J2EE (tzw. "Java korporacyjna").

Do poprawnego 'skonsumowania' serwisu używamy standardowego mechanizmu WCF poprzez "Add Service Reference". Po wpisaniu adresu serwisu (z końcówką ?wsdl) powinniśmy wykryć serwis, a nast. utworzyć na jego podstawie klasę proxy. Gdyby automat nie wykrył usługi, to należy wpisać adres usługi do przeglądarki, a nast. zapisać plik na dysk z rozszerzeniem .xml (i w Add Service Reference podać ścieżkę na dysku gdzie zapisaliśmy ten plik). W przypadku mojej usługi, konieczne było jeszcze zgranie pliku .xsd (na szczęście znajdował się pod tym samym adresem co serwis, tylko z innym rozszerzeniem).

W momencie, gdy mamy utworzoną klasę proxy, możemy spróbować ją wywołać. Niestety w moim przypadku, standardowe wywołanie klasy proxy oznaczało błąd:
„Atrybut System.Xml.Serialization.XmlAttributeAttribute obiektu XmlSerializer nie jest prawidłowy w parametrXYZ. Kiedy atrybut IsWrapped ma wartość true, obsługiwane są tylko atrybuty XmlElement, XmlArray, XmlArrayItem i XmlAnyElement.”
Poprosiłem więc o pomoc bardziej doświadczone osoby i one zasugerowały mi aby,  w miejscu, w którym chcemy wywołać usługę dodać przestrzeń nazw:
using System.ServiceModel
 a w kodzie aplikacji EndPoint:
EndpointAddress ea = new EndpointAddress("https://abcd/hash/ws/xyz");
WS ws = new WSClient(new BasicHttpBinding(BasicHttpSecurityMode.Transport), ea);
Posiadając tak wygenerowaną klasę proxy, zasugerowano mi wejść w obiekt "WS" ("go to definition") i tam sprawdzić klasy, które wykorzystują 'parametrXYZ'.
Okazało się, że faktycznie, były tam dwie klasy, które wykorzystywały taki parametr. Rozwiązaniem tego problemu okazało się przemodelowanie tych klas, poprzez implementację System.ComponentModel.INotifyPropertyChanged czyli dodanie i obsługę zdarzenia  
 public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
oraz dodanie zmiennych prywatnych każdemu odpowiadającemu mu publicznemu propertisowi, a także usunięcie atrybutu [System.ServiceModel.MessageBodyMemberAttribute] dla tych propetrtisów.
Przykład poprawnie poprawionej klasy:

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    [System.ServiceModel.MessageContractAttribute(WrapperName="parametr-XYZ", WrapperNamespace="http://xyz.pl/ab/xyz", IsWrapped=true)]
    public partial class xyzRequest : object, System.ComponentModel.INotifyPropertyChanged {

        private string parametrXYZField;
        [System.Xml.Serialization.XmlAttributeAttribute("parametr-XYZ")]
        public string parametrXYZ
        {
            get
            {
                return this.parametrXYZField;
            }
            set
            {
                this.parametrXYZField= value;
                this.RaisePropertyChanged("parametrXYZ");
            }
        }
       
        public xyzRequest()
        {
        }

        public xyzRequest(string parametrXYZ)
        {
            this.parametrXYZField = parametrXYZ;
        }
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        protected void RaisePropertyChanged(string propertyName)
        {
            System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
            if ((propertyChanged != null))
            {
                propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        }
    }
Czynność taką powtarzamy dla każdej klasy (i propertisu w tej klasie), w której występuje problematyczny dla nas parametr "parametrXYZ". W moim konkretnym przypadku to wystarczyło, a wywołanie usługi nast. poprzez:
string fuu = "Bar";
var raport = ws.pobranieraportu(fuu);

Korzystanie z web serwisów napisanych dla J2EE w .NET może nieść za sobą inne problemy np:
„Element XML 'rezultat-xyz' z obszaru nazw 'http://xyz.pl/ab/xyz' odwołuje się do metody i typu. Zmień nazwę komunikatu metody, używając atrybutu WebMethodAttribute, lub zmień element główny typu, używając atrybutu XmlRootAttribute.”
Powodem błędu jest zastosowanie dla kilku operacji: takiej samej nazwy typu zwracanego, którą jest „rezultat-xyz”. Technologia C#/.NET przewiduje obecność tylko unikalnych nazw elementów zwracanych przez poszczególne operacje. 
W celu wyeliminowania błędu niezbędna jest ręczna modyfikacja automatycznie wygenerowanej klasy Proxy. Modyfikacja polega na nadaniu unikalnych nazw elementów zwracanych przez wszystkie operacje dostępne w WebService, których nazwy elementów zwracanych kolidują ze sobą.

Inne problemy mogą wynikać z niestarannie napisanego XSD, lub innych, trudnych do zidentyfikowania powodów.



piątek, 11 października 2013

.NET Client consuming PHP ZendFramework Web Service - solution no.1 (WPF)

 Dostałem za zadanie zintegrowanie się z kilkoma web servisami. Naturalnym rozwiązaniem było wybranie do tego celu technologii WPF, szczególnie, że korzystanie z jego poprzednika zwanego potocznie "(SOAP) Web Services" jest niezalecane przez firmę Microsoft od 2009r:
 W książce "Professional-ASP-NET-4-5-in-C-and-VB.productCd-1118311825.html" jest napisane:

In 2009, ASMX Web Services were marked as legacy. Therefore, the code found within will notbe updated with future ASP.NET releases (the exception would be a security update).

 O ile zintegrowanie się z serwisami wystawionymi przez inne WCF poszło gładko, to pewne problemy napotkałem podczas integracji z web serwisem napisanym w PHP przy pomocy Zend Framework. Początkowo chciałem wykonać standardową operację połączenia, która powinna wyglądać tak samo, jak w przypadku korzystania z serwisu napisanego w WCF (link do artykułu, w którym programista łączy się z php za pomocą wcf). Niestety, w moim przypadku, WCF wykrył usługę, ale wyrzucił błąd:

There was an error downloading 'http://xxx.yyy.zzz.pl/Swdsoapserver/query?wsdl/_vti_bin/ListData.svc/$metadata'.
The request failed with the error message:
--
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAP-ENV:Fault><faultcode>Sender</faultcode><faultstring>Invalid XML</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>

--.
Metadata contains a reference that cannot be resolved: 'http://xxx.yyy.zzz.pl/Swdsoapserver/query?wsdl'.
The content type text/html; charset=UTF-8 of the response message does not match the content type of the binding (application/soap+xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly. The first 1024 bytes of the response were: '<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:tns="http://xxx.yyy.zzz.pl/Swdsoapserver/query" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" name="SwdQuerySoap"
    targetNamespace="http://xxx.yyy.zzz.pl/Swdsoapserver/query">
    <wsdl:types>
        <xsd:schema 
 Jak zwykle w takich przypadkach nieodzowne okazało się forum stackoverflow, gdzie zadałem pytanie: "consuming-php-web-service-by-wcf-client".
 Dzięki otrzymanej tam pomocy, zapisałem plik .wsdl na dysku i za jego pomocą utworzyłem klasy proxy (Add Service Reference, tylko zamiast linku, wskazujemy lokalizację pliku na dysku).

Tak wygenerowane klasy proxy potrafiły się połączyć i wykonać zapytanie, ale miały problemy z odczytaniem odpowiedzi. Błędy były dwa:

a) Wielkość przesyłanej odpowiedzi była większa, niż długość standardowej odpowiedzi
"Maksymalny przydział rozmiaru wiadomości dla wiadomości przychodzących (65536) został przekroczony. Aby zwiększyć przydział, użyj właściwości MaxReceivedMessageSize we właściwym elemencie wiązania."
Obejście tego problemu jest stosunkowo proste i polega na modyfikacji ustawień powiązania (ang. binding) wewnątrz pliku web.config/app.config naszej aplikacji:

        <binding name="Logic_DzSoapServer_QueryKoBinding" maxBufferSize="2147483647"
          maxReceivedMessageSize="2147483647">
          <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
            maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
        </binding>
b) Problem mappera z parsowaniem odpowiedzi wysyłanej przez PHP na obiekty .NET
"Nie można przypisać obiektu typu System.Int32 do obiektu typu System.Int32[]."
Sposobem aby to obejść okazała się ręczna modyfikacja wygenerowanych klas proxy, aby zamiast spodziewanego typu odpowiedzi, odpowiadały typem 'object':

        public object getParcel(CreditRiskEngine.ko.Logic_DzSoapServer_Request_BodyKo parameters, string hashCode) {
            return base.Channel.getParcel(parameters, hashCode);
        }

        public object getParcelAsync(CreditRiskEngine.ko.Logic_DzSoapServer_Request_BodyKo parameters, string hashCode)
        {
            return base.Channel.getParcelAsync(parameters, hashCode);
        }

     [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    [System.ServiceModel.ServiceContractAttribute(Namespace="http://xxx.yyy.zzz.pl/Dzsoapserver/queryko", ConfigurationName="ko.Logic_DzSoapServer_QueryKoPort")]
    public interface Logic_DzSoapServer_QueryKoPort {
      
        [System.ServiceModel.OperationContractAttribute(Action="http://xxx.yyy.zzz.pl/Dzsoapserver/queryko#getParcel", ReplyAction="*")]
    [System.ServiceModel.XmlSerializerFormatAttribute(Style=System.ServiceModel.OperationFormatStyle.Rpc, SupportFaults=true, Use=System.ServiceModel.OperationFormatUse.Encoded)]
        [return: System.ServiceModel.MessageParameterAttribute(Name="return")]
        object getParcel(CreditRiskEngine.ko.Logic_DzSoapServer_Request_BodyKo parameters, string hashCode);
      
        [System.ServiceModel.OperationContractAttribute(Action="http://xxx.yyy.zzz.pl/Dzsoapserver/queryko#getParcel", ReplyAction="*")]
        [return: System.ServiceModel.MessageParameterAttribute(Name="return")]
        object getParcelAsync(CreditRiskEngine.ko.Logic_DzSoapServer_Request_BodyKo parameters, string hashCode);
    }

 Co spowodowało, że wywołanie odpowiedzi było stosunkowo proste:

            List<MigDzKo> migDzKoList = new List<MigDzKo>();
            try
            {
                ko.Logic_DzSoapServer_QueryKoPortClient client = new ko.Logic_DzSoapServer_QueryKoPortClient();
                ko.Logic_DzSoapServer_Request_BodyKo request = new ko.Logic_DzSoapServer_Request_BodyKo();
                request.sy = "12345678";
                client.Open();
                var response = client.getParcel(request, Constraints.MIGDZ.MigDzHashBase64);
                XmlNode[] xmlNodes = (XmlNode[])response;
                for (int i = 0; i < xmlNodes.Length; i++)
                {
                    try
                    {
                        string organization = string.Empty;
                        if (xmlNodes[i].ChildNodes[0].HasChildNodes)
                        {
                            MigDzKo migDzKo = new MigDzKo();
                            migDzKo.operation_id = Convert.ToInt32(xmlNodes[i].ChildNodes[0]["operation_id"].InnerXml);
                            migDzKo.hashpublic = xmlNodes[i].ChildNodes[0]["hashpublic"].InnerXml;
                            migDzKoList.Add(migDzKo);
                        }
                    }
                    catch (Exception ex)
                    {
                        log.Error(ex.Message.ToString());
                    }
                }
                client.Close();
            }
            catch (Exception ex)
            {
                log.Error(ex.Message.ToString());
            }
            return migDzKoList;


Dla spójności podaję też zawartość klasy MigDzKo

    public class MigDzKo
    {
        public int operation_id { get; set; }
        public string hashpublic { get; set; }
    }

poniedziałek, 30 września 2013

Odrobina kryptografi

W przypadku, gdy zaistnieje potrzeba wykorzystania kryptografii, tj. zakodowania pewnych danych w aplikacji (z możliwością ich późniejszego odszyfrowania), powinno się unikać korzystania ze standardowych algorytmów szyfrujących dostępnych w .NET 2.0, ponieważ nie zapewniają one wystarczającego bezpieczeństwa.  W miarę bezpieczny, wydaje się algorytm  AES, jednak jak to w kryptografii bywa, skuteczna użyteczność poszczególnych algorytmów z biegiem czasu maleje.

Link, do działającego algorytmu w C#, można znaleźć na stackoverflow.com. 

Osobiście miałem okazję z niego korzystać w momencie, gdy jedynym skutecznym sposobem przekazania danych identyfikacyjnych z głównego okna aplikacji do popupa było wysłanie danych za pomocą querystringa (klient zażyczył sobie przeniesienie wszystkich plików typu BLOB z naszej bazy danych, do zewnętrznego syst. plików obsługiwanego przez firmę zewnętrzną z możliwością obsługi załączników do wniosku przez aplikacje trzecie).

Inne przydatne linki:
stackoverflow.com - how-to-start-learn-cryptography-with-c-sharp
http://stackoverflow.com - c-sharp-rsa-encryption-decryption-with-transmission



czwartek, 26 września 2013

Datatable to CSV (Excel)

Ost. zostałem poproszony o pomoc w pewnej sprawie. Sprawa dotyczyła... problemu z zapisem, a nast. odczytaniem dokumentu CSV na dysku. No cóż. Nie pozostało mi nic innego, jak zamienić FileStream na MemoryStream, a nast. przekazać go do obiektu klasy Attachment (pomijając zbędne zapisywanie załącznika na dysku). Przy okazji poprawiłem też metodę tworzenia samego załącznika (zapytanie SQL zwracało DataTable, który nast. był konwertowany na CSV). Całkiem sprytne i zgrabne rozwiązanie znalazłem na stackoverflow.com (convert-datatable-to-csv-stream).

Dlaczego więc piszę tutaj o tym? Ponieważ, w rozwiązaniu, które jest na stackoverflow, dokonałem kilku kosmetycznych zmian i postanowiłem je sobie tutaj zachować na przyszłość.

W klasie wywołującej kodowanie: "Windows-1250"
W klasie Extensions, w metodzie ToCSV znak oddzielający: ";"

niedziela, 21 lipca 2013

Programming never changes (asp mvc)

Czyli... w końcu rozpocząłem moją przygodę z ASP MVC na poważnie. Rozpocząłem ją od wszelkiej maści tutoriali, jakie można znaleźć na oficjalnej stronie asp mvc. O ile pierwszy tutorial z asp mvc 4 (MvcMovie) poszedł gładko, to już w nast. z asp mvc 3 (MvcMusicStore)  w 5 części tutka wystąpiły błędy, a dokładniej "Using the same DbCompiledModel to create contexts against different types of database servers is not supported". Ale... od czego mamy stackoverflow.com ;)

Parafrazując słynny cytat z serii Fallout: Programming never changes

środa, 26 czerwca 2013

Usuwanie elementu kolekcji podczas iterowania po tej kolekcji.

Kolekcje dynamiczne w jakie wyposażone są języki c# oraz java to wielki krok na przód w porównaniu do standardowego języka c++. Oczywiście w c++ były tablice i wskaźniki, a rozszerzeniach języka c++ kolekcje, jednak operowanie na dynamicznych listach z punktu widzenia programisty jest dużo bardziej wygodne i bezpieczne na kolekcjach niż na tablicach (ehh, te wskaźniki). Główna zaleta kolekcji polega bowiem na tym, że dynamicznie zmieniają one swoją objętość podczas dodawania i usuwania elementów. Takie zachowanie ma mnóstwo plusów, ale ma też jeden minus, tj. jeżeli iterujemy pętlą po kolekcji generycznej w standardowy sposób (od elementu 0, do elementu n) to usuwając elementy z listy, dynamicznie zmniejszamy jej wielkość, a co za tym idzie pętla będzie próbowała się odwołać do większej ilości elementów niż jest w kolekcji, a to spowoduje wystąpienie wyjątku. Oczywiście jest na to prosty i wygodny sposób, tj. iterowanie po kolekcji 'od tyłu'. Rozwiązanie zaczerpnięte z forum stackoverflow.com

var list = new List<int>(Enumerable.Range(1, 10));
for (int i = list.Count - 1; i >= 0; i--)
{
    if (list[i] > 5)
     list.RemoveAt(i);
}
list.ForEach(i => Console.WriteLine(i));

środa, 22 sierpnia 2012

jQuery growfield plugin

Kolejnym bardzo interesującym pluginem jQuery, który chciałbym zaprezentować, jest "growfield", czyli funkcjonalność dynamicznego rozszerzania się textBoxa znana z przeglądarki Mozilla Firefox. Na czym ta funkcjonalność polega? Na tym, że mając textbox i wpisując/usuwając z niego tekst, textbox automatycznie się rozszerza/minimalizuje.
Użytkownicy przeglądarki Mozilla Firefox mają tą funkcję w standardzie, aby jednak uzyskać podobną funkcjonalność w innych przeglądarkach, należy skorzystać z darmowego pluginu jQuery o nazwie "growfield".

Growfield, stanowi część projektu jQuery, i podobnie jak on, udostępniony jest na licencji MIT/X11.
Samo włączenie funkcji autorozszerzania jest banalnie proste i polega na
  • podpięciu odpowiednich bibliotek do projektu (zarówno standardowego jQuery.js w wersji minium 1.8, oraz growfield.js w wersji minimum 1.3),
  • dodaniu referencji do tych plików na stronie asp.net
  • wywołania prostego skryptu jQuery która aktywuje feature na odpowiednim obiekcie
Przykład skryptu aktywującego:

<script type="text/javascript">
    $(function () {
        $('.cssAreaNote').growfield();
    });
</script>

Samo podpięcie 'growfield' nie jest niczym innym, niż wywołanie dodatkowej funkcji na selektorze.W tym konkretnym przykładzie, jako element wyszukujący selektora  użyłem klasy CSS.

Uwaga: w trakcie pracy i testowania tej funkcjonalności spotkałem się z błędem w bibliotece, objawiającym się tym, że czasami do textboxa dopisywane były dodatkowe wartości (dokładnie '111'). Problem był już poruszony na forum StackOveflow. Tak jak ktoś dobrze zauważył, problem polegał na tym, że drugi "textBox" (rozwiązanie opiera się na pomyśle 2x textBoxów, z czego w jednym czasie tylko jeden z nich jest widoczny) miał taką samą nazwę jak pierwszy :/
Rozwiązanie tego problemu jest bardzo proste:
  • bierzemy plik z biblioteką 'growfield.js'
  • odnajdujemy funkcję o nazwie 'createDummy'
  • zmieniamy fragment odpowiadający za tworzenie "kopii zapasowej'' (czyli textBoxa nr.2) na:   
var dummy = o.clone().addClass('growfieldDummy').attr('name', '').attr('id', o.attr('id') + '-dummy')
                               .css({position: 'absolute', left: -9999, top: 0, height: '20px', resize: 'none'})
                               .insertBefore(o).show();

Możliwe, że w momencie, w którym Ty, mój drogi czytelniku pobierzesz najnowszą wersję tej biblioteki, problem będzie już rozwiązany. Możliwe jednak, że... pojawi się również u Ciebie, a wtedy będziesz wiedział co zrobić, aby temu zaradzić ;)