Wrong problem with Autofac WCF Integration

Autofac is a brand new .Net Dependency Injection / IoC just like Castle Windsor, etc. Autofac like Castle has a nice WCF integration facility. I was trying hard to establish a self hosted WCF server using Autofac. But was not successful at all. I guessed that is because of IP, Windows Firewall, Administration rights, version incompatibility and everything you say.

I tried Autofac samples but couldn’t get them running. After all I tried Alex’s solution for second or third time and realized that his sample works very fine. By watching error messages from Microsoft WcfTestClient more carefully that was clear that an internal error was occurring. But how an internal error could be viewed by WcfTestClient? Yes you are right, by using IncludeExceptionDetailInFaults. I found that in a question on SO here. So my modified version of Alex’s sample goes as here:

private static void StartServerOriginal()
{
    ContainerBuilder builder = new ContainerBuilder();
    builder.Register(c => new MySecurityManager()).As<IMySecurityManager>();

    using (IContainer container = builder.Build())
    {
        Uri address = new Uri("http://localhost:8046/MySecurityManager");
        ServiceHost host = new ServiceHost(typeof(MySecurityManager), address);
        host.AddServiceEndpoint(typeof(IMySecurityManager), new BasicHttpBinding(), string.Empty);

        IComponentRegistration registration;
        if (!container.ComponentRegistry.TryGetRegistration(new TypedService(typeof(IMySecurityManager)), out registration))
        {
            Console.WriteLine("The service contract has not been registered in the container.");
            Console.ReadLine();
            Environment.Exit(-1);
        }

        (host.Description.Behaviors[typeof(ServiceDebugBehavior)] as ServiceDebugBehavior).IncludeExceptionDetailInFaults = true;
        host.Description.Behaviors.Add(new AutofacDependencyInjectionServiceBehavior(
            container, typeof(MySecurityManager), registration));
        host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true, HttpGetUrl = address });

        host.Open();

        Console.WriteLine("The host has been opened.");
        Console.ReadLine();

        host.Close();
        Environment.Exit(0);
    }
}

By running WcfTestClient again I found that some of my own service contract’s exceptions were not marked as Serializable!

Oh Autofac, please forgive me, as I am a former user of Castle WCF Facility, I was thinking root cause of problem was you! Sorry, the problem was in my own code.

‫NHibernate Session Management در Winform و WCF

بحث NHibernate Session Management در برنامه‌های وب اصلاً کار سختی نیست. روتین‌ها و نمونه‌های زیادی هم در مورد آن وجود دارد. اما انجام همین بحث در Winform و WCF کمی کار می‌برد. یکی از بهترین روش‌ها برای مدیریت Session در NHibernate استفاده از الگوی Unit Of Work است. تطبیق این الگو با مدل کاری وب خیلی ساده است. فقط کافی است فرض کنید شروع هر unit of workی با شروع Web Request توسط کاربر و پایان آن با رندر صفحه و ارسال آن به کلاینت منطبق است.

برای ویندوز و WCF هم که از بسیاری جهات به هم شبیه هستند چند روش وجود دارد. یکی از این راه‌ها conversation per business transaction یا CpBT است. در CpBT منطق کاری (business transaction) مبنای کار است. یعنی شروع unit of work با شروع عملیات کاری و پایان آن با پایان عملیات کاری اتفاق می‌افتد. به عنوان مثال فرض کنید یک منطق یا روال کاری داریم به نام «محاسبه حقوق ماهیانه شخص فلان». در CpBT شروع unit of work وقتی است که کاربر از طریق winform یا wcf روال «محاسبه حقوق» را شروع می‌کند. به عبارت دیگر شروع روال «محاسبه حقوق» توسط کاربر باعث می‌شود یک session از NHibernate دریافت شده و یک Transaction شروع شود. حال هر وقت که کاربر پایان کار را با مثلاً فشردن دکمه تایید به پایان رساند، پایان unit of work هم فرا رسیده و کارهای مرتبط با آن مثل commit شدن transaction هم انجام می‌شود. دقت شود که اگر از الگوی CpBT استفاده نمی‌شد ممکن بود در حین کاری مثل محاسبه حقوق که چندین و چند مراجعه به دیتابیس و چند modify اطلاعات دارد، مجبور شویم چندین session از NHibernate گرفته و چندین بار commit transation داشته باشیم. که هم کار را خیلی کند می‌کند و هم مدیریت transactionها را غیر ممکن می‌کند.

