۱۳۹۰/۰۶/۰۸

تهيه فيد از تغييرات SVN


كتابخانه‌ي iTextSharp 5.1.2 هفته‌ي قبل منتشر شده و ... من هر چقدر سايتي، بلاگي جايي را جستجو كردم كه خلاصه‌اي از تغييرات انجام شده آن‌را گزارش دهد، چيزي نيافتم. ولي خوب، مطابق روال متداول كتابخانه‌هاي سورس باز، حداقل مي‌توان به change log مرتبط با سورس كنترل آن‌ها مراجعه كرد. مثلا:


البته اين هم خوب است ولي اي‌كاش مي‌شد مثلا يك فيد هم از اين تغييرات تهيه كرد. يك سري از سايت‌هاي هاستينگ مثل CodePlex و GitHub يك چنين فيدهايي را دارند. اما به نظر SourceForge از اين لحاظ اندكي ضعيف است.
سايت روسي زير مي‌تواند با گرفتن آدرس يك مخزن كد SVN (براي مثال: https://itextsharp.svn.sourceforge.net/svnroot/itextsharp/trunk/ ) يك فيد RSS از آن تهيه كند:


در همين راستا برنامه‌ي CommitMonitor هم موجود است.



۱۳۹۰/۰۶/۰۷

آدرس جديد مخزن كد NHibernate

تيم NHibernate از سيستم SVN سورس فورج، به سورس كنترل Git در سايت GitHub نقل مكان كرده است: [^]
همچنين Issue tracker آن‌ها هم مدتي است كه به آدرس جديدي منتقل شده است: [^]
و ... اگر علاقمند باشيد كه از آخرين تغييرات اين كتابخانه آگاه شويد، زياد به دنبال وبلاگ‌ يا سايت خاصي نگرديد. روش متداول كار با كتابخانه‌هاي سورس باز، دنبال كردن change log ارسالي آن‌ها به سيستم‌هاي سورس كنترل است (همان متني كه حين commit ارسال مي‌كنند). براي مثال جهت آگاه شدن از آخرين تغييرات NHibernate مشترك اين فيد شويد: [^]






۱۳۹۰/۰۶/۰۴

كمپين ضد IF !


بكارگيري بيش از حد If و خصوصا Switch برخلاف اصول طراحي شيءگرا است؛ تا اين حد كه يك كمپين ضد IF هم وجود دارد!



البته سايت فوق بيشتر جنبه تبليغي براي سمينارهاي گروه مذكور را دارد تا اينكه جنبه‌ي آموزشي/خود آموزي داشته باشد.

يك مثال كاربردي:
فرض كنيد داريد يك سيستم گزارشگيري را طراحي مي‌كنيد. به جايي مي‌رسيد كه نياز است با Aggregate functions سروكار داشته باشيد؛ مثلا جمع مقادير يك ستون را نمايش دهيد يا معدل امتيازهاي نمايش داده شده را محاسبه كنيد و امثال آن. طراحي متداول آن به صورت زير خواهد بود:

using System.Collections.Generic;
using System.Linq;

namespace CircularDependencies
{
    public enum AggregateFunc
    {
        Sum,
        Avg
    }

    public class AggregateFuncCalculator
    {
        public decimal Calculate(IList<decimal> list, AggregateFunc func)
        {
            switch (func)
            {
                case AggregateFunc.Sum:
                    return getSum(list);
                case AggregateFunc.Avg:
                    return getAvg(list);
                default:
                    return 0m;
            }
        }

        private decimal getAvg(IList<decimal> list)
        {
            if (list == null || !list.Any()) return 0;
            return list.Sum() / list.Count;
        }

        private decimal getSum(IList<decimal> list)
        {
            if (list == null || !list.Any()) return 0;
            return list.Sum();
        }
    }
}

در كلاس AggregateFuncCalculator يك متد Calculate داريم كه توسط آن قرار است روي list دريافتي يك سري عمليات انجام شود. عمليات پشتيباني شده هم توسط يك enum معرفي شده؛ براي مثال اينجا فقط جمع و ميانگين پشتيباني مي‌شوند.
و مشكل طراحي اين كلاس، همان switch است كه برخلاف اصول طراحي شيء‌گرا مي‌باشد. يكي از اصول طراحي شيءگرا بر اين مبنا است كه:
يك كلاس بايد جهت تغيير، بسته اما جهت توسعه، باز باشد.

يعني چي؟
داستان طراحي Aggregate functions كه فقط به جمع و ميانگين خلاصه نمي‌شود. امروز مي‌گويند واريانس چطور؟ فردا خواهند گفت حداقل و حداكثر چطور؟ پس فردا ...
به عبارتي اين كلاس جهت تغيير بسته نيست و هر روز بايد بر اساس نيازهاي جديد دستكاري شود.

چكار بايد كرد؟
آيا مي‌توانيد در كلاس AggregateFuncCalculator يك الگوي تكراري را تشخيص دهيد؟ الگوي تكراري موجود، محاسبات بر روي يك ليست است. پس مي‌شود بر اساس آن يك اينترفيس عمومي را تعريف كرد:

public interface IAggregateFunc
{
     decimal Calculate(IList<decimal> list);
}

اكنون هر كدام از پياده سازي‌هاي موجود در كلاس AggregateFuncCalculator را به يك كلاس جدا منتقل خواهيم كرد تا يك اصل ديگر طراحي شيءگرا نيز محقق شود:
هر كلاس بايد تنها يك كار را انجام دهد.

public class Sum : IAggregateFunc
{
        public decimal Calculate(IList<decimal> list)
        {
            if (list == null || !list.Any()) return 0;
            return list.Sum();
        }
}

public class Avg : IAggregateFunc
{
        public decimal Calculate(IList<decimal> list)
        {
            if (list == null || !list.Any()) return 0;
            return list.Sum() / list.Count;
        }
}

تا اينجا 2 هدف مهم حاصل شده است:
- كم كم كلاس AggregateFuncCalculator دارد خلوت مي‌شود. قرار است هر كلاس يك كار را بيشتر انجام ندهد.
- برنامه از بسته بودن جهت توسعه هم خارج شده است (يكي ديگر از اصول طراحي شيءگرا). اگر تعاريف توابع محاسباتي را تماما در يك كلاس قرار دهيم صاحب اول و آخر آن كتابخانه خودمان خواهيم بود. اين كلاس بسته است جهت تغيير. اما با معرفي IAggregateFunc، من امروز 2 تابع را تعريف كرد‌ه‌ام، شما فردا توابع خاص خودتان را تعريف كنيد. باز هم برنامه كار خواهد كرد. نيازي نيست تا من هر روز يك نگارش جديد از كتابخانه را ارائه دهم كه در آن فقط يك تابع ديگر اضافه شده است.

اكنون يكي از چندين و چند روش بازنويسي كلاس AggregateFuncCalculator به صورت زير مي‌تواند باشد

public class AggregateFuncCalculator
{
        public decimal Calculate(IList<decimal> list, IAggregateFunc func)
        {
            return func.Calculate(list);
        }
}

بله! ديگر سوئيچي در كار نيست. اين كلاس تنها يك كار را انجام مي‌دهد. همچنين ديگر نيازي به تغيير هم ندارد (محاسبات از آن خارج شده) و باز است جهت توسعه (شما نگارش‌هاي دلخواه IAggregateFunc ديگر خود را توسعه داده و استفاده كنيد).

۱۳۹۰/۰۶/۰۲

تعريف رنگ در iTextSharp


در كتابخانه‌ي iTextSharp به جهت سازگاري با كتابخانه‌ي اصلي، رنگ‌ها را بر اساس كلاسي به نام BaseColor تعريف كرده‌اند؛ كه اي‌كاش به جاي اين‌كار، همه را با كلاس Color فضاي نام استاندارد System.Drawing جايگزين مي‌كردند. همين مشكل با فونت هم هست. يك كلاس فونت در فضاي نام iTextSharp.text وجود دارد به علاوه كلاس فونت تعريف شده در فضاي نام استاندارد System.Drawing دات نت؛ كه خيلي سريع مي‌تواند به خطاي كامپايل زير ختم شود:

'Font' is an ambiguous reference between 'iTextSharp.text.Font' and 'System.Drawing.Font'	

و در نهايت مجبور خواهيم شد كه به صورت صريح علام كنيم، iTextSharp.text.Font منظور ما است و نه آن يكي.
در كل اگر با كلاس Color فضاي نام استاندارد System.Drawing بيشتر راحت هستيد به صورت زير هم مي‌توان رنگ‌هاي متداول را مورد استفاده قرار داد:

تعريف رنگ‌ها بر اساس نام آن‌ها:

var color = new BaseColor(Color.LightGray);

تعريف رنگ‌ها بر اساس مقادير Hex متداول در المان‌هاي HTML :

var color = new BaseColor(ColorTranslator.FromHtml("#1C5E55"));

اين نكته‌اي است كه شايد خيلي‌ها از وجود آن بي‌اطلاع باشند. به صورت پيش فرض در كلاس استاندارد ColorTranslator، امكان دريافت رنگ‌هاي بكاررفته در المان‌هاي HTML به كمك متد ColorTranslator.FromHtml مهيا است.
البته اگر زماني خواستيد خودتان اين متد را پياده سازي كنيد، نكته‌ي آن به صورت زير است:

string htmlColor = "#1C5E55";
int x = Convert.ToInt32(htmlColor.Replace("#", "0x"), 16);
byte red   = (byte)((x & 0xff0000) >> 16);
byte green = (byte)((x & 0xff00) >> 8);
byte blue  = (byte)(x & 0xff);

سپس كلاس‌هاي Color و همچنين BaseColor امكان پذيرش اين اجزاي حاصل را دارند (به كمك متد Color.FromRgb و يا سازنده‌ي BaseColor).
علت ذكر ColorTranslator.FromHtml به اين بر مي‌گردد كه تركيبات رنگ جالبي را مي‌توان از جداول HTML ايي موجود در سايت‌هاي مختلف ايده گرفت و يا حتي از قالب‌هاي پيش فرض GridView در ASP.NET مثلا.


اكنون براي ساخت جدولي مانند شكل فوق، به ازاي هر سلولي كه مشاهده مي‌كنيد بايد يكبار BorderColor و BackgroundColor تنظيم شوند. رنگ متن هم از رنگ فونت دريافت مي‌شود:

var pdfCell = new PdfPCell(new Phrase(Text, Font))
            {
                RunDirection = ...,
                BorderColor = ...,
                BackgroundColor = ...
            };


۱۳۹۰/۰۵/۳۱

روش صحيح تعريف قلم در iTextSharp


روش متداول تعريف فونت در iTextSharp به صورت زير است:

public static iTextSharp.text.Font Tahoma()
{
     var fontPath = Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\tahoma.ttf";
     var baseFont = BaseFont.CreateFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
     return new Font(baseFont);
}

از آنجائيكه خصوصا براي متون فارسي نياز است تا به ازاي هر المان كوچكي اين فونت تنظيم شود و‌ در غير اينصورت متني نمايش داده نخواهد شد، با سربار بالايي مواجه خواهيم شد. بنابراين به نظر مي‌رسد كه بهتر باشد اين توليد اشياء فونت را كش كنيم. خوشبختانه iTextSharp سيستم كش كردن تعريف قلم‌هاي متفاوت را هم به صورت توكار دارا است:

public static iTextSharp.text.Font GetTahoma()
{
    var fontName = "Tahoma";
    if (!FontFactory.IsRegistered(fontName))
    {
         var fontPath = Environment.GetEnvironmentVariable("SystemRoot") + "\\fonts\\tahoma.ttf";
         FontFactory.Register(fontPath);
    }
    return FontFactory.GetFont(fontName, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
}

كلاس FontFactory كار ثبت و بازيابي قلم‌هاي متفاوت را به عهده دارد. تنها كافي است يكبار قلمي در آن ثبت شود (FontFactory.Register)، بار ديگر اطلاعات قلم به سادگي از كش FontFactory خوانده خواهد شد (FontFactory.GetFont).

۱۳۹۰/۰۵/۲۹

ايجاد ليستي از كلاسي جنريك

كلاس جنريك زير را در نظر بگيريد:
public class Column<T>
{
     public string Name { set; get; }
     public T Data { set; get; }
}

مشكلي كه با اين نوع كلاس‌ها وجود دارد اين است كه نمي‌توان مثلا ليست زير را در مورد آن‌ها تعريف كرد:

IList<Column<T>> myList = new List<Column<T>>();


به عبارتي مي‌خواهيم يك ليست از كلاسي جنريك داشته باشيم. راه حل انجام آن به صورت زير است:

using System.Collections;

namespace Tests
{
    public interface IColumn
    {
        string Name { set; get; }
        object Data { set; get; }
    }

    public class Column<T> : IColumn
    {
        public string Name { set; get; }

        public T Data { set; get; }

        object IColumn.Data
        {
            get { return this.Data; }
            set {  this.Data = (T)value; }
        }
    }
}

ابتدا يك اينترفيس عمومي را همانند اعضاي كلاس Column تعريف مي‌كنيم كه در آن بجاي T از object‌ استفاده شده است. سپس يك پياده سازي جنريك از اين اينترفيس را ارائه خواهيم داد؛ با اين تفاوت كه اينبار خاصيت Data مربوط به اينترفيس، به صورت خصوصي و صريح با استفاده از IColumn.Data تعريف مي‌شود و نمونه‌ي جنريك هم نام آن، عمومي خواهد بود.
اكنون مي‌توان نوشت:

var myList = new List<IColumn>();

براي مثال در اين حالت تعريف ليست زير كه از تعدادي وهله‌ي كلاسي جنريك ايجاد شده، كاملا مجاز مي‌باشد:

var myList = new List<IColumn>
{
    new Column<int> { Data = 1, Name = "Col1"},
    new Column<double> { Data = 1.2, Name = "Col2"}
};

خوب، تا اينجا يك مرحله پيشرفت است.اكنون اگر بخواهيم در اين ليست، Data مثلا عنصري را كه نامش Col1 است، دريافت كنيم چه بايد كرد؟ آن هم نه به شكل object بلكه از نوع T مشخص:

static T GetColumnData<T>(IList<IColumn> list, string name)
{
    var column = (Column<T>)Convert.ChangeType(list.Single(s => s.Name.Equals(name)), typeof(Column<T>), null);
    return column.Data;
}

و نمونه‌اي از استفاده آن:

int data = GetColumnData<int>(myList, "Col1");

۱۳۹۰/۰۵/۲۸

به روز رساني مهم تمام برنامه‌هاي ASP.NET WebForms و همچنين SharePoint


اين روزها توسعه دهندگان مرورگرها، شماره‌هاي نگارش‌هاي برنامه‌هاي خود را مرتبا و با اعداد نجومي بالا مي‌برند. امروز فايرفاكس 5، تا چند وقت ديگر فايرفاكس 8 هم در راه خواهد بود؛ ساير مرورگرها نيز به همين ترتيب. به همراه نگارش‌هاي متفاوت دات نت، يك سري browser definition files نيز وجود دارد كه تنها بازه‌ي محدودي از اين شماره نگارش‌ها در آن‌ها تعريف شده است. در نتيجه زمانيكه مثلا مرورگر كاربر IE 10 باشد، آن‌را به عنوان unknown browser تشخيص داده و يك سري از قابليت‌ها را به صورت خودكار حذف مي‌كند. براي مثال جاوا اسكريپت براي اين نوع مرورگرها خاموش مي‌شود. منظور از خاموش كردن جاوا اسكريپت اين است كه موتور ASP.NET Webforms ديگر يك سري اسكريپت‌هاي توليدي مخصوص خود را به صفحه تزريق نخواهد كرد؛ براي مثال تمام PostBackهاي سايت شما از كار خواهند افتاد و مسايلي از اين دست.

روش حل آن هم ساده است. بايد فايل‌هاي firefox.browser و ie.browser به روز شده را در پوشه‌ي استاندارد App_Browsers كپي كنيد و همين!
به زودي يك آپديت دات نت نيز جهت به روز رساني در سطح ماشين، ارائه خواهد شد. فعلا مجبور هستيد به ازاي تك تك برنامه‌هاي خود اين پوشه‌ي به روز شده را كپي كنيد.

جهت توضيحات بيشتر و دريافت فايل‌‌هاي ذكر شده به اين مطلب مراجعه نمائيد.


۱۳۹۰/۰۵/۲۶

چند ستونه كردن در iTextSharp


فرض كنيد جدولي داريد با چند ستون محدود كه نتيجه‌ي نهايي گزارش آن مثلا 100 صفحه است. جهت صرفه جويي در كاغذ مصرفي شايد بهتر باشد كه اين جدول را به صورت چند ستوني مثلا 5 ستون در يك صفحه نمايش داد؛ چيزي شبيه به شكل زير:


روش انجام اينكار به كمك iTextSharp به صورت زير است:


using System;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.Diagnostics;

class Program
{
        static void Main(string[] args)
        {
            using (var pdfDoc = new Document(PageSize.A4))
            {
                var pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream("Test.pdf", FileMode.Create));
                pdfDoc.Open();

                var table1 = new PdfPTable(1);
                table1.WidthPercentage = 100f;
                table1.HeaderRows = 2;
                table1.FooterRows = 1;

                //header row
                var headerCell = new PdfPCell(new Phrase("header"));
                table1.AddCell(headerCell);

                //footer row
                var footerCell = new PdfPCell(new Phrase(" "));
                table1.AddCell(footerCell);

                //adding some rows
                for (int i = 0; i < 400; i++)
                {
                    var rowCell = new PdfPCell(new Phrase(i.ToString()));
                    table1.AddCell(rowCell);
                }

                // wrapping table1 in multiple columns
                ColumnText ct = new ColumnText(pdfWriter.DirectContent);
                ct.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
                ct.AddElement(table1);

                int status = 0;
                int count = 0;
                int l = 0;
                int columnsWidth = 100;
                int columnsMargin = 7;
                int columnsPerPage = 4;
                int r = columnsWidth;
                bool isRtl = true;

                // render the column as long as it has content
                while (ColumnText.HasMoreText(status))
                {
                    if (isRtl)
                    {
                        ct.SetSimpleColumn(
                            pdfDoc.Right - l, pdfDoc.Bottom,
                            pdfDoc.Right - r, pdfDoc.Top
                        );
                    }
                    else
                    {
                        ct.SetSimpleColumn(
                            pdfDoc.Left + l, pdfDoc.Bottom,
                            pdfDoc.Left + r, pdfDoc.Top
                        );
                    }

                    var delta = columnsWidth + columnsMargin;
                    l += delta;
                    r += delta;

                    // render as much content as possible
                    status = ct.Go();

                    // go to a new page if you've reached the last column
                    if (++count > columnsPerPage)
                    {
                        count = 0;
                        l = 0;
                        r = columnsWidth;
                        pdfDoc.NewPage();
                    }
                }
            }

            //open the final file with adobe reader for instance.
            Process.Start("Test.pdf");
        }
}


توضيحات:
تا قسمت تعريف جدول و اضافه كردن سطرها و ستون‌هاي مورد نظر، همان بحث «تكرار خودكار سرستون‌هاي يك جدول در صفحات مختلف، توسط iTextSharp» مي‌باشد.
اصل مطلب از قسمت ColumnText شروع مي‌شود. با استفاده از شيء ColumnText مي‌توان محتواي خاصي را در طي چند ستون در صفحه نمايش داد. عرض اين ستون‌ها هم توسط متد SetSimpleColumn مشخص مي‌شود و همچنين محل دقيق قرارگيري آن‌ها در صفحه. در اينجا دو حالت راست به چپ و چپ به راست در نظر گرفته شده است.
اگر حالت راست به چپ را در نظر بگيريم، محل قرارگيري اولين ستون از سمت راست صفحه (pdfDoc.Right) بايد تعيين شود. سپس هربار به اندازه‌ي عرضي كه مد نظر است بايد محل شروع ستون را مشخص كرد (pdfDoc.Right - l). هر زمانيكه ct.Go فراخواني مي‌شود، تاجايي كه ميسر باشد، اطلاعات جدول 1 در يك ستون درج مي‌شود. سپس بررسي مي‌شود كه تا اين لحظه چند ستون در صفحه نمايش داده شده است. اگر تعداد مورد نظر ما (columnsPerPage) تامين شده باشد، كار را در صفحه‌ي بعد ادامه خواهيم داد (pdfDoc.NewPage)، در غيراينصورت مجددا مكان يك ستون ديگر در همان صفحه تعيين شده و كار افزودن اطلاعات به آن آغاز خواهد شد و اين حلقه تا جايي كه تمام محتواي جدول 1 را درج كند، ادامه خواهد يافت.


۱۳۹۰/۰۵/۲۵

فرمت مناسب تصاوير جهت استفاده در iTextSharp


عموما هنگام تهيه يك مستند يا گزارش، هرچقدر حجم نهايي كمتر باشد، توزيع آن ساده‌تر خواهد بود. در اينجا اينطور به نظر مي‌رسد كه اگر مثلا از تصاويري با فرمت jpg يا png استفاده كنيم، كمترين حجم نهايي را مي‌توان بدست آورد. اما حين استفاده از iTextSharp شما با استفاده از تصاويري با فرمت BMP بهترين نتيجه را خواهيد گرفت: كمترين حجم و بهترين كيفيت! البته يك نكته‌ي ريز دارد كه بايد رعايت شود:


using (var pdfDoc = new Document(PageSize.A4))  
{  
    var pdfWriter = PdfWriter.GetInstance(pdfDoc, new FileStream("tpn.pdf", FileMode.Create));  
    pdfWriter.SetPdfVersion(new PdfName("1.5"));
    pdfWriter.CompressionLevel = PdfStream.BEST_COMPRESSION;
    //...  

}


در اينجا pdf version و همچنين compression level بايد تنظيم شوند. پس از آن فشرده سازي تصاوير BMP به صورت خودكار حين تهيه فايل نهايي انجام خواهد شد.

۱۳۹۰/۰۵/۲۴

نقدي بر كتاب «مرجع كامل entity framework 4.1»


كتاب «مرجع كامل entity framework 4.1» نوشته‌ي آقاي راد نزديك به يك ماهي است كه منتشر شده است. فرصتي پيدا شد تا اين كتاب حدودا 260 صفحه‌اي را مطالعه كنم و در ادامه توضيحاتي را پيرامون آن مطالعه خواهيد كرد.

بررسي كتاب

در عنوان كتاب ذكر شده «مرجع كامل»؛ ولي خوب، 260 نمي‌تونه مرجع كامل باشه. بنابراين كمي رعايت اعتدال در كارهاي بعدي لازم به نظر مي‌رسد. همچنين يك مورد را هم هميشه در نشر كتب تخصصي در نظر داشته باشيد: «ذكر شماره نگارش محصول» مورد نظر در عنوان كتاب، خيلي سريع كار شما را از مد افتاده خواهد كرد. خيالتان راحت باشد تا يك سال ديگر همينطور اين شماره‌ها افزايش پيدا مي‌كنند. خريداري هم كه آنچنان اطلاعاتي از كل كار نداشته باشد، بر اساس همين شماره و بدون مطالعه متن، از خريد كتاب امتناع خواهد كرد.

  • فصل اول اين كتاب به معرفي تاريخچه‌ي EF و لزوم استفاده از آن مي‌پردازد. همچنين خلاصه‌اي از قابليت‌هاي آن‌را همانند روش‌هاي model first ، database first و code first بيان مي‌كند.
  • تمركز فصل دوم بر نحوه‌ي استفاده از روش‌هاي model first و database first است به همراه نحوه‌ي توليد اسكريپت بانك اطلاعاتي در حالت model first.
  • فصل سوم كتاب به مرور جزئيات طراح EF در ويژوال استوديو جهت كار بهتر با موجوديت‌ها اختصاص دارد.
  • در فصل چهارم با روش‌هاي كوئري نويسي در EF آشنا خواهيد شد. همچنين بر روي مباحث اجراي به تعويق افتاده و مفهوم آن هم بحث شده كه بسيار ارزشمند است.
  • فصل پنجم كتاب به مباحث ثبت، حذف و به روز رساني اطلاعات توسط EF اختصاص دارد. همچنين يك سري مباحث همانند سطح اول caching در NHibernate كه در EF هم وجود دارد، بررسي شده است كه البته نام آن در اينجا Object state و entity state است.
  • در فصل ششم در مورد نحوه‌ي نگاشت رويه‌هاي ذخيره شده SQL Server به اشياء دات نتي بحث شده همچنين نحوه‌ي اجرا و استفاده از آن‌ها
  • فصل هفتم كتاب به ارتباطات بين موجود‌يت‌ها يا همان مباحث one to many و امثال آن اختصاص دارد به همراه نحوه‌ي تنظيمات آن در طراح EF در VS.NET
  • در فصل هشتم، به قالب‌هاي T4 پرداخته شده. ابتدا معرفي، سپس آشنايي با Syntax و نهايتا نحوه‌ي دستكاري و سفارشي سازي قالب‌هاي پيش فرض T4 مرتبط با EF ارائه شده‌اند.
  • فصل نهم به بررسي كاملتر مبحث model first كه در فصل دوم معرفي شده مي‌پردازد. ايجاد موجوديت‌ها، نحوه‌ي تعريف ارتباطات و نهايتا ايجاد بانك اطلاعاتي از روي آن
  • فصل دهم آن به مباحث جديد EF در مورد Code first اختصاص دارد. اين فصل واقعا ارزشمند است چون ... نتيجه‌ي تحقيق بوده نه ترجمه. تقريبا با تمام تاريخچه‌ي مرتبط با code first در EF، محل‌هاي دريافت فايل‌ها، ابزارهاي كمكي، روش‌هاي كوئري گرفتن،نحوه‌ي ايجاد بانك اطلاعاتي از روي كد، تعيين اعتبار و غيره در طي يك فصل آشنا خواهيد شد.
  • در فصل يازدهم آن مروري بر WCF Data services و پروتكل OData صورت گرفته است. نحوه‌ي ايجاد و سپس فراخواني آن توسط يك كلاينت. در عنوان كتاب ذكر شده : «مرجع»، بنابراين به دنبال يك كتاب خودآموز قدم به قدم نباشيد. اين كتاب بيشتر به «معرفي» امكانات موجود در EF در طي 260 صفحه مي‌پردازد كه الزاما با توجه به تعداد صفحات كتاب، بعضي از موارد آن مانند اين فصل آخر، از عمق لازم برخوردار نيستند ولي، حداقل سرنخ را به دست شما خواهند داد.


مزايا:
  •  به روز بودن مطالب آن
  •  آشنايي و تسلط مؤلف/مترجم به مطالبي كه تهيه كرده. اين مورد در فصل دهم آن مشهود است.
  •  زبان فارسي (بله! خيلي مهمه! هستند كساني كه چند گيگ، ببخشيد چند صد گيگ (!)، eBook به زبان انگليسي دارند ولي حتي يكي از آن‌ها را هم تمام نكرده‌اند)
  •  متن روان و سليس
  •  كيفيت خوب كتاب، صفحه بندي و امثال آن


معايب:
  •  قيمت نزديك به 8000 تومان براي كتاب 260 صفحه‌اي به نظر زياد است. البته با بالا رفتن قيمت‌ها (براي مثال 4 برابر شدن قيمت يك عدد نان لواش از سال قبل تا به امسال!)، بالاخره ... خوب اين مسايل را هم به همراه خواهد داشت.
  •  تصاوير موجود در كتاب عموما بيش از اندازه كوچك شده‌اند. اين مورد خواندن تعدادي از آن‌ها را با مشكل مواجه كرده است.
  •  در مورد متد الحاقي معروف Include در EF من مطلبي را در اين كتاب پيدا نكردم. اين مورد به بحث عدم نياز به join نويسي صريح در EF مرتبط مي‌شود.
  •  در مورد نحوه‌ي استفاده از EF با ساير بانك‌هاي اطلاعاتي بحث نشده. كتاب فقط به SQL Server منحصر است.
  •  در يكي از فصل‌ها به الگوي Repository در حد نامبردن اشاره شده. اين مورد براي خواننده‌اي كه اطلاعاتي از موضوع ندارد، كافي نيست. مي‌شد يك فصل را به آن اختصاص داد.


در كل خواندن كتاب «معرفي» EF 4.1 ، به كساني كه با Silverlight و WCF RIA Services سر و كار دارند (و كوئري‌هاي آن برايشان كمي گنگ است) و همچنين عموم علاقمنداني كه مي‌خواهند جايگزيني براي ADO.NET (در يك سطح بالاتر از آن البته) پيدا كنند توصيه مي‌شود.



در حاشيه!

شايد بپرسيد چرا اين كتاب در 260 صفحه و چرا فقط در 1000 نسخه منتشر شده است. چرا اينقدر تعداد كتاب‌هاي تخصصي كم است. چرا بيشتر تمايل به چاپ كتاب‌هاي نصب ويندوز و امثال آن است تا مثلا كتاب EF 4.1 يا خداي نكرده NHibernate ! پاسخ هم در يك جمله خلاصه مي‌شود: «نگراني ناشر از بازگشت سرمايه»
اين شما هستيد كه با پشتيباني خود مي‌توانيد اين اميدواري را به ناشرين كشور بدهيد تا «جرات كنند» بيشتر به طرف كتاب‌هاي تخصصي بروند و اين پشتيباني با صرفا گفتن چقدر عالي، دست شما درد نكنه، خيلي خوب بود، باز هم از اين كارها بكنيد،‌ معنا پيدا نمي‌كند! بايد لطف كنيد و «خريد كنيد». هيچ راه ديگري هم ندارد. الان چند عدد كتاب ASP.NET MVC 3.0 در كشور به زبان فارسي وجود دارد؟ چند عدد كتاب تخصصي SQL Server 2008 R2 را مي‌توانيد پيدا كنيد؟ در مورد كتابخانه پردازش موازي دات نت 4 چطور؟ و ...
البته منهاي نگراني اين بحث بازگشت سرمايه ، يك مورد ديگر هم سبب اين نوع تاخيرها هست. يادم مياد كتاب الگوهاي طراحي برنامه نويسي شيءگرا در سي شارپ رو كه چند سال قبل به ناشر دادم نحوه‌ي پرداخت آن به اين صورت بود: نزديك به 10 درصد پشت جلد، در طي چند قسط، آن هم 6 ماه پس از انتشار عمومي كتاب! خوب همين شد كه من ديگر به طرف اين كار نرفتم. چون واقعا نوشتن، يك «كار» كامل است. بايد وقت گذاشت (6 ماه حداقل يا بيشتر)، تحقيق كرد، رياضت كشيد و دست آخر 6 ماه پس از انتشار كتاب ... با توجه به اينكه كتاب رو كه الان شما به دست ناشر مي‌ديد شايد يكسال ديگر منتشر شود (بسته به تعداد كاري كه در دست دارد).
در هر حال، با تمام اين تفاسير، هستند كساني كه «اميدوارانه» نسبت به نوشتن كتاب‌هاي تخصصي مانند «مرجع كامل entity framework 4.1» اقدام مي‌كنند و شما هم حداقل كاري كه مي‌توانيد جهت حمايت از اين نوع حركات بكنيد، «خريد است». در غيراينصورت مدام اينطرف اونطرف ننويسيد كه چرا كتاب WPF 4.0 يا WCF 4.0 به زبان فارسي نداريم. پشتيباني نمي‌كنيد؟! خوب ... نداريم! «همين!»
يك مورد ديگر هم هست البته. عده‌اي هستند كه مثلا كلاس‌هاي ميليوني، جهت آموزش اين مباحث برگزار مي‌كنند. خوب اين‌ها هم مسلما خوشحال نخواهند شد كه مثلا كتاب WCF 4.0 و مباحث SOA مرتبط با آن به زبان فارسي منتشر شود يا حتي در اين زمينه پيش قدم شوند. اين هم هست!


۱۳۹۰/۰۵/۲۳

نمايش تصوير پس زمينه در تمام صفحات توسط iTextSharp


فرض كنيد مي‌خواهيم تصويري را در پس زمينه‌ي تمام صفحات pdf توليدي توسط iTextSharp قرار دهيم. براي اين منظور شبيه به مطلب «نمايش تعداد كل صفحات در iTextSharp» مي‌توان از رخدادهاي صفحات استفاده كرد. در متد رويداد گردان OnOpenDocument، يك قالب را به اندازه‌ي يك صفحه‌ي متني تهيه مي‌كنيم. سپس در متد OnStartPage، اين قالب را به تمام صفحات اضافه خواهيم كرد. در حقيقت فضايي را به اين شكل رزرو مي‌كنيم و در نهايت در متد OnCloseDocument ، تصوير مورد نظر را دريافت كرده، Alignment آن‌را طوري تنظيم خواهيم كرد كه زير متون صفحات قرار گيرد و به كمك متد AddImage ، آن‌را به قالب تعريف شده اضافه مي‌كنيم. به اين ترتيب، تصوير اضافه شده به صورت خودكار به تمام صفحات اضافه مي‌شود:


public class PageEvents : PdfPageEventHelper
{
        PdfTemplate _backgroundImageTemplate;

        public override void OnStartPage(PdfWriter writer, Document document)
        {
            base.OnStartPage(writer, document);
            writer.DirectContent.AddTemplate(_backgroundImageTemplate, 0, 0);
        }

        public override void OnOpenDocument(PdfWriter writer, Document document)
        {
            _backgroundImageTemplate = writer.DirectContent.CreateTemplate(document.PageSize.Width, document.PageSize.Height);
        }

        public override void OnCloseDocument(PdfWriter writer, Document document)
        {
            base.OnCloseDocument(writer, document);

            iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(
			@"C:\My Pictures\bg.png");
            img.Alignment = iTextSharp.text.Image.UNDERLYING;
            img.SetAbsolutePosition((document.PageSize.Width - img.Width) / 2, (document.PageSize.Height - img.Height) / 2);
            _backgroundImageTemplate.AddImage(img);
        }
}


۱۳۹۰/۰۵/۲۱

به روز رساني اسمبلي‌هاي داراي امضاي ديجيتال در VS.NET

زمانيكه در VS.NET يك اسمبلي داراي امضاي ديجيتال را اضافه مي‌كنيم، در فايل پروژه برنامه مدخلي شبيه به عبارت زير اضافه مي‌شود:

<Reference Include="NHibernate, Version=2.1.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">

همانطور كه ملاحظه مي‌كنيد، شماره نگارش فايل، PublicKeyToken و غيره دقيقا ذكر مي‌شوند. حال اگر همين پروژه را بخواهيد به نگارش 3.2 ارتقاء دهيد، احتمالا به روش متداول كپي اسمبلي جديد در پوشه bin برنامه اكتفاء خواهيد كرد. برنامه هم پس از يك Rebuild، به خوبي كامپايل مي‌شود و مشكلي ندارد. اما به محض اجرا و ديباگ در VS.NET، با خطاي زير مواجه خواهيد شد:

Could not load file or assembly 'NHibernate, Version=2.0.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4' 
or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. 
(Exception from HRESULT: 0x80131040)

بله! هنوز به دنبال نگارش 2 مي‌گردد و به نظر، نگارش 3.2 جديد را نديد گرفته است. مشكل هم به همان مدخل دقيق موجود در فايل پروژه برنامه، مرتبط است. اين مدخل صرفا با copy/paste فايل‌هاي جديد در پوشه bin برنامه يا rebuild پروژه، «به روز نمي‌شود» !
يا بايد دستي اين فايل csproj يا vbproj را ويرايش كنيد، يا يكبار بايد از داخل VS.NET اين ارجاعات را حذف كرده و مجددا بر اساس فايل‌هاي جديد ايجاد كنيد تا فايل پروژه برنامه بر اين اساس به روز شود.

اين مشكلي هست كه حداقل با تمام مثال‌هاي NHibernate دريافتي از اين سايت خواهيد داشت.


روش ديگر حل اين مشكل، مراجعه به خواص اسمبلي اضافه شده در ليست ارجاعات پروژه در VS.NET و خاموش كردن گزينه‌ي "Specific Version" آن است.


به صورت خلاصه حين به روز رساني اسمبلي‌هاي داراي امضاي ديجيتال:
  • يا بايد ارجاعات داراي امضاي ديجيتال را حذف و بار ديگر اضافه كنيد.
  • يا بايد فايل پروژه برنامه را با يك ويرايشگر متني ساده باز كرده و شماره نگارش‌ها را اصلاح كنيد. (ساده‌ترين روش ممكن)
  • يا خاموش كردن بررسي Specific Version را هم آزمايش كنيد.

۱۳۹۰/۰۵/۲۰

بلاگر و تگ‌هاي BR‌ اضافي!


اگر پس از ارسال كدها در بلاگر فاصله‌هاي عجيبي را بين سطرهاي رندر شده مشاهده كرديد، مشكل از قرار دادن تك BR به صورت خودكار است!





براي حل اين مشكل به آدرس زير مراجعه كنيد:
Dashboard -> Settings -> Formatting -> Convert line breaks -> change to No


يا در قسمت كنترل پنل فارسي : تنظيمات ، قسمت قالب بندي و سپس گزينه‌ي «برعكس كردن شكست خط» را خير كنيد.

كاربردهاي Static reflection - قسمت اول


در مورد static reflection مقدمه‌اي پيشتر در اين سايت قابل مطالعه است (^) و پيشنياز بحث جاري است. در ادامه قصد داريم يك سري از كاربردهاي متداول آن‌را كه اين روزها در گوشه و كنار وب يافت مي‌شود، به زبان ساده بررسي كنيم.

بهبود كدهاي موجود

از static reflection در دو حالت كلي مي‌توان استفاده كرد. يا قرار است كتابخانه‌اي را از صفر طراحي كنيم يا اينكه خير؛ كتابخانه‌اي موجود است و مي‌خواهيم كيفيت آن‌را بهبود ببخشيم. هدف اصلي هم «حذف رشته‌ها» و «استفاده از كد بجاي رشته‌ها» است.
براي مثال قطعه كد زير يك مثال متداول مرتبط با WPF و يا Silverlight است. در آن با پياده سازي اينترفيس INotifyPropertyChanged و استفاده از متد raisePropertyChanged ، به رابط كاربري برنامه اعلام خواهيم كرد كه لطفا خودت را بر اساس اطلاعات جديد تنظيم شده در قسمت set خاصيت Name ، به روز كن:
using System.ComponentModel;

namespace StaticReflection
{
    public class User : INotifyPropertyChanged
    {
        string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name == value) return;
                _name = value;
                raisePropertyChanged("Name");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        void raisePropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler == null) return;
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

تعاريف قسمت PropertyChangedEventArgs اين پياده سازي، خارج از كنترل ما است و در دات نت فريم ورك تعريف شده است. حتما هم نياز به رشته دارد؛ آن هم نام خاصيتي كه تغيير كرده است. چقدر خوب مي‌شد اگر مي‌توانستيم اين رشته را حذف كنيم تا كامپايلر بتواند صحت بكارگيري اطلاعات وارد شده را دقيقا پيش از اجراي برنامه بررسي كند. الان فقط در زمان اجرا است كه متوجه خواهيم شد، مثلا آيا به روز رساني مورد نظر صورت گرفته‌است يا خير؛ اگر نه، يعني احتمالا يك اشتباه تايپي جايي وجود دارد.
براي بهبود اين كد همانطور كه در قسمت قبل نيز گفته شد، از تركيب كلاس‌هاي Expression و Func استفاده خواهيم كرد. در اينجا Func قرار نيست چيزي را اجرا كند، بلكه از آن به عنوان قطعه‌ كدي كه اطلاعاتش قرار است استخراج شود (Lambdas as Data) استفاده مي‌شود. اين استخراج اطلاعات هم توسط كلاس Expression انجام مي‌شود. بنابراين قسمت اول بهبود كد به صورت زير شروع مي‌شود:
void raisePropertyChanged(Expression<Func<object>> expression)

الان اگر متد raisePropertyChanged بكارگرفته شده در خاصيت Name را بخواهيم اصلاح كنيم، حداقل با دو واقعه‌ي مطلوب زير مواجه خواهيم شد:
Intellisense به صورت خودكار كار مي‌كند:


حتي بدوي‌ترين ابزارهاي Refactoring موجود (منظور همان ابزار توكار VS.NET است!) هم امكان Refactoring را در اينجا فراهم خواهند ساخت:



در پايان كد تكميل شده فوق به شرح زير خواهد بود كه در آن از كلاس Expression جهت استخراج Member.Name استفاده شده است:
using System;
using System.ComponentModel;
using System.Linq.Expressions;

namespace StaticReflection
{
    public class User : INotifyPropertyChanged
    {
        string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name == value) return;
                _name = value;
                raisePropertyChanged(() => Name);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        void raisePropertyChanged(Expression<Func<object>> expression)
        {
            var memberExpression = expression.Body as MemberExpression;
            if (memberExpression == null)
                throw new InvalidOperationException("Not a member access.");

            var handler = PropertyChanged;
            if (handler == null) return;
            handler(this, new PropertyChangedEventArgs(memberExpression.Member.Name));
        }
    }
}

