مطالب
پیاده سازی حذف منطقی در Entity framework
یکی از روش‌هایی که در اکثر پروژه‌های بزرگ استفاده می‌شود، بحث استفاده از حذف منطقی (soft delete) بجای حذف فیزیکی رکورد می‌باشد (اکثرا در برنامه‌هایی که با بخش مالی (پول) در ارتباط هستند) و از آنجاییکه هیچ برنامه‌ای بدون باگ نمی‌باشد، حذف منطقی بجای حذف فیزیکی پیشنهاد می‌شود. در واقع داشتن و حفظ دیتا، یک امتیاز مثبت می‌باشد؛ به علاوه استرس از دست دادن داده به صورت اتفاقی (سهل انگاری کاربر) را هم نخواهیم داشت. لازم به ذکر است کاربران نهایی استفاده کننده از نرم افزار، خبری از نوع حذف منطقی ندارند.

علاوه بر مواردی که ذکر شد، حذف منطقی می‌تواند به عنوان روشی برای حذف مطرح شود؛ به این صورت که حذف یک رکورد، در دو مرحله صورت گیرد:
- مرحله اول، حذف منطقی: کاربر اقدام به حذف رکورد مورد نظر را میکند. بعد از حذف، خبری از نمایش رکورد مربوطه نخواهد بود .
- مرحله دوم، حذف فیزیکی: مدیر اصلی می‌تواند تصمیم بگیرد که رکورد‌های حذف منطقی شده واقعا حذف شوند یا خیر. فقط مدیر اصلی و سایر افردای که دسترسی حذف مرحله دوم را داشته باشند، از روند دو مرحله‌ای حذف با خبر هستند.

 
در ادامه قصد داریم به مزایا و معایب حذف منطقی و روش‌هایی برای مدیریت آن بپردازیم.
پیشهاد میکنم سری مقالات مفید تحلیل سیستم مدیریت محتوا DTNCMS را حتما مطالعه نمایید.

برای اینکه بخواهیم حذف منطقی را پیاده سازی نماییم، نیاز داریم به هر رکورد، فیلدی اضافه شود تا از طریق آن مشخص نماییم که آیا رکورد حذف شده است یا خیر. برای این منظور باید فیلدی از نوع boolean به تمام کلاس‌ها (جداول) اضافه شود. می‌توانیم این فیلد را به صورت زیر تعریف کنیم:
  public bool IsDeleted { get; set; }
برای جلوگیری از تکرار کد فوق پیشنهاد میکنم اقدام به ایجاد یک اینترفیس به صورت زیر نمایید:
public interface ISoftDelete
{
    bool IsDeleted { get; set; }
}
و در کلاسی که قصد حذف منطقی را در آن دارید، فقط کافیست از اینترفیس فوق ارث بری نماید:
public class Post :ISoftDelete
{
}


بعد از افزودن فیلد فوق، نیاز داریم تا در تمام کوئری‌ها شرطی را اضافه نماییم تا فقط رکوردهایی را از دیتابیس واکشی کند که حذف نشده‌اند. یعنی فیلد فوق برابر False باشد. در ادامه روش‌هایی برای این هدف بیان خواهند شد.

  روش هایی برای فیلتر رکورد‌های حذف شده
1- افزودن فیلتر زیر در تمامی کوئری‌ها:
where (IsDeleted=false && ...)
در روش فوق نیاز است در تمامی کوئری هایمان شرط فوق را اضافه کنیم. همانطور که حدس زده‌‌اید، در این روش احتمال فراموش شدن شرط فوق وجود دارد و از طرفی یک کد را در همه کوئری‌ها تکرار کرده‌ایم.

2- نوشتن یک متد الحاقی‌:
برای جلوگیری از تکرار شرط فوق می‌توان یک متد الحاقی را به صورت زیر پیاده سازی نمود و در تمامی شرط‌ها، آن را فراخوانی کرد:
public static class EntityFrameworkExtentions
{
    public static ObservableCollection<TEntity> Alive<TEntity>(this DbSet<TEntity> set)
        where TEntity : class, ISoftDelete
    {
        var data = set.Where(e => e.IsDeleted == false);
        return new ObservableCollection<TEntity>(data);
    }
}

3-استفاده از کتابخانه  EntityFramework.DynamicFilte
ابتدا اقدام به نصب بسته آن نمایید:
Install-Package EntityFramework.DynamicFilters
و در کلاس Context خود فیلتر  زیر را قرار دهید :
modelBuilder.Filter("IsDeleted", (ISoftDelete d) => d.IsDeleted, false);


مشکلاتی پیرامون حذف منطقی

- کلاس User و Post را در نظر بگیرد که یک User چندین Post دارد. حال اگر حذف، فیزیکی باشد و کاربر اقدام به حذف User مورد نظر کند، با خطای زیر مواجه می‌شود:
The DELETE statement conflicted with the REFERENCE constraint ....
  اما در حذف منطقی چطور؟ در حذف منطقی چون رکوردی حذف نمی‌شود و فقط فیلد IsDeleted به روز رسانی می‌شود، خطای فوق رخ نخواهد داد و همین مورد باعث بروز مشکل می‌شود؛ چون رکوردی که دارای کلید اصلی بوده حذف شده، ولی رکورد‌های وابسته به آن هنوز داخل سیستم موجود می‌باشند. پس برای این مورد نیاز هست ابتدا تمامی Navigation property‌های رکورد مورد نظر یافت شوند و در صورتیکه مقداری وجود نداشت، رکورد مورد نظر حذف فیزیکی شود. برای این منظور دو راه حل پیشنهاد می‌شود:

1- در سرویس‌های مربوط به کلاس‌هایی که از ISoftDelet ارث بری کرده‌اند، متدی تحت عنوان CanDelete، به صورت زیر تعریف شود:
public bool CanDelete(user model)
{
return !model.posts.Any() &&  ! model.news.Any();
}
در متد Candelete، خصوصیات مورد نیاز کلاس را بررسی کرده و در صورتیکه هیچ رکوردی وجود نداشت، کاربر می‌تواند رکورد مورد نظر را حذف نماید.

2- برای جلوگیری از تکرار قطعه کد فوق، میتوان از روش زیر استفاده کرد:
- یک Attribute سفارشی را به صورت زیر تعریف نمایید:
[AttributeUsage(AttributeTargets.Property)]
public class MustBeEmptyToDeleteAttribute : Attribute { }
- به تمامی خصوصیات مورد نیاز که قصد بررسی آنها را داریم، آن‌را به صورت زیر اضافه میکنیم:
public class User
{
    public int Id { get; set; }
    public bool IsDeleted { get; set; }

    [MustBeEmptyToDelete] public virtual ICollection<Post> Posts { get; set; }
    [MustBeEmptyToDelete] public virtual ICollection<File> Files { get; set; }
    // etc...
}
و در پایان متد الحاقی زیر، برای بررسی رکورد‌های وابسته می‌باشد:
public static class EntityExtensions
{
    public static bool CanDelete(this object entity)
    {
        return entity.GetType().GetProperties()
            .Where(x => x.IsDefined(typeof(MustBeEmptyToDeleteAttribute)))
            .Select(x => x.GetValue(entity))
            .OfType<IEnumerable<object>>()
            .All(x => !x.Any());
    }

همانطور که بیان شد، در حذف منطقی فقط رکورد مورد نظر به روز رسانی می‌شود. برای این منظور می‌توان دو متد را همانند زیر در نظر گرفت و هر کدام که مورد نیاز بود، فراخوانی شود:
 public void MarkAsSoftDeleted<TEntity>(TEntity entity) where TEntity : ISoftDelete
        {
            Entry(entity).State = EntityState.Modified;
           // set IsDelete=true 
          // here you can set other logs like who deleted ,when ,...
      }