برای اعمال CpBT در NHibernate و WCF و Winform یک کتابخانه مفید به نام uNhAddins وجود دارد. این کتابخانه تمام این کار را اتوماتیک می‌کند. مثلاً برای آن که بگویید یک متودی از یک کلاس برابر است با نقطه شروع یک CpBT فقط کافی است روی کلاس آن عبارت

[PersistenceConversational(MethodsIncludeMode = MethodsIncludeMode.Implicit)]

و روی متود آن عبارت

[PersistenceConversation(ConversationEndMode = EndMode.End)]

را قرار دهید. البته نیاز به کدها و تنظیمات دیگری هم وجود دارد. از جمله این که اگر WCF کار می‌کنید، از uNhAddIns.SessionEasier.Contexts.ThreadLocalSessionContext به عنوان current_session_context_class در تنظیمات NHibernate استفاده کنید و اگر از WinForm استفاده می‌کنید از uNhAddIns.SessionEasier.Conversations.ThreadLocalConversationalSessionContext استفاده کنید.

به عنوان یک نمونه کامل uNhAddins و CpBT که هم WCF را پوشش می‌دهد، هم WPF را، هم MVP را و هم IoC را، نگاهی به نمونه کد uNHAddins.Examples.SessionManagement در مخزن کد uNhAddins بیندازید.

WcfFacility Hello World

WcfFacility is one facilities of Castle Windsor facilities that helps you ignoring adding service reference by Visual Studio. With WcfFacility you can ease server and client service management. Before starting this sample I searched for and found a good starting point here. Unfortunately this sample was usng .svc file. As I didn’t want to use .svc file I wrote my own hello world sample with Wcf Facility:

Service code:

        public static void StartService()
        {
            var windsorContainer = new WindsorContainer()
                .AddFacility<WcfFacility>()
                .Register(Component.For<IDateService>()
                    .ImplementedBy<DateService>()
                    .DependsOn(new { number = 42 })
                    .AsWcfService(new DefaultServiceModel().AddEndpoints(
                            WcfEndpoint.BoundTo(new NetTcpBinding { PortSharingEnabled = true })
                                .At(“net.tcp://localhost:8063/DateService”)
                               ).PublishMetadata()
                ));
        }

Client Code:

        public static string InvokeWcfService()
        {
            var container = new WindsorContainer().AddFacility<WcfFacility>();

            container.Register(Component
                .For<IDateService>()
                .AsWcfClient(DefaultClientModel
                .On(WcfEndpoint.BoundTo(new NetTcpBinding())
                .At(“net.tcp://localhost:8063/DateService”)))
                .LifeStyle.Transient
                );

            var dateService = container.Resolve<IDateService>();
            string message = dateService.SaySomething(“123”);

            return message;
        }

Complete sample source code be downloaded here.

‫سه راه برای ارتباط با سرویس WCF

برای آن که از داخل یک برنامه بتوان از سرویس‌های WCF یک سرور استفاده کرد، یعنی wcf client ایجاد کرد، اقلاً سه راه وجود دارد:

۱- اضافه کردن service reference با کمک ویژوال استودیو: در این روش خود شما فقط url سرویس مورد نظر را به ویژوال استودیو می‌دهید. ویژوال استودیو هم کلاس‌ها و interfaceهای لازم را برای تولید proxy مورد نظر تولید می‌کند. بدی این روش این است که باید مقدار زیادی کد را نگه دارید بدون آن که خودتان آن را تولید کرده باشید. علاوه بر این کد تولیدی وابستگی زیادی به سرویس دارد، با کمترین تغییر در سرویس WCF شما هم باید service reference را update کرده و مجدداً برنامه را کامپایل کنید. ایراد آخر هم این است که موقع برنامه‌نویسی کلاینت باید سرور هم در حال اجرا و در دسترس باشد در غیر این صورت نمی‌توانید عملیات add reference را انجام دهید.

۲-اضافه کردن service reference با کمک ویژوال استودیو و instance گیری با کمک ابزارهای DI و IoC مثل Castle Windsor: این روش هم مشابه روش قبلی است با این تفاوت که روال instance گیری را به Castle Windsor می‌سپارید و خودتان درگیر نمی‌شوید.

۳- استفاده از WCF Facility برای ایجاد پراکسی‌های WCF بدون اجبار به Add Reference کردن یا تهیه کلاس‌های کلاینتی. در این روش به طور runtime و با کمترین دردسری می‌توان برای هر نوع سرویس WCFی پراکسی تعریف کرده و مورد استفاده قرار داد. WCF Facility یکی از امکانات جانبی Castle Windsor است. از WCF Facility حتی برای سرور WCF هم می‌توان استفاده کرد.

منبع:
http://blog.hatchan.nl/2010/07/wcf-facility-for-castle-windsor-client.html

‫دسترسی به app.config در نصب Wcf Windows Service

اگر بخواهید سرویس WCF را به عنوان سرویس ویندوز اجرا کنید می‌توانید از این راهنما استفاده کنید. بخشی از این کار نصب سرویس در ویندوز است. اگر در این installer که باید در برنامه شما باشد، نیاز به اطلاعات app.config داشته باشید باید دقت کنید که دسترسی به app.config در حین نصب امکان‌پذیر نیست. پس باید از روش‌های دیگری مثل این استفاده کنید.

‫چند نکته در باب WCF

WCF هم مثل هر تکنولوژی و سکوی جدیدی با تعدادی نکته همراه است. به نکات زیر دقت کنید:

عدم دسترسی به HttpCpntext
ممکن است حین کار با WCF نیاز به HttpContext.Current.Request یا غیر داشته باشید. این نیاز وقتی که سرویس WCFتان در IIS میزبانی می‌شود بیشتر هم می‌شود. متاسفانه HttpContext.Current در داخل یک سرویس WCF همواره null است چون اصولاً یک سرویس WCF ربطی به Http ندارد. در WCF باید به جای HttpContext از OperationContext.Current استفاده کرد.

آدرس خالی
لزومی ندارد که همیشه اسم یا IP سرویس دهنده در config مربوط به سرویس دهنده مشخص شود. بیشتر وقت‌ها می‌توان بخش address مربوط به endpoint را خالی گذاشت.
امکان مشاهده پیغام خطای رخ داده در سرور WCF
برنامه‌های ASP.NET را می‌شد طوری در سرور تنظیم کرد که اگر خطایی رخ داد به کاربر هیچ چیزی از خطا نمایش داده نشود یا این که بشود. همین کار را می‌توان در config سرور WCF با استفاده از <servicedebug includeexceptiondetailinfaults=”true” > انجام داد.

مشکل در WCF RSS در سرویس‌های WCF که مثل RSS به نوعی با وب سر و کار دارند ممکن است لازم شود نوع binding به صورت webHttpBinding تعریف شود. لینک

‫تولید Feed با استفاده WCF

مطمئناً برای تولید Feedهای RSS یا ATOM در ASP.NET راه‌ها و کدهای زیادی وجود دارد. اما یکی از راه‌های جالب تولید Feed در دات‌نت، استفاده از WCF است. WCF با استفاده از چند خط کد ساده برای شما Feed می‌سازد.

سرویس‌های Feed معمولاً در برنامه‌های تحت وب که در IIS اجرا می‌شوند قرار داده می‌شوند. بنابراین باید سرویس WCF را در فایل‌های ‎.svc قرار داد. اگر سرویس در یک host با دسترسی خیلی پایین مثل سایت‌های اینترنتی قرار داشته باشد نمی‌توان از کلاس ServiceHost استفاده کرد. بنابراین باز هم باید از فایل‌های ‎.svc که توسط IIS میزبانی (host) می‌شوند استفاده کرد. خوشبختانه امکانات اصلی WCF از نسخه ۳٫۵ دات‌نت در دسترس هستند و از آنجا که دات‌نت ۳٫۵ روی runtime نسخه ۲ دات‌نت هم کار می‌کند بنابراین مشکلی در سرورهای ویندوز ۲۰۰۳ و IISهای قبل از نسخه ۷ هم وجود ندارد.

جهت کسب اطلاعات بیشتر به MSDN مراجعه کنید. در صورت استفاده از IIS بایستی از webHttpBinding به عنوان binding استفاده کرد.

‫استفاده از Authentication/Authorization استاندارد WCF یا پیاده‌سازی خودمان؟

WCF خودش مکانیزم‌های زیادی برای Authentication و Authorization دارد. یکی از سناریوهای ممکن برای Membership یک برنامه نوعی می‌تواند این باشد که Membership در سرور با استفاده از یک Windows Domain Controller تعریف شده و به ازای همه کاربران و نقش‌های ممکن، در آن domain به تعداد مورد نیاز Windows Account و Windows Group ساخته شود. سپس Role Management تمام سرویس‌ها بر اساس این Windows Groupها تعریف شده و کاربران برنامه WCF هم بر اساس Windows Accountهای تعریف شده در domain وارد سیستم شوند.

سناریوی دوم این است که از مکانیزم Authentication و Authorization داخلی WCF استفاده نکرده و به جای آن خودمان همه موارد مورد نیاز Membership را پیاده‌سازی کنیم. این طوری کنترل بیشتری روی کار داریم ولی باید زحمت بیشتری کشیده و خیلی از موارد را که قبلاً WCF پیاده‌سازی کرده، مجدداً خودمان پیاده‌سازی نماییم. هر چند که این روش توصیه شده نیست ولی وقتی که روش Authentication و Authorization انتخابی ما زیادی پیچیده و غیر معمول است چاره‌ای نیست جز این که خودمان همه چیز را انجام دهیم.

‫ایمن‌سازی انتقال اطلاعات در WCF

بعد از آن که سرور و کلاینت خودشان را به هم شناساندند، نیاز هست که کانال ارتباطی بین آنها هم ایمن‌سازی شود. یعنی هیچ کسی وسط راه نتواند این پیغام‌ها را خوانده یا دستکاری کند. ایمن بودن پیغام‌های در WCF سه جنبه دارد، integrity به معنی دست نخورده بودن پیغام، privacy به این معنی که هیچ کس وسط راه پیغام‌ها را نخوانده باشد و mutual authentication به این معنی که پیغام صادره از طرف کلاینت صرفا به دست سرور اصلی برسد نه سرورهای دیگر.

WCF پنج روش را برای ایمن‌سازی انتقال اطلاعات فراهم می‌کند. اولین روش، عدم استفاده از هیچ نوع ایمن‌سازی است. دومین روش، ایمن‌سازی لایه انتقال است. در این روش WCF از یکی از پروتکل‌های ایمن HTTPS، TCP، IPC یا MSMQ برای انتقال اطلاعات استفاده می‌کند. این روش برای وقتی که کلاینت و سرور به طور مستقیم به هم وصل نیستند مشکلاتی دارد. سومین روش، ایمن‌سازی خود پیغام‌ها است. در این روش پیغام‌ها اول رمزنگاری شده سپس انتقال داده می‌شوند. این روش هم روی پروتکل‌های ساده مثل HTTP قابل اجراست هم بر عکس روش ایمن‌سازی لایه انتقال، نیازی به ارتباط مستقیم بین کلاینت و سرور ندارد. بنابراین از این روش در فضای اینترنتی هم می‌توان استفاده کرد. تنها نکته منفی این روش، overhead اجرای آن به دلیل رمزنگاری تک تک پیغام‌هاست. روش چهارم و پنجم هم عبارتند از ترکیب روش دوم و سوم یا استفاده هم‌زمان از هر دو که ممکن است خیلی هم مورد استفاده قرار نگیرند.

منبع:
فصل ۱۰ کتاب Programming WCF Services

‫Authentication و Authorization در WCF‫

Authentication در WCF به چندین روش انجام می‌شود:

No authentication
هیچ نوع authenticationی انجام نمی‌شود.

Windows authentication
در صورت وجود domain server از Kerberos و در غیر این صورت از NTLM استفاده می‌شود.

Username و Password
user name و password می‌تواند بر اساس اکانت‌های ویندوز یا یک روش سفارشی سازی شده سنجیده شود.

X509 certificate
کلاینت خودش را با استفاده از یک certificate به سرور می‌شناساند.

Custom mechanism
برنامه‌نویسان می‌توانند روش authentication خاص خودشان را پیاده‌سازی کرده و WCF را مجبور به استفاده از آن کنند.

Issued Token
در این روش هم سرور و هم کلاینت از یک سرویس دیگر مثل Windows Card Space برای authentication استفاده می‌کنند.

WCF برای Authorization هم از دو روش آشنا استفاده می‌کند. یکی استفاده از Windows groups و دیگری استفاده از ASP.NET role provider.

منبع: فصل ۱۰ کتاب Programming WCF Services