در اينجا باز هم نهايتا به همان PropertyChangedEventArgs استاندارد و موجود، برمي‌گرديم؛ اما آرگومان رشته‌اي آن‌را به كمك تركيب كلاس‌هاي Expression و Func تامين خواهيم كرد.

۱۳۹۰/۰۵/۱۷

نگاشت JSON به كلاس‌هاي معادل آن


يكي از مواردي كه عموما در برنامه نويسي با آن سر و كار داريم، parse اطلاعات با فرمت‌هاي مختلف است. از CSV تا XML تا ... JSON .
در مورد كار با XML در دات نت فريم ورك، فضاهاي نام مرتبط زيادي وجود دارند؛ براي مثال System.Xml.Linq و System.Xml . همچنين يك روش ديگر هم براي كار با اطلاعات XML ايي در دات نت وجود دارد. مي‌شود كلاس معادل يك فايل XML را توليد و سپس اطلاعات آن‌را به اين كلاس نگاشت كرد. اطلاعات بيشتر : (^). اين برنامه كار خود مايكروسافت است.
در مورد JSON از دات نت سه و نيم به بعد كارهايي صورت گرفته مانند : (^). اما آنچنان دلچسب نيست. جهت رفع اين خلاء كتابخانه‌ي سورس باز و بسيار كاملي در اين زمينه به نام JSON.NET تهيه شده كه از اين آدرس قابل دريافت است: (^)
و خبر خوب اينكه امكان تهيه كلاس‌هاي معادل اطلاعات JSON ايي هم مدتي‌است توسط برنامه نويس‌هاي مستقل تهيه شده است. يا مي‌توان از امكانات توكار دات نت استفاده كرد يا از كتابخانه‌‌هايي مانند JSON.NET يا از هيچكدام! مي‌توان يك راست كل اطلاعات JSON ايي دريافتي را به يك يا چند كلاس معادل آن نگاشت كرد:
  • و يا يك ابزار آنلاين مشابه: json2csharp