 public void MarkAsDeleted<TEntity>(TEntity entity) where TEntity : class
    {
            Entry(entity).State = EntityState.Deleted;
    }

پیشنهادها
- اگر از حذف منطقی استفاده میکنید، امکانی را در سیستم قرار دهید تا در صورت تمایل رکورد‌های حذف منطقی را بتوان حذف کرد (تهیه backup و حذف)، حذف منطقی در دراز مدت حجم دیتابیس را بالا می‌برد.
- تا حد امکان به کاربران استفاده کننده، وجود امکان حذف منطقی را اطلاع ندهید. اطلاع از این امر شاید باعث عدم دقت افراد استفاده کننده شود.
مطالب
الگوی سردر Facade Pattern
یکی از الگوهای ساختاری Gang Of Four، استفاده از الگوی Facade است که پیچیدگی‌های یک سیستم را مخفی می‌سازد و با ارائه یک پیاده سازی ساده‌تر، استفاده از آن و تست آن را راحت‌تر می‌سازد. این الگو یک کلاس یا یک سیستمی را با متدها و رویدادهایی ساده، در اختیار ما قرار می‌دهد و در یک لحظه، تنها با یک کلاس واحد سر و کله می‌زنیم. احتمالا بسیاری از شما از این الگو استفاده کرده‌اید، ولی شاید با اسم آن آشنا نبوده‌اید.

کار این کلاس در واقع ترکیب کلاس‌ها و کتابخانه‌های کاری مشخص است که نیاز به ارتباط با یکدیگر را دارند. به عنوان مثال یک برنامه کتابخانه، برای وظیفه‌ای چون امانت یک کتاب نیاز است تا چندین کلاس مختلف را با یکدیگر به کار بگیرد که این وظایف شامل موارد زیر می‌باشند:
  1. بررسی وجود کتاب
  2. بررسی تعداد موجود یک کتاب در کتابخانه
  3. بررسی وضعیت امانی کتاب (آیا کتاب در دست کسی از قبل امانت است؟ یا کتاب برای امانت آزاد است؟)
  4. در صورتی که کتابی بیش از زمان مورد نظر در دست کسی امانت است، با یک پیامک از او بخواهیم که کتاب را بازگرداند.
تمامی موارد بالا تنها قسمتی از انجام یک عمل ساده هستند که در یک گروه جای می‌گیرند؛ ولی در واقع از چندین کلاس جدا مثل کلاس کتاب، امانت، سیستم پیامکی و ... استفاده شده است . الگوی Facade به ما کمک می‌کند تا پیچیدگی و تعداد خطوط اجرا را در سطوح بالاتر مخفی سازیم و تنها با صدا زدن یک یا چند متد ساده، کار را به اتمام برسانیم. این کار باعث کاهش کد و خوانایی برنامه در سطوح بالاتر می‌شود.

در کد زیر ما قصد داریم نمونه‌ای از اجرای این الگو را ببینیم. ابتدا سه کلاس اطلاعاتی زیر را ایجاد می‌کنیم:
کلاس کتاب Book:
    class Book
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public int Quantity { get; set; }
        public bool IsLoanable { get; set; }
    }

کلاس کاربر User
 class User
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public  string CellPhoneNumber { get; set; }
    }

کلاس امانت Loan
  class Loan
    {
       public DateTime ExpiredDate { get; set; }
        public User User { get; set; }
    }

بعد از آن سه کلاس را برای مدیریت کتاب، مدیریت امانت و مدیریت پیامکی، ایجاد می‌کنیم:
کلاس کتاب BookManager:
 class BookManager
    {
        public Book GetBook(int id)
        {
                return new Book()
                {
                    Id=id,
                    Title = "a book",
                    Quantity = 3,
                    IsLoanable = true
                };
        }
    }
کلاس بالا یک کتاب را با موجودی سه عدد بازمی‌گرداند که خاصیت IsLoanable آن می‌گوید کتاب را برای بردن به خانه امانت می‌دهند؛ ولی اگر کتاب به فرض یک کتاب مرجع باشد باید False برگرداند تا از امانت آن خودداری شود.

کلاس LoanManager برای مدیریت امانت‌ها
 public int IsLoan(int bookId)
        {
            return 2;
        }

        public List<Loan> GetLoans(int bookId)
        {
            return new List<Loan>()
            {
                new Loan()
                {
                    ExpiredDate = DateTime.Now.AddDays(-15),
                    User = new User()
                {
                    Id = 2,
                    Title = "User1",
                    CellPhoneNumber = "342342424"
                }
                },
                new Loan()
                {
                    ExpiredDate = DateTime.Now.AddDays(5),
                    User = new User()
                        {
                            Id = 56,
                            Title = "User56",
                            CellPhoneNumber = "324324324324"
                        }
                }
            };
        }
این کلاس شامل دو متد است که اولین متد آن کد کتاب را دریافت می‌کند و تعداد افرادی را که در حال حاضر نسخه‌های مختلف آن را به امانت برده‌اند، برمی‌گرداند. در کد بالا عدد دو بازگردانده می‌شود که میگوید از نسخه‌های موجود این کتاب در کتابخانه، دوتای آن‌ها به امانت برده شده‌اند. در متد دوم، کد کتاب داده شده و امانت‌های فعلی آن کتاب که همان دو عدد بالا می‌باشد را برگشت می‌دهد.

در نهایت سومی کلاس مدیریتی برای پیامک هاست:
   class SmsManager
    {
        public void SendMessage(string number)
        {
            Console.WriteLine("please take back the book to the library : "+number);
        }
    }

و در کلاس Facade داریم
    class FacadeBookLoan
    {
        private readonly BookManager _bookManager;
        private readonly LoanManager _loanManager;
        private readonly SmsManager _smsManager;
        public FacadeBookLoan()
        {
            _bookManager = new BookManager();
            _loanManager=new LoanManager();
            _smsManager=new SmsManager();
        }

        public int IsLoanable(int bookId)
        {
            var book = _bookManager.GetBook(2);
            if (book == null)
                return -2;
            if (!book.IsLoanable)
                return -1;
            var howManyBookIsLoaned = _loanManager.IsLoan(bookId);

            if(howManyBookIsLoaned>0) ManageLoaners(bookId);

            return book.Quantity - howManyBookIsLoaned;
        }

        private void ManageLoaners(int bookId)
        {
            var loans = _loanManager.GetLoans(bookId);

            foreach (var loan in loans)
            {
                if (loan.ExpiredDate > DateTime.Now)
                {
                    _smsManager.SendMessage(loan.User.CellPhoneNumber);
                }
            }
        }
    }
در این کلاس متد IsLoanable چک می‌کند که آیا کتاب قابل امانت هست یا خیر. در اینجا مرحله به مرحله، وجود کتاب و قابلیت امانت کتاب بررسی شده و در صورت نتیجه، کد وضعیت -1 یا -2 بازگردانده می‌شوند و در مرحله بعد تعداد نسخه‌های آن کتاب که در حال حاضر در دست امانت هستند، بررسی می‌شوند. اگر کتابی در درست امانت باشد، متد خصوصی صدا زده شده و به کاربرانی که بیش از مدت معینی یک نسخه از کتاب را داشته‌اند، پیامک می‌زند که کتاب را بازگردانید؛ چرا که نسخه‌ها در حال کاهش هستند و در مرحله بعد تعداد نسخه‌های موجود از آن کتاب را در کتابخانه باز می‌گرداند که در این مثال تنها یک نسخه از آن کتاب در کتابخانه موجود است و دو تای آن‌ها در دست امانت هستند که یکی از امانت دارها 15 روز است کتاب را در تاریخ معینی تحویل نداده است.