۱۳۹۰/۰۵/۱۵

NHibernate 3.2


نگارش نهايي NHibernate 3.2 مدتي است كه ارائه شده و به همراه آن قابليت‌هايي همانند Fluent NHibernate جهت حذف فايل‌هاي XML ايي تعريف نگاشت‌ها به كمك كد نويسي هم وجود دارد. در حال حاضر آنچنان مطالب خودآموز قابل توجهي را در اين مورد نمي‌توان يافت ولي در كل دو ويديوي مقدماتي زير مي‌توانند كمك خوبي جهت شروع به كار با اين امكان جديد باشند:



ماخذ

۱۳۹۰/۰۵/۱۳

تبديل زيرنويس‌هاي يوتيوب به فايل srt


تعدادي از ويديوهاي يوتيوب، خصوصا مواردي كه از Google videos به يوتيوب منتقل شده‌اند، داراي زير نويس هستند. اين زير نويس هم فرمت جالبي دارد:
<?xml version="1.0" encoding="UTF-8"?>
<transcript>
<text start="23.49" dur="5.97">
&amp;gt;&amp;gt;Commentator: We have here today, Steve with
us whom I met not too long ago at something
</text>
...
</transcript>
و از هر لحاظ بهتر است از فرمت srt متداول (خصوصا از لحاظ قابليت بهتر parse آن) ولي ... اين فرمت xml ايي، متداول نيست و بهتر است جهت استفاده از آن در برنامه‌هاي پخش كننده فايل‌هاي ويديويي، به srt تبديل شود.
ابتدا شايد بپرسيد كه اين‌ها را چگونه مي‌توان دريافت كرد. روش كار به صورت زير است:
http://video.google.com/timedtext?hl=en&v=Id&lang=en

در قسمت‌هاي hl و lang ، زبان مورد نظر قرار مي‌گيرد و قسمت v همان id معروف ويديوي يوتيوب مورد نظر است. براي مثال اگر لينك ويديوي ما http://www.youtube.com/watch?v=-PA-buwI3q4 باشد، لينك زيرنويس انگليسي هماهنگ با آن http://video.google.com/timedtext?hl=en&v=-PA-buwI3q4&lang=en خواهد بود.

البته زمان‌هاي اين فايل xml يك نكته‌ي خاص گوگلي دارد!
اگر عدد 3.4 را مشاهده كرديد يعني سه ثانيه و 400 ميلي ثانيه
اگر عدد 3.04 را مشاهده كرديد يعني سه ثانيه و 40 ميلي ثانيه
به عبارتي با قسمت اعشاري آن بايد به صورت رشته برخورد كنيد. اگر طول آن يك بود، دو صفر و اگر طول آن 2 بود، 1 صفر در جلوي آن بايد قرار گيرد تا قابل استفاده شود.

كلاس تهيه شده زير كار تبديل Xml گوگل را به Srt انجام مي‌دهد:
using System;
using System.Linq;
using System.Text;
using System.Web;
using System.Xml.Linq;

namespace YtSubs
{
public class GoogleXmlToSrt
{
public string ConvertXmlToSrt(string url)
{
var transcript = loadTranscript(url);

var srtBuf = new StringBuilder();
var lineNumber = 0;

foreach (var item in transcript.Root.Elements("text"))
{
lineNumber++;
srtBuf.Append(lineNumber.ToString());

var timeLine = getTimeLine(item);
srtBuf.AppendFormat("{0}{1} --> {2}{3}", Environment.NewLine, timeLine.Item1, timeLine.Item2, Environment.NewLine);

var msg = decodeText(item);
srtBuf.AppendLine(msg + Environment.NewLine);
}
return srtBuf.ToString();
}

private static XDocument loadTranscript(string url)
{
var transcript = XDocument.Load(url);

if (transcript.Root == null)
throw new InvalidOperationException("couldn't load received data.");
return transcript;
}

private static string decodeText(XElement text)
{
var textValue = text.Value.Split('\n');
var msg = textValue.Aggregate(
string.Empty, (current, line) =>
current + (HttpUtility.HtmlDecode(line.Trim()) + Environment.NewLine)
);
return msg.Trim();
}

private static int fractionalPart(string data)
{
var idx = data.IndexOf(".");
if (idx == -1) return 0;
var fractionalPart = data.Substring(idx + 1);
if (fractionalPart.Length == 1) //3.4 --> 3.400
return int.Parse(fractionalPart + "00");

if (fractionalPart.Length == 2) //3.04 --> 3.040
return int.Parse(fractionalPart + "0");

return int.Parse(fractionalPart.Substring(0, 3));
}

private Tuple<string, string> getTimeLine(XElement text)
{
var startTs = getStartTs(text);
TimeSpan endTs = getEndTs(text, startTs);
return new Tuple<string, string>(timeSpanToString(startTs), timeSpanToString(endTs));
}

private static TimeSpan getStartTs(XElement text)
{
var startData = text.Attribute("start");
if (startData == null)
throw new InvalidOperationException("This is not a valid subtitle file.");

var start = startData.Value;
var startTs = new TimeSpan(0, 0, 0, (int)Math.Truncate(double.Parse(start)), fractionalPart(start));
return startTs;
}

private static TimeSpan getEndTs(XElement text, TimeSpan startTs)
{
TimeSpan endTs;
var durData = text.Attribute("dur");
if (durData == null)
{
endTs = startTs + new TimeSpan(0, 0, 0, 3, 0);
}
else
{
var dur = durData.Value;
var durTs = new TimeSpan(0, 0, 0, (int)Math.Truncate(double.Parse(dur)), fractionalPart(dur));
endTs = startTs + durTs;
}
return endTs;
}

private static string timeSpanToString(TimeSpan lineTs)
{
return string.Format("{0}:{1}:{2},{3}",
lineTs.Hours.ToString("D2"),
lineTs.Minutes.ToString("D2"),
lineTs.Seconds.ToString("D2"),
lineTs.Milliseconds.ToString("D3"));
}
}
}
و يك نمونه از استفاده آن مي‌تواند به شكل زير باشد:
using System.IO;