کد بدنه اصلی برنامه:
var myfacade=new FacadeBookLoan();
var loansCount= myfacade.IsLoanable(2);
Console.WriteLine(loansCount > 0 ? "you can loan the book" : "you can't loan the book");
خروجی برنامه:
please take back the book to the library : 324324324324
you can loan the book
مطالب
الگوی وضعیت State Pattern
الگوی وضعیت، یکی از الگوهای رفتاری Gang Of Four است و بسیار شبیه به الگوی Strategy  می‌باشد؛ ولی با کپسوله سازی بیشتر. در الگوی استراتژی تغییر وضعیت از بیرون کلاس اعمال می‌د ولی در الگوی وضعیت، بر اساس تغییر وضعیت درونی خودش صورت می‌گیرد.
یکی از استفاده‌های این الگو برای مثال در پلیرهاست که وضعیت پخش را چون Play,Pause و ... در خود دارند. در اینجا هم از این مثال استفاده می‌کنیم:
ابتدا یک اینترفیس برای وضعیت خود بسازید که آرگومان ورودی متد آن را در مرحله بعد تعریف میکنیم:
public interface IState
{
    void PressPlay(MP3PlayerContext context);
}
سپس نوبت ایجاد کلاس اصلی یا همان دستگاه پخش که به آن Context می‌گوییم می‌رسد تا تغییر وضعیت الگو را به آن بسپاریم:
public class MP3PlayerContext
{
    public MP3PlayerContext()
    {
        this.CurrentState = new StandbyState();
    }
    
    public MP3PlayerContext(IState state)
    {
       this.CurrentState = state; 
    }
    
    public IState CurrentState { get; set; }
    
    public void Play()
    {
        this.CurrentState.PressPlay(this);
    }
}
سپس کلاس‌های مختلف خود را بر اساس اینترفیس بالا می‌سازیم:
public class StandbyState : IState
{
    public void PressPlay(MP3PlayerContext context)
    {
        context.CurrentState = new PlayingState();
    }
}

public class PlayingState : IState
{
    public void PressPlay(MP3PlayerContext context)
    {
        context.CurrentState = new StandbyState();
    }
}
در کدهای بالا، کلاس‌های Playing و StandBy در واقع شبیه سازی از عمل کلید پخش هستند که با هر بار فشردن آن، پخش به طور موقت توقف کرده و یا پخش خود را از سر می‌گیرد. کلاس Context نیز باید در ابتدا به طور پیش فرض با یکی از این مقادیر پر شود و برای دکمه پخش مشخص است که کلاس PlayingState می‌باشد.
بدین ترتیب در اولین اجرای متد Play در کلاس Context، کلاس PlayingState اجرا می‌شود و وضعیت، به StandbyState تغییر می‌کند و هر بار که مجددا متد Play اجرا گردد، تعویض بین این دو کلاس صورت می‌گیرد.
مطالب
UrlRewriter توسط Intelligencia.UrlRewriter
Intelligencia.UrlRewriter یک DLL رایگان برای انجام بازنویسی URL‌ها در ASP.Net می‌باشد. به این صورت که بر اساس تنظیماتی که در فایل Web.Config انجام می‌شود، عمل تغییر URL را انجام میدهد و با زبان فارسی نیز سازگار است؛ مثل استفاده از عنوان فارسی در URL برای  SEO بهتر:

برای استفاده، ابتدا DLL مورد نظر را دانلود و به References‌ها اضافه می‌کنیم؛ دانلود فایل   Intelligencia.UrlRewriter.dll 

دانلود آخرین نسخه‌ی فایل از سایت مرجع:  urlrewriter.net

مرحله‌ی بعد تنظیمات Web.Config است که به صورت زیر می‌باشد:

<configuration>
  <configSections>
    <section name="rewriter" requirePermission="false" type="Intelligencia.UrlRewriter.Configuration.RewriterConfigurationSectionHandler, Intelligencia.UrlRewriter" />
  </configSections>
  <system.web>
    <httpModules>
      <add type="Intelligencia.UrlRewriter.RewriterHttpModule,Intelligencia.UrlRewriter" name="UrlRewriter" />
    </httpModules>
  </system.web>
  <rewriter>
    <rewrite url="^.*-T([0-9]+)/?$*" to="~/ShowTerol.aspx?ID=$1" processing="stop" />
  </rewriter>
</configuration>

مرحله بعد تنظیم این است که کدام لینک‌ها بازنویسی شوند که در کد‌های بالا فقط یک مدل بازنویسی وجود دارد:

<rewrite url="^.*-T([0-9]+)/?$*" to="~/ShowTerol.aspx?ID=$1" processing="stop" />

و می‌گوید تمام لینک‌هایی را که در آخر آنها T- و عدد وجود دارد، به صفحه‌ی ShowTerol.aspx با یک کوئری استرینگ به نام ID که مقدار آن، همان عدد بعد حرف T میباشد بفرست.

یعنی این لینک را  

به این لینک تبدیل میکند                               

ShowTerol.aspx?ID=87
مثال‌های بیشتر
<rewrite url="^.*-p([0-9]+)/?$*" to="~/Products.aspx?p=$1" processing="stop"/>
<rewrite url="^.*-c([0-9]+)pa([0-9]+)/?$*" to="~/Default.aspx?c=$1&amp;pa=$2" processing="stop"/>
<rewrite url="^.*-c([0-9]+)/?$*" to="~/Default.aspx?c=$1" processing="stop"/>

الگوی تبدیلات توسط Regular Expansion

مطالب
آموزش Coded UI Test #1
اکثر برنامه نویسان با مباحث Unit Testing  آشنایی دارند و بعضی برنامه نویسان هم، از این مباحث در پروژه‌های خود استفاده می‌کنند. ساختار الگو‌های MVC و MVVM به گونه ای است که به راحتی می‌توان برای این گونه پروژه‌ها Unit Test بنویسیم. در پروژه‌های MVC به دلیل عدم وابستگی بین View و Controller به طور مستقیم، امکان نوشتن Unit Test برای Controller امکان پذیر است و از طرفی در الگوی MVVM به دلیل منطق وجود ViewModel می‌توان برای اینگونه پروژه‌ها نیز Unit Test نوشت. اما ساختار سایر پروژه‌ها به گونه ای است که نوشتن Unit Test برای آن‌ها مشکل و در بعضی مواقع غیر ممکن می‌شود. برای مثال در پروژهای Desktop نظیر Windows Application و حتی وب به صورت Asp.Net Web Forms به دلیل وابستگی مستقیم کنترل‌های UI به منطق اجرای برنامه، طراحی و نوشتن Unit Test بسیار مشکل و در برخی موارد بیهوده است. در VS.Net ابزاری وجود دارد به نام Coded UI Test که برای تست این گونه پروژه‌ها طراحی شده است و همان طور که از نامش پیداست صرفا برای تست کنترل‌های UI و رویداد‌های کنترل‌ها و تست درستی برنامه با توجه به داده‌های ورودی به کار می‌رود. یکی از مزیت‌های اصلی آن تسریع عملیات تست در حجم بالا است و زمان ایجاد unit  test را به حداقل می‌رساند. مزیت دوم آن امکان ایجاد unit test برای پروژه‌های که در مراحل پایانی تولید هستند ولی هنوز اطمینانی به عملکرد صحیح برنامه در حالات مختلف نیست.
 در این پست قصد دارم روش استفاده از این گونه پروژه‌های تست را با ذکر یک مثال بررسی کنیم و در پست‌های بعدی به بررسی امکانات دیگر خواهیم پرداخت.

نکته : فقط در Vs.Net با نسخه‌های Ultimate و Premium می‌تونید از Code UI Test استفاده کنید که البته به دلیل اینکه در ایران پیدا کردن نسخه‌های دیگر Vs.Net به غیر از Ultimate سخت‌تر است به طور قطع این محدودیت برای برنامه نویسان ما وجود نخواهد داشت. برای اینکه از نسخه Vs.Net خود اطمینان حاصل کنید از منوی Help گزینه About Microsoft Visual Studio رو انتخاب کنید. پنجره ای به شکل زیر مشاهده خواهید کرد که در آن مشخصات کامل Vs.Net ذکر شده است.

در این مرحله قصد داریم برای فرم زیر Unit Test طراحی کنیم. پروژه به صورت زیر است:

کاملا واضح است که در این فرم دو عدد به عنوان ورودی دریافت می‌شود و بعد از کلیک بر روی CalculateSum نتیجه در textbox سوم نمایش داده می‌شود. برای تست عملکرد صحیح فرم بالا ایتدا به Solution مورد نظر از منوی test Project یک Coded UI Test Project اضافه می‌کنیم. به دلیل اینکه این قبلا در این Solution پروژه تست از نوع Coded UI Test نبود بلافاصله یک پنجره نمایش داده می‌شود. مطمئن شوید گزینه اول انتخاب شده و بعد بر روی Ok کلیک کنید.(گزینه اول به معنی است که قصد داریم عملیات مورد نظر بر روی UI را رکورد کنیم و گزینه دوم به معنی است که قصد داریم از عملیات رکورد شده قبلی استفاده کنیم).  یک کلاس به نام CodeUITest1 به همراه یک متد تست به نام CodedUITestMethod1 ساخته می‌شود. اولین چیزی که جلب توجه می‌کند این است که این کلاس به جای TestClassAttribute دارای نشان CodeUITestAttrbiute است. در گوشه سمت راست Vs.Net خود یک پنجره کوچک به نام UI Map Test Builder مانند شکل زیر خواهید دید.

دکمه قرمز رنگ به نام Record Button است و عملیات تست را رکورد خواهد کرد. دکمه دایره ای به رنگ مشکی برای تعیین Assertion به کار می‌رود. و در نهایت گزینه آخر کد‌های مورد نظر مراحل قبل را به صورت خودکار تولید خواهد کرد.

#روش کار

روش کار به این صورت است که ابتدا شما مراحل تست خود را شبیه سازی خواهید کرد و بعد از آن Test Builder مراحل تست شما را به صورت کامل به صورت کد‌های قابل فهم تولید خواهد کرد. (دقیقا شبیه به ایجاد UnitTest به روش Arrange/Act/Assert است با این تفاوت که این مراحل توسط UI Map رکورد شده و نیازی به کد نویسی ندارد). در پایان باید یک Data Driven Coded UI Test طراحی کنید تا بتوانید از این مراحل رکورد استفاده نمایید. 

#چگونگی شبیه سازی :

پروژه را اجرا نمایید. زمانی که فرم مورد نظر  ظاهر شد بر روی گزینه Record در TestBuilder کلیک کنید. عملیات ذخیره سازی شروع شده است. در نتیجه به فرم مربوطه رفته و در Textbox اول مقدار 10 و در textbox دوم مقدار 5 را وارد نمایید. با کلیک بر روی دکمه CalculateSum مقدار 15 نمایش داده خواهد شد. از برنامه خارج شوید و بعد بر روی گزینه Generate Code در TestBuilder کلیک کنید با از کلید‌های ترکیبی Alt + G استفاده نمایید.(اگر در این مرحله، از برنامه خارج نشده باشید با خطا مواجه خواهید شد.) در پنجره نمایش داده شده یک نام به متد اختصاص دهید. عملیات تولید کد شروع خواهد شد. بعد کدی مشابه زیر را در متد مربوطه مشاهده خواهید کرد.

   [TestMethod]
        public void CodedUITestMethod1()
        {            
            this.UIMap.CalculateSum();
            this.UIMap.txtSecondValueMustBe10();
        }
بخشی از سورس کد تولید شده برای متد CalculateSum به شکل زیر است:
        public void CodedUITestMethod1 ()
        {
            #region Variable Declarations
            WinEdit uITxtFirstNumberEdit = this.UIدوعددصحیحواردنماییدWindow.UITxtFirstNumberWindow.UITxtFirstNumberEdit;
            WinEdit uITxtSecondNumberEdit = this.UIدوعددصحیحواردنماییدWindow.UITxtSecondNumberWindow.UITxtSecondNumberEdit;
            WinButton uICalculateSumButton = this.UIدوعددصحیحواردنماییدWindow.UICalculateSumWindow.UICalculateSumButton;
            #endregion

            // Type '10' in 'txtFirstNumber' text box
            uITxtFirstNumberEdit.Text = this.CalculateSumParams.UITxtFirstNumberEditText;

            // Type '{Tab}' in 'txtFirstNumber' text box
            Keyboard.SendKeys(uITxtFirstNumberEdit, this.CalculateSumParams.UITxtFirstNumberEditSendKeys, ModifierKeys.None);

            // Type '10' in 'txtSecondNumber' text box
            uITxtSecondNumberEdit.Text = this.CalculateSumParams.UITxtSecondNumberEditText;

            // Click 'Calculate Sum' button
            Mouse.Click(uICalculateSumButton, new Point(83, 12));

            // Type '10' in 'txtFirstNumber' text box
            uITxtFirstNumberEdit.Text = this.CalculateSumParams.UITxtFirstNumberEditText1;

            // Type '{Tab}' in 'txtFirstNumber' text box
            Keyboard.SendKeys(uITxtFirstNumberEdit, this.CalculateSumParams.UITxtFirstNumberEditSendKeys1, ModifierKeys.None);

            // Type '10' in 'txtSecondNumber' text box
            uITxtSecondNumberEdit.Text = this.CalculateSumParams.UITxtSecondNumberEditText1;

            // Type '{Tab}' in 'txtSecondNumber' text box
            Keyboard.SendKeys(uITxtSecondNumberEdit, this.CalculateSumParams.UITxtSecondNumberEditSendKeys, ModifierKeys.None);

            // Click 'Calculate Sum' button
            Mouse.Click(uICalculateSumButton, new Point(49, 11));

            // Type '10' in 'txtFirstNumber' text box
            uITxtFirstNumberEdit.Text = this.CalculateSumParams.UITxtFirstNumberEditText2;

            // Type '{Tab}' in 'txtFirstNumber' text box
            Keyboard.SendKeys(uITxtFirstNumberEdit, this.CalculateSumParams.UITxtFirstNumberEditSendKeys2, ModifierKeys.None);

            // Type '5' in 'txtSecondNumber' text box
            uITxtSecondNumberEdit.Text = this.CalculateSumParams.UITxtSecondNumberEditText2;

            // Type '{Tab}' in 'txtSecondNumber' text box
            Keyboard.SendKeys(uITxtSecondNumberEdit, this.CalculateSumParams.UITxtSecondNumberEditSendKeys1, ModifierKeys.None);

            // Click 'Calculate Sum' button
            Mouse.Click(uICalculateSumButton, new Point(74, 16));
        }
همان طور که می‌بینید تمام مراحل تست شما رکورد شده است و به صورت کد قابل فهم بالا ایجاد شده است.
چگونگی ایجاد Assertion
اگر به کد متد تست CodedUITestMethod1 در بالا دقت کنید یک متد به صورت this.UIMap.txtSecondValueMustBe10 فراخوانی شده است. این در واقع یک Assertion  است که در هنگام عملیات رکورد ایجاد کردم و به این معنی است که مقدار TextBox دوم حتما باید 10 باشد. حال روش تولید Assertion‌ها را بررسی خواهیم کرد.
بعد از شروع شدن مرحله رکورد اگر قصد دارید برای یک کنترل خاص Assert بنویسید، دکمه assertion (به رنگ مشکی و به صورت دایره است) را بر روی کنترل مورد نظر drag&drop کنید. یک border آبی برای کنترل مورد نظر ایجاد خواهد شد:

به محض اتمام عملیات drag&drop منوی زیر ظاهر خواهد شد:

از گزینه Add Assertion استفاده کنید و برای کنترل مورد نظر یک assert بنویسید. در شکل زیر یک assert برای textbox دوم نوشتم به صورتی که مقدار آن باید با 5 برابر باشد.


از گزینه آخر برای نمایش پیغام مورد نظر خودتون در هنگامی که aseert با شکست مواجه می‌شود استفاده کنید.
کد تولید شده زیر برای عملیات assert بالا است:
  public void txtSecondValueMustBe10()
        {
            #region Variable Declarations
            WinEdit uITxtSecondNumberEdit = this.UIدوعددصحیحواردنماییدWindow.UITxtSecondNumberWindow.UITxtSecondNumberEdit;
            #endregion

            // Verify that the 'ControlType' property of 'txtSecondNumber' text box equals '10'
            Assert.AreEqual(this.txtSecondValueMustBe10ExpectedValues.UITxtSecondNumberEditControlType, uITxtSecondNumberEdit.ControlType.ToString());
        }