namespace YtSubs
{
class Program
{
static void Main(string[] args)
{
var url = "http://video.google.com/timedtext?hl=en&v=-PA-buwI3q4&lang=en";
//var url = "subtitle.xml";
var srtBuf = new GoogleXmlToSrt().ConvertXmlToSrt(url);
File.WriteAllText("-PA-buwI3q4.srt", srtBuf);
}
}
}


مطالب تكميلي:
همه چیز درباره زیرنویس ویدئوهای گوگل

۱۳۹۰/۰۵/۱۲

تعرفه مصوب سال 1390


گاهي از اوقات كه ... چه عرض كنم، «عموما» آمار و اطلاعات ما از دره سيليكون بيشتر است از آمار و اطلاعات داخلي؛ از آمار عطسه كردن مدير اجرايي گوگل تا جيغ كشيدن رئيس مايكروسافت تا نوسانات فشار خون استيو جابز در يك ماه اخير و تا ... عوض شدن لوگوي فلان موبايل!
به همين جهت براي «تنوع» شايد بد نباشد «تعرفه نرخ پايه خدمات فني - تخصصي انفورماتيك براي سال 1390» منتشره توسط سازمان نظام صنفي رايانه‌اي كشور را هم مطالعه كنيم:




۱۳۹۰/۰۵/۱۱