مرحله اول انجام شد. برای تست این مراحل باید یک Data DrivenTest بسازید که در پست بعدی به صورت کامل شرح داده خواهد شد.
مطالب
قسمت اول - ساخت گزارش در محیط Telerik Reporting
یکی از ضروریات نرم افزارها وجود گزارشات مختلف در قالب لیست‌ها ، نمودارها و ... در آنها می‌باشد.یک نرم افزار خوب باید توانایی ارائه گزارشات خوب و زیبا را نیز داشته باشد.گزارشات در حقیقت نمایشی از داده‌ها هستند که عموما به چاپ می‌رسند. مورد دیگری که در خصوص گزارشات حائز اهمیت می‌باشد ، تبدیل آنها به فرمت‌های مختلف جهت حمل و جابه جایی آسان از سیستمی به سیستم دیگر می‌باشد. برای مثال تبدیل گزارشات به قالب Pdf مزایایی بسیاری از نظر قابل حمل بودن در پی خواهد داشت. در برنامه نویسی دات نت گزارشات را می‌توان توسط دستورات چاپ در دات تهیه ، نمایش و چاپ نمود. اما تهیه گزارش توسط دستورات دات نت کاری مشکل و طاقت فرسا می‌باشد. همچنین امکان تبدیل این گزارشات به فرمت‌های دیگر نظیر Pdf ، به راحتی انجام نمی‌شود و باید از کلاس‌ها و ابزارهای جانبی که برای این کار تهیه شده اند استفاده نمود . از این رو ابزارهای مختلفی در جهت تهیه گزارشات به وجود آمدند.ابزارهایی نظیر :

• Crystal Report
• Stimul Report
• Telerik Reporting
• … 

در ادامه این سری آموزش‌ها قصد داریم Telerik Reporting و نحوه تهیه گزارش با آن را مورد بررسی قرار دهیم. این ابزار امکانات بسیاری در خصوص تهیه گزارش برنامه‌های دات نتی نظیر Windows Form ، Asp.net و ... در اختیار ما قرار می‌دهد.در ادامه و برای شروع ، ساخت یک گزارش ساده در این محیط را بررسی میکنیم.

 
 نکته : گزارشاتی که توسط Telerik Reporting تهیه می‌شوند به وسیله کدهای C# جنریت می‌شوند.بنابراین همیشه توصیه می‌شود گزارشات خود را درون یک یا چند پروژه Class Library قرار دهیم و از این پس ، این گزارشات از درون پروژه‌های دیگر (ویندوزی ، وب و ...) در دسترس هستند.کافی ست پروژه Class Library را به عنوان Reference به پروژه مورد نظر خود اضافه کنیم..  برای شروع می‌توان یک پروژه جدید  از نوع Class Library  ایجاد کرد.پس از آن روی نام پروژه راست کلیک کنید و گزینه Telerik Report را انتخاب نمایید.پس از تعیین نام گزارش کلید Ok را کلیک نمایید.

انتخاب گزینه Telerik Reporting از پنجره New Item


در این حالت فایل گزارش به پروژه افزوده می‌شود. در ادامه می‌توانید توسط ویزاردی که نمایش داده می‌شود کارهای عمومی مربوط به پیاده سازی گزارش (انتخاب منبع داده(Data Source) ، ساخت Query جهت بارگذاری اطلاعات ، فیلدهایی که باید نمایش داده شوند ، گروه بندی داده‌ها و ...) را توسط این ویزارد انجام دهید. برای اینکار در پنجره ای که نمایش داده می‌شود بر روی کلید Next کلیک نمایید.
جهت ایجاد یک گزارش جدید در پنجره  Report Choose Page گزینه New Report را انتخاب نموده و کلید Next را کلیک نمایید.
جهت انتخاب منبع داده گزارش در پنجره Choose Data Source گزینه Add New Data Source را انتخاب نمایید.در این حالت می‌توانید گزینه‌های متفاوتی را به عنوان منبع داده گزارش خود انتخاب نمایید. گزینه‌های نمایش داده شده به شرح ذیل است:
• Sql Data Source : جهت اتصال مستقیم به بانک اطلاعات Microsoft Sql Server
• Object Data Source :  جهت اتصال به کلاس‌های لایه Business و بارگذاری داده از این کلاس ها
• Entity Data Source : جهت اتصال به  Entity Framework
• Open Access Data Source : جهت اتصال به Open Access ORM ساخت شرکت Telerik
• Cube Data Source : جهت اتصال و نمایش داده‌های تحلیل شده
  در ادامه برای اینکه بتوان مستقیما به Sql Server وصل شد و Query‌های مربوط به گزارش را روی آن اجرا نمود؛ می‌توان گزینه Sql Data Source را انتخاب نمود و بر روی کلید Ok کلیک کرد.سپس در پنجره Choose Your Data Connection گزینه New Connection را کلیک کنید و یک اتصال به بانک مورد نظر خود ایجاد کنید.پس ایجاد و تست Connection ساخته شده روی Next  کلیک کنید.در پنجره Save the connection string می‌توان نامی را جهت Connection string انتخاب کرد تا Connection string با همان نام در فایل Config پروژه ذخیره شود.در ادامه کلید Next را کلیک کرده و وارد مرحله بعد شوید. در پنجره Configure Data Source Command گزینه Query Builder را جهت ساخت Query مورد نظر برای بارگذاری داده‌ها انتخاب نمایید.

انتخاب جداول جهت ساخت Query


پنجره ساخت Query

پس از ساخت Query مورد نظر کلید Ok را کلیک نمایید. در پنجره Configure Data Source Command کوئری ساخته شده به شما نمایش داده می‌شود.کلید Next را کلیک کنید. 

سپس وارد مرحله Preview Data Source Result می‌شوید که در آن قادر خواهید بود پیش نمایشی از داده هایی که بعدا توسط Query ساخته شده بارگذاری خواهند شد را مشاهده نمایید. Next را کلیک نموده تا وارد مرحله بعد شوید.مرحله بعد Standard Report Type می‌باشد که در این مرحله شما می‌توانید نوع گزارش خود را انتخاب نمایید و کلید Next را فشار دهید.در بخش Design Data Layout چند فیلد را از بخش سمت چپ (Available Fields) انتخاب نموده و کلید Details را کلیک نمایید.فیلدهای انتخاب شده به بخش Details گزارش اضافه خواهند شد.در ادامه Next را کلیک کنید تا وارد بخش Choose Report Layout شوید.شما می‌توانید در این بخش یک حالت نمایشی را برای گزارش خود انتخاب نمایید و Next را کلیک نمایید.در بخش Choose Report Style یک قالب بندی جهت گزارش خود انتخاب نمایید.در ادامه Next و سپس Finish را کلیک نمایید.کدهای گزارش Generate شده می‌توان در قسمت Designer گزارش را مشاهده نمود. 

در این حالت کارهای زیر توسط Wizard به صورت اتوماتیک انجام خواهد شد:
• بایند شدن اتوماتیک فیلدهای گزارش به ستوان‌های مرتبط
• اعمال قالب بندی انتخاب شده برای صفحه و سر ستونها
• افزودن تاریخ و شماره صفحه به پایین گزارش
در ادامه پروژه را Rebuild کرده و گزینه Preview را در Designer جهت نمایش ، پیش نمایش گزارش کلیک نمایید.

نکته : در هر برنامه‌ی گزارش سازی بخش Designer گزارش به 4 بخش کلی تقسیم می‌شود:
•  Report Header: مواردی که در این بخش از گزارش قرار میگیرند در بالای صفحه اول گزارش نمایش داده می‌شوند.
•  Page Header: مواردی که در این بخش از گزارش قرار میگیرند در بالای همه صفحات گزارش قرار گرفته و تکرار می‌شوند.
•  Details: داده‌های اصلی گزارش که شامل جزئیات و بخش اصلی گزارش می‌باشند و سطر به سطر نیز تکرار می‌شوند در این بخش قرار می‌گیرند.
•  Page Footer: مواردی که در این بخش از گزارش قرار میگیرند در پایین همه صفحات نمایش داده می‌شوند.
•  Report Footer:مواردی که در این بخش قرار می‌گیرند در پایین صفحه آخر گزارش نمایش داده می‌شوند.  

ادامه دارد ...
مطالب
شرح یک مشکل امنیتی با فایرفاکس
حدود دو ماه قبل دوبار از طریق میل‌باکس یاهو من به تمام contactهای تعریف شده در آن ایمیلی با محتوای زیر ارسال شده بود:

Hello,
you should definitely check this thing out http://www.newsl5.net/biz/?page=xyz

این ایمیل‌ها هم جعلی نبودند. یعنی واقعا از اکانت یاهوی من ارسال شده بودند و در قسمت sent وجود خارجی داشتند! فقط IP ارسال کننده آن (115.78.224.246) متعلق به کشور ویتنام بود (IP ارسال کننده را در هدر ایمیل ارسالی می‌توان مشاهده کرد).
این مساله باعث شد که من سیستم را چندین بار چک کنم؛ از لحاظ بحث ویروس تا اسپای‌ور و غیره. «هیچ» مشکلی مشاهده نشد.
مرحله بعد کمی در مورد یاهو جستجو کردم و مشخص شد که یاهو با session hijacking به شدت مشکل دارد. همچنین ابزار دیگری که می‌تواند به این session hijacking کمک کند خود «فایرفاکس» است. فایرفاکس حاوی گزینه‌ای است که سشن‌های قبلی شما را ذخیره می‌کند. زمانیکه مرورگر را بسته و پس از مدتی آن‌را باز می‌کنیم، یک راست و قشنگ همان سشن قبلی مثلا یاهو را بازیابی کرده و کار ادامه پیدا می‌کند.
کمی گشتم و این قابلیت رو به کل غیرفعال کردم. برای غیرفعال کردن آن «Disable Session Restore in Firefox» را در گوگل جستجو کنید.
و خلاصه آن به صورت زیر است:
در نوار آدرس فایرفاکس تایپ کنید about:config
در ادامه موارد زیر را یافته و غیرفعال کنید:
browser.sessionstore.resume_from_crash;false
browser.sessionstore.resume_session_once;false
browser.sessionstore.restore_pinned_tabs_on_demand;false
browser.sessionstore.restore_hidden_tabs;false
services.sync.prefs.sync.browser.sessionstore.restore_on_demand;false

راه ساده‌تر:
افزونه session manager را نصب کنید
در قسمت session manager options در برگه startup & shutdown آن کلا بحث ذخیره سازی سشن در حین بسته شدن مرورگر را غیرفعال کنید.


و به صورت خلاصه: تنظیمات پیش فرض فایرفاکس از لحاظ امنیتی مناسب نیستند.
ضمن اینکه ایمیل فوق رو من هفته‌ای یکی دو بار از تمام افرادی که می‌شناسم دریافت می‌کنم! به عبارتی خیلی‌ها گرفتار این مساله شده‌اند.
ذخیره سازی سشن‌ها به نظر کارها رو ساده می‌کنه. مرورگر رو باز می‌کنی همه چیز مثل قبل از بسته شدن آن است و ... همین یعنی مشکل امنیتی. خصوصا مراجعه به سایت‌ها و لینک‌هایی که از باگ‌های XSS سوء استفاده می‌کنند.
 
بازخوردهای پروژه‌ها
اعمال نشدن فونت معرفی شده به بخش هدر
جناب نصیری من با توجه به روش معرفی شده در این مخزن کد هدر‌های خودم رو تعریف کردم .
در ابتدا فونت‌های مورد نظر رو معرفی کردم :
pdf.DefaultFonts(fonts =>
            {
                fonts.Path(System.IO.Path.Combine(AppPath.ApplicationPath, "fonts\\irsans.ttf"),
                    System.IO.Path.Combine(Environment.GetEnvironmentVariable("SystemRoot"), "fonts\\verdana.ttf"));
                fonts.Size(10);
            });

و در مرحله بعد در زمان معرفی هدر فونت رو نیز به هدر اعلام کردم :
 rptheader.PageHeaderProperties(new XHeaderBasicProperties
                    {
                        RunDirection = PdfRunDirection.RightToLeft,
                        ShowBorder = true,
                        PdfFont = header.PdfFont

                    });

اما فونت‌ها با وجود معرفی شدن در سیستم به جدول تعریف شده با ساختار زیر اعمال نمی‌شود :
<div style='text-align: center'>{message}</div>
<table style='font-size:10pt;font-family:tahoma;'>
<tr>
<td style='width:85%'>{year}</td>
    <td style='font-weight: bold;width:15%'> سال مالی :</td>
</tr>
<tr>
<td>{client}</td>
    <td style='font-weight: bold;width:15%' >شرکت :</td>
</tr>
<tr>
<td>{type}</td>
    <td style='font-weight: bold;width:15%' >نوع:</td>
</tr>
                                              
</table>

با تغییر فونت از Tahoma به irsans نوشته‌های بصورت نامفهوم نمایش داده می‌شوند. لطفا راهنمائی بفرمائید.تنها در div  اول کد‌های html فونت اعمال می‌شود.
پروژه‌ها
Mvc File Manager
به نظر من بهترین روش برای یادگیری برنامه نویسی انجام  یک پروژه واقعی و کاربردی است . (هرچند ساده)  به همین دلیل در حین یادگیری asp.net mvc  تصمیم گرفتم یک فایل منجیر درست کنم که با پیشرفت در یادگیری asp.net mvc اون رو تکمیل کنیم.

فاز اول : نسخه 0.1.1       نسخه 0.1.2
پیاده سازی کنترلر های Browse,Download,Upload, CreateFolder,Delete
هدف از این مرحله یادگیری کنترلر ، ویو ، ویو مدل ، مدل بایندینگ ، روتینگ ، اکشن ریزالت و....
فاز دوم : (انجام شد) نسخه 0.2.3
پیاده سازی کنترلر های Rename,Properties 
استفاده از  WebGrid  در مرورگر فایل
محاسبه حجم پوشه
نسخه اصلاحی آقای وکیلی:دریافت
تغییر در نوع چینش models
افزودن PlUploader برای آپلود فایل
تغییر در Partial به نام _breadCrumb
امکان چند انتخاب هم زمان - و همچنین حذف  چند مورد هم زمان
باز شدن پوشه‌ها و دانلود شدن فایل‌ها با دابل کلیک 
تغییرات مختصر در style
تغییر در ساختار لینک برگشت -> انتقال از model به view و پیاده سازی با jquery به دلیل سهولت بیشتر