اتصال SQL Server به MySQL


اگر SQL Server و MySQL بر روي سيستم شما نصب است، روشي ساده براي انتقال اطلاعات بين اين دو وجود دارد كه نيازي به دخالت هيچ نوع برنامه‌ي جانبي نداشته و با امكانات موجود قابل مديريت است.

ايجاد يك Linked server

براي اينكه SQL Server را به MySQL متصل كنيم مي‌توان بين اين دو يك Linked server تعريف كرد و سپس دسترسي به بانك‌هاي اطلاعاتي MySQL همانند يك بانك اطلاعاتي محلي SQL Server خواهد شد كه شرح آن در ادامه ذكر مي‌شود.
ابتدا نياز است تا درايور ODBC مربوط به MySQL دريافت و نصب شود. آن‌را مي‌توانيد از اينجا دريافت كنيد : (+)
سپس management studio را گشوده و در قسمت Server objects ، بر روي گزينه‌ي Linked servers كليك راست نمائيد. از منوي ظاهر شده، گزينه‌ي New linked server را انتخاب كنيد:


در ادامه، بايد تنظيمات زير را در صفحه‌ي باز شده وارد كرد:


در قسمت Linked server و Product name ، نام دلخواهي را وارد كنيد.
Provider انتخابي بايد از نوع Microsoft OLE DB Provider for ODBC Drivers باشد.
مهم‌ترين تنظيم آن، قسمت Provider string است كه بايد به صورت زير وارد شود (در غير اينصورت كار نمي‌كند):
DRIVER={MySQL ODBC 5.1 Driver}; SERVER=localhost; DATABASE=testdb; USER=root; PASSWORD=mypass; OPTION=3;PORT=3306; CharSet=UTF8;
در اينجا نام ديتابيس پيش فرض، نام كاربري اتصال به MySQL و Password و غيره را مي‌توان تنظيم كرد.
پس از انجام اين تنظيمات بر روي دكمه‌ي Ok كليك كنيد تا Linked server ساخته شود:


اگر ليست بانك‌هاي اطلاعاتي را مشاهده نموديد، يعني اتصال به درستي برقرار شده است.

تنظيمات ثانويه:

تا اينجا اس كيوال سرور به MySQL متصل شده است، اما براي استفاده بهينه از امكانات موجود نياز است تا يك سري تغييرات ديگر را هم اعمال كرد.

تنظيم MSDASQL Provider :
در همان قسمت Linked provider ، ذيل قسمت Providers ، گزينه‌ي MSDASQL را انتخاب كرده و بر روي آن كليك راست نمائيد. سپس صفحه‌ي خواص آن‌را انتخاب كنيد تا بتوان تنظيمات زير را به آن اعمال كرد. اين پروايدر جهت اتصال به MySQL مورد استفاده قرار مي‌گيرد.



فعال سازي RPC :

براي اينكه بتوان از طريق SQL Server ركوردي را در يكي از جداول بانك‌هاي اطلاعاتي MySQL متصل شده ثبت نمود، مي‌توان از دستور زير استفاده كرد:
EXECUTE('insert into testdb.testtable(f1,f1) values(1,''data'')') at mysql

اينجا testdb نام بانك اطلاعاتي اتصالي MySQL است و testTable هم نام جدول مورد نظر. MySQL ايي كه در آخر عبارت ذكر شده همان نام linked server ايي است كه پيشتر تعريف كرديم.
به محض سعي در اجراي اين كوئري خطاي زير ظاهر مي‌شود:
Server 'mysql' is not configured for RPC.

براي رفع اين مشكل، مجددا به صفحه‌ي خواص همان liked server ايجاد شده مراجعه كنيد. در قسمت Server options دو گزينه مرتبط به RPC بايد فعال شوند:



و اكنون براي كوئري گرفتن از اطلاعات ثبت شده هم از عبارت زير مي‌توان استفاده كرد:
SELECT * FROM OPENQUERY(mysql, 'SELECT * FROM testdb.testtable')

در اين كوئري، MySQL نام Linked server ثبت شده است و testdb هم يكي از بانك‌هاي اطلاعاتي MySQL مورد نظر.


انتقال تمام اطلاعات يك جدول از بانك اطلاعاتي MySQL به SQL Server

پس از برقراري اتصال، اكنون import كامل يك جدول MySQL به SQL Server به سادگي اجراي كوئري زير مي‌باشد:
SELECT * INTO MyDb.dbo.testtable FROM openquery(MYSQL, 'SELECT * FROM testdb.testtable')

در اين كوئري، MySQL همان Linked server تعريف شده است. MyDB نام بانك اطلاعاتي موجود در SQL Server جاري است و testtable هم جدولي است كه قرار است اطلاعات testdb.testtable بانك اطلاعاتي MySQL به آن وارد شود.

با اطلاعات فارسي هم (در سمت SQL Server) مشكلي ندارد. همانطور كه مشخص است، در اطلاعات provider string ذكر شده‌، مقدار charset به utf8 تنظيم شده و همچنين اگر نوع collation فيلدهاي تعريف شده در MySQL نيز به utf8_persian_ci تنظيم شده باشد، با مشكل ثبت اطلاعات فارسي به صورت ???? مواجه نخواهيد شد.


نكته:
اگر بانك اطلاعاتي MySQL شما بر روي local host نصب نيست، جهت فعال سازي دسترسي ريموت به آن، مي‌توان به يكي از نكات زير مراجعه كرد و سپس اين اطلاعات جديد بايد در همان قسمت provider string مرتبط با تعريف linked server وارد شوند:


مطالب مشابه:

هيچكدام از اين روش‌ها قابل استفاده نبودند چون provider string صحيحي را نهايتا توليد نمي‌كنند. همچنين تمام اين روش‌ها مبتني است بر ايجاد DSN در كنترل پنل كه اصلا نيازي به‌ آن نيست و اضافي است.