فاز سوم:
پیاده سازی اعتبار سنجی کاربران و نقش‌های آنان 
تعریف نقش‌های زیر (ایده خام)
Admin (Full access)
FileManager_Read(readonly access)
FileManager_Write(Creat Folder & upload file)
FileManager_Change(Move & Rename)
FileManager_Delete(Delete file and Folder
فاز چهارم :
پیاده سازی مراحل قبل تحت Ajax
فاز پنجم:
بهینه سازی و تکمیل پروژه
ایده‌های پراکنده :
قابلیت کپی 
قابلیت انتخاب چندتایی برای کپی و حذف و ...
 قابلیت آپلود همزمان چند فایل
چند زبانه بودن 
توسعه اینترفیس (درختواره پوشه‌ها ، ویوهای مختلف جهت نمایش فایل (لیست، آیکون‌های کوچک ، آیکون‌های بزرگ))
نمایش و تغییر دسترسی‌های ویندوز
آپلود فایل فشرده و اکسترکت کردن آن 
جستجوی فایل
.
.
.
مطالب
HTML5 Web Component - قسمت دوم - Custom Elements
Custom Elements، دارای یک چرخه حیات می‌باشند. در طی این چرخه حیات، می‌توان تعدادی متد خاص را به المان سفارشی خود اضافه کرد که به صورت خودکار توسط مرورگر فراخوانی می‌شوند. به این متدها Life-cycle Callbacks یا Custom Element Reactions نیز می‌گویند. برای درک بهتر چرخه حیات مذکور، به تکه کد زیر توجه نمائید:
customElements.define("x-component", class extends HTMLElement {
    constructor() {
        super();
        console.log('constructed!');
    }

    connectedCallback() {
        console.log('connected!');
    }

    disconnectedCallback() {
        console.log('disconnected!');
    }

    adoptedCallback() {
        console.log('adopted!');
    }

    attributeChangedCallback(name, oldValue, newValue) {
        console.log('attirbuteChanged!', name, oldValue, newValue);
    }

    static get observedAttributes() {
        return ['checked','demo','label'];
    }
});

Element Upgrades
به صورت پیش‌فرض، المان‌های موجود در DOM که مبتنی‌بر استانداردهای HTML تعریف نشده‌ا‌ند، توسط مرورگر به عنوان HTMLUnknownElement تجزیه و تحلیل خواهند شد. ولی این موضوع برای المان‌های سفارشی که نام معتبری دارند (وجود «-»)، صدق نمی‌کند. برای مثال دو خط کد زیر را در کنسول مربوط به Developer tools مرورگر خود اجرا کنید:
// "tabs" is not a valid custom element name
document.createElement('tabs') instanceof HTMLUnknownElement === true //true

// "x-tabs" is a valid custom element name
document.createElement('x-tabs') instanceof HTMLElement === true //true
در این صورت، امکان استفاده از المان سفارشی، قبل از معرفی و ثبت آن توسط متد customElements.define نیز وجود خواهد داشت. یعنی اگر در DOM شما تعدادی المان سفارشی وجود داشته باشند که به هر دلیل نیاز است پس از گذشت یک بازه زمانی کوتاهی معرفی و ثبت شوند (مثال: lazy load اسکریپت‌های متناظر با المان‌های سفارشی در Angular)، این المان‌ها معتبر هستند. فرآیند فراخوانی متد define و استفاده از کلاس معرفی شده برای ارتقاء المان سفارشی موجود در DOM، اصطلاحا Element Upgrades نامیده می‌شود. همچنین با استفاده از متد customElements.whenDefined که یک Promise را بازگشت می‌دهد، می‌توان از معرفی و ثبت شدن المان خاصی آگاه شد:
customElements.whenDefined('x-component').then(() => {
  console.log('x-component defined');
});
یا حتی امکان استفاده از سلکتور «‎:defined‏» نیز به شکل زیر وجود دارد:
<share-buttons>
  <social-button type="twitter"><a href="...">Twitter</a></social-button>
  <social-button type="fb"><a href="...">Facebook</a></social-button>
  <social-button type="plus"><a href="...">G+</a></social-button>
</share-buttons>

// Fetch all the children of <share-buttons> that are not defined yet.
let undefinedButtons = buttons.querySelectorAll(':not(:defined)');

let promises = [...undefinedButtons].map(socialButton => {
  return customElements.whenDefined(socialButton.localName);
));

// Wait for all the social-buttons to be upgraded.
Promise.all(promises).then(() => {
  // All social-button children are ready.
});
در اینجا ابتدا تمام المان‌های تعریف نشده، کوئری شده‌اند و با استفاده از متد map و اجرای متد whenDefined، به لیستی از Promiseها رسیده‌ایم و در نهایت با استفاده از Promise.all منتظر اتمام مرحله upgrade المان‌های مذکور هستیم.
سازنده مرتبط با کلاس المان سفارشی x-component، در هنگام وهله‌سازی یا فرآیند upgrades فراخوانی می‌شود و می‌تواند برای مقداردهی اولیه خواص وهله جاری، تنظیم رخدادگردان‌ها (Event Listeners) و یا ایجاد و اتصال ShadowDOM با استفاده از متد attachShadow، محل مناسبی باشد. طبق مستندات مرتبط، فراخوانی ()super بدون ارسال هیچ آرگومانی باید در اولین خط این سازنده انجام شود.
برای وهله‌سازی المان سفارشی نیز می‌توان از متد customeElements.get به شکل زیر استفاده کرد:
customeElements.define('x-component',...)
let XComponent = customElements.get('x-component');
document.body.appendChild(new XComponent())
متد get، ارجاعی به سازنده کلاس x-component را بازگشت خواهد داد.

connectedCallback 
 اولین متد بعد از فراخوانی سازنده، connectedCallback نام دارد و زمانی رخ می‌دهد که وهله‌ای از یک المان سفارشی به Light DOM افزوده شده‌است و یا توسط Parser مرورگر، در DOM شناسایی شود. این متد، محل پیشنهاد شده برای اجرای کدهای زمان راه‌اندازی مانند دریافت منابع از سرور و یا رندر کردن محتوایی خاص، می‌باشد. 
نکته: این متد ممکن است بیش از یکبار نیز فراخوانی شود! هنگامیکه یک المان موجود در DOM از طریق کد از DOM جداشده و سپس اضافه شود:
const el = document.createElement('x-component');
document.body.appendChild(el);
// connectedCallback() called

el.remove();
// disconnectedCallback()

document.body.appendChild(el);
// connectedCallback() called again


disconnectedCallback
این متد نیز هر وقت المانی از DOM حذف شود، اجرا خواهد شد و مانند متد connectedCallback ممکن است چندین بار فراخوانی شود. همچنین محل مناسبی برای آزادسازی منابع استفاده شده در پیاده‌سازی المان سفارشی، می‌باشد. مانند: استفاده از متد clearInterval برای پاکسازی یک تایمر که با متد setInterval ایجاد شده‌است.  
نکته: هیچ تعهدی به اجرای متد disconnectedCallback در تمام حالاتی که یک المان از DOM حذف می‌شود، وجود ندارد. به عنوان مثال هنگامیکه یک برگه‌ی مرورگر بسته شود، این متد فراخوانی نخواهد شد.

‌attributeChangedCallback
 این متد هنگامی فراخوانی خواهد شد که خصوصیات مشخص شده از طریق observedAttributes به عنوان یک static getter، اضافه، حذف، ویرایش و یا جایگزین شوند. همچنین در مرحله Upgrades برای مقادیر اولیه خصوصیات که توسط استفاده کننده از المان سفارشی، مشخص شده‌است نیز فراخوانی می‌شود.
adoptedCallback
متدهای قبلی بیشترین استفاده را دارند؛ این متد خاص نیز زمانیکه یک المان سفارشی به یک DOM دیگری منتقل می‌شود، اجرا خواهد شد. برای مثال:
const iframe = document.querySelector('iframe');
const iframeImages = iframe.contentDocument.querySelectorAll('img');
const newParent = document.getElementById('images');

iframeImages.forEach(function(imgEl) {
  newParent.appendChild(document.adoptNode(imgEl));
});
در تکه کد بالا، لیست المان‌های img موجود در داخل iframe کوئری شده و سپس با پیمایش بر روی لیست بدست آمده و در زمان فراخوانی متد adoptNode که کار تغییر ownerDocument مرتبط با یک المان را انجام می‌دهد، متد adoptedCallback ما نیز اجرا خواهد شد.

Methods

اگر المان‌های بومی و استاندارد موجود را بررسی کنید، همه آنها دارای یکسری متد، پراپرتی و صفات مشخصی هستند. در اینجا نیز تعریف رفتاری برای یک المان سفارشی و کپسوله، نکته خاصی ندارد و به شکل زیر قابل تعریف و استفاده می‌باشد:
class XComponent extends HTMLElement {
  constructor() {
    super();
  }
  
  doSomething(){
    console.log('doSomething');
  }
}

let component = document.querySelector('x-component');
component.doSomething();
مشخص است که امکان تعریف انواع و اقسام متدها با پارامترها و خروجی‌های مختلفی و حتی نسخه‌های همزمان یا ناهمزمان آن‌ها وجود دارد.

Attributes & Properties

در HTML خیلی رایج است که مقادیر پراپرتی‌های یک المان در قالب یکسری صفات، نمودی در DOM هم داشته باشند. برای مثال:
div.id = 'id-value';
div.hidden = true;
انتظار چنین خروجی داریم:
<div id="id-value" hidden>
این موضوع، تحت عنوان «Reflecting Properties to Attributes» مطرح می‌باشد. در این صورت علاوه بر اینکه با یک نگاه به DOM، از مقادیر خصوصیات یک المان آگاه خواهیم بود، امکان استفاده از این صفات به عنوان سلکتورهایی در زمان استایل‌دهی نیز وجود دارد. حال از این مکانیزم برای اعمال یکسری صفات دسترسی‌پذیری مانند صفات ARIA به المان سفارشی خود نیز می‌توان استفاده کرد. برای مثال:
class XComponent extends HTMLElement {
    constructor() {
        super();
    }

    connectedCallback() {
        this._render();
    }

    get disabled() {
        return this.hasAttribute('disabled');
    }

    set disabled(val) {
        // Reflect the value of `disabled` as an attribute.
        if (val) {
            this.setAttribute('disabled', '');
        } else {
            this.removeAttribute('disabled');
        }

        this._render();
    }

    _render() {
        //... 
    }
}
ایده کار خیلی ساده است؛ پراپرتی‌های یک المان‌سفارشی را از طریق متدهای getter و setter تعریف کرده و در بدنه پیاده‌سازی آنها، صفات HTML ای المان جاری را تغییر داده و یا از مقادیر آنها استفاده کنیم.
اینبار با مقداردهی پراپرتی disabled برروی وهله‌ای از المان سفارشی ما، این مقادیر نمودی در DOM هم خواهند داشت. با استفاده از متدهای setAttribute یا removeAttribute کار همگام‌سازی پراپرتی‌ها با صفات را انجام داده‌ایم.
همچین با استفاده از متد attributeChangedCallback نیز می‌توان برای اعمال صفات ARIA که اشاره شد، به شکل زیر استفاده کرد:
attributeChangedCallback(name, oldValue, newValue) {
  switch (name) {
    case 'checked':
      // Note the attributeChangedCallback is only handling the *side effects*
      // of setting the attribute.
      this.setAttribute('aria-disabled', !!newValue);
      break;
    ...
  }
یا حتی به شکل زیر:
attributeChangedCallback(name, oldValue, newValue) {
    // When the component is disabled, update keyboard/screen reader behavior.
    if (this.disabled) {
      this.setAttribute('tabindex', '-1');
      this.setAttribute('aria-disabled', 'true');
    } else {
      this.setAttribute('tabindex', '0');
      this.setAttribute('aria-disabled', 'false');
    }
    // TODO: also react to the other attribute changing.
  }
در اینجا از همان getter که طبق پیاده‌سازی ما، در پشت صحنه از همان مقدار صفت disabled استفاده می‌کند، برای تنظیم یکسری صفات دیگر استفاده کرده‌ایم. به عنوان مثال اگر المان ما غیرفعال شده بود، صفت tabindex آن را با «‎-1‏» مقداردهی می‌کنیم تا از توالی پیمایش مبتنی‌بر Tab خارج شود.
نکته: پیشنهاد می‌شود از مکانیزم همگام‌سازی پراپرتی‌ها و صفات، برای انواع داده‌ای اولیه (رشته، عدد و ...) استفاده شود و برای دریافت مقادیری مانند objects و یا arrays، از متدها یا پراپرتی‌ها استفاده کنید. در غیر این صورت نیاز خواهد بود که این مقادیر را سریالایز و در داخل المان سفارشی، عملیات معکوس آن را انجام دهید که می‌تواند هزینه‌ی زیادی داشته باشد. عملیات سریالایز نیز خود باعث از دست دادن ارجاعات به آن مقادیر خواهد شد. به صورت کلی هیچکدام از المان‌های بومی موجود، چنین اطلاعاتی را دریافت نمی‌کند. برای مثال:
constructor() {
    super();

    this._data = [];
}

get data() {
    return _data;
}

set data(value) {
    if (this_data === value) return;
    this._data = value;
    this._render();
}

Lazy Properties

همانطور که اشاره شد حتی قبل از مرحله upgrades مربوط به المان‌های سفارشی استفاده شده در سند HTML برنامه شما، به عنوان المان‌های معتبری هستند که امکان کوئری کردن و مقداردهی اولیه خصوصیات آنها از طریق کد نیز ممکن است. این موضوع زمانیکه از فریم‌ورکی مثل Angular استفاده می‌شود، المان موردنظر به صورت خودکار توسط فریم‌ورک لود و به صفحه اضافه شده و در انتهای عملیات، binding پراپرتی‌های آن به خصوصیات موجود در کامپوننت Angular ای انجام خواهد شد. به مثال زیر توجه کنید:
<x-component [disabled]="model.disabled"></x-component>
در این صورت اگر لود اسکریپت، معرفی و ثبت این المان سفارشی به صورت Lazy انجام شود، امکان آن وجود دارد که فریم‌ورک، عملیات binding را قبل از مرحله upgrades انجام دهد. خوب... این موضوع چه مشکلی را ایجاد می‌کند؟ در این صورت چون مرحله upgrades تمام نشده است، پیاده‌سازی بدنه متد setter متناظر با خصوصیات المان سفارشی، توسط پراپرتی جدیدی که توسط فریم‌ورک برروی وهله موجود تعریف می‌شود، بی‌استفاده خواهد ماند. برای مثال، اگر سعی کنیم قبل از مرحله upgrades خصوصیت disabled المان x-component را مقداردهی کنیم، عملیات مکانیزم همگام‌سازی مدنظر ما اجرا نخواهد شد:
let el = document.querySelector('x-component');
el.disabled = true;

customElements.define("x-component", class extends HTMLElement {
    constructor() {
        super();
    }

    get disabled() {
        return this.hasAttribute('disabled');
    }

    set disabled(val) {
        // Reflect the value of `disabled` as an attribute.
        if (val) {
            this.setAttribute('disabled', '');
        } else {
            this.removeAttribute('disabled');
        }

        this._render();
    }
});

با این خروجی مواجه خواهیم شد که هیچ اثری از صفت disabled دیده نمی‌شود:


یکی از روش‌های پیشنهاد شده برای حل این مشکل، مقداردهی مجدد پراپرتی‌ها بعد از مرحله upgrades و پس از اینکه متد setter تعریف شده‌است، می‌باشد:
let el = document.querySelector('x-component');
el.disabled = true;

customElements.define("x-component", class extends HTMLElement {
    constructor() {
        super();
    }

    connectedCallback() {
        this._upgradeProp('disabled');
    }

    get disabled() {
        return this.hasAttribute('disabled');
    }

    set disabled(val) {
        // Reflect the value of `disabled` as an attribute.
        if (val) {
            this.setAttribute('disabled', '');
        } else {
            this.removeAttribute('disabled');
        }

        this._render();
    }

    _upgradeProp(prop) {
        if (this.hasOwnProperty(prop)) {
            let value = this[prop];
            delete this[prop]; //delete instance property
            this[prop] = value; // set prototype property
        }
    }
});
ایده کار به این صورت است که مقدار پراپرتی مورد نظر را که قبل از مرحله upgrades برروی وهله جاری (instance property) تنظیم شده‌است، در متغییری نگهداری کرده و آن پراپرتی را حذف و سپس پراپرتی تعریف شده در کلاس (prototype property) را برای وهله جاری مقداردهی کنیم.
نکته: به یاد داشته باشید که قبل از اینکه یکسری صفات خاص را مقدار دهی کنید، بررسی شود که استفاده کننده از المان سفارشی، مقداری را تنظیم نکرده باشد. برای مثال اگر قصد دارید المان سفارشی شما قابلیت focus را داشته باشد، نیاز است شما حداقل tabindex=-1 را تنظیم کنید؛ حتی اگر استفاده کننده، آن را مقداردهی نکرده باشد:
connectedCallback() {
  if (!this.hasAttribute('role'))
    this.setAttribute('role', 'checkbox');
  if (!this.hasAttribute('tabindex'))
    this.setAttribute('tabindex', -1); //element is not reachable via sequential keyboard navigation, but could be focused
}