?????????
????????????д?????????????????С??λ?????????????????????У??????????????????磬????????????????????File???????????????DummyFile?????????????????????????????????????????????????????????class???????????/???????л???????????????????á???????????????IFile??IDatabase????????????File??Database??
??????????????????????????????????????????????????鰴????????????????????????????????????м??????????磺???????CunsomerData??????????LINQ to SQL???????????????????У?????????????????????????????
??????????????TDD?????????????????????????????д????????????д??????????磬??д????????????CustomerData??????????????????CustomerData????CustomerData???????????????InsertCustomer ??DeleteCustomer ?? GetCustomer????????????????????Щ????Customer???????????????? ?????????????????????????????????д??CustomerData????????????????????????????????д????????????????????и????????TDD?????????????????????????????С????е??????????????????磬CustomerData????????SqlConnection?????????ISqlConnection ?????е?????????CustomerData????
??????????????????

?????????????????????????鷳????????д????????????????????????????????????????????????????У????????????????????????????????????????????????????
???磺?????????Stack ?????????????????Pop??????????????????е????????????????????????????????κζ??????Pop????????????????????????Pop?????????????????????????????????????Pop ??

???BDD???????

???????????У?????????е?????????????????LINQ??SQL???????????????????????????檔???磬??????????????????????????????棬????????????????棬?????У???????????м???????????????????????????PageRepository ?????????漰Page?????????????????????????o??????CRUD????????????????????GetPageById ????????PageId???????Page????????С?

public class PageRepository : IPageRepository
{
    #region Fields
    private readonly IDropthingsDataContext _database;
    private readonly ICache _cacheResolver;
    #endregion Fields

    #region Constructors
    public PageRepository(IDropthingsDataContext database?? ICache cacheResolver)
    {
        this._database = database;
        this._cacheResolver = cacheResolver;
    }
    #endregion Constructors

    #region Methods
    public Page GetPageById(int pageId)
    {
        string cacheKey = CacheSetup.CacheKeys.PageId(pageId);
        object cachedPage = _cacheResolver.Get(cacheKey);

        if (null == cachedPage)
        {
            var page = _database.GetSingle<Page?? int>(
                   DropthingsDataContext.SubsystemEnum.Page??
                    pageId??
                    LinqQueries.CompiledQuery_GetPageById);
            page.Detach();
            _cacheResolver.Add(cacheKey?? page);
            return page;
        }
        else
        {
            return cachedPage as Page;
        }
    }
}
PageRepository???IDropthingsDataContext ???????????LINQ to SQL???????λDataContext ?????????£?LINQ??SQL????????DataContext??????????????ò??????????DataContext?????????????????????ICache?????????漰???????????????У???????????????EnterpriseLibraryCache???????ICache ??

?????????????????????PageRepository??????????棬?? GetPageById??????PageId ?????????黺?棬?????????У???????????е?????м???????????????檔


public void GetPage_Should_Return_A_Page_from_database_when_cache_is_empty_and_then_caches_it()
{
    var cache = new Mock<ICache>();
    var database = new Mock<IDropthingsDataContext>();
    IPageRepository pageRepository = new PageRepository(database.Object?? cache.Object);
    const int pageId = 1;
    var page = default(Page);
    var samplePage = new Page() { ID = pageId?? Title = "Test Page"?? ...};
    database
        .Expect<Page>(d => d.GetSingle<Page?? int>(
                              DropthingsDataContext.SubsystemEnum.Page??
                               1?? LinqQueries.CompiledQuery_GetPageById))
        .Returns(samplePage);
    "Given PageRepository and empty cache".Context(() =>
        {
            // cache is empty
            cache.Expect(c => c.Get(It.IsAny<string>())).Returns(default(object));
            // It will cache the Page object afte loading from database
            cache.Expect(c =>
                 c.Add(It.Is<string>(cacheKey =>
                       cacheKey == CacheSetup.CacheKeys.PageId(pageId))??
                      It.Is<Page>(cachePage =>
                          object.ReferenceEquals(cachePage?? samplePage))))
                .AtMostOnce().Verifiable();
        });
    "when GetPageById is called".Do(() =>
        page = pageRepository.GetPageById(1));
    "it checks in the cache first and finds nothing and then caches it".Assert(() =>
        cache.VerifyAll());
    "it loads the page from database".Assert(() =>
        database.VerifyAll());
    "it returns the page as expected".Assert(() =>
        {
            Assert.Equal<int>(pageId?? page.ID);
        }); 
}
????????????????

?????д?????????????????????????????ò????????????????????е?????????????????????????á?????????????У???????cache??database????????о?????????????????????????????磬??????????????AspectF?????????????PageRepository ??????????????????PageRepository??????????????????????????????????棬????????仺?????????黺?棬?????????????????????????????У???????????м?????????????????GetPageById??????AspectF ???????????


public Page GetPageById(int pageId)
{
    return AspectF.Define
        .Cache<Page>(_cacheResolver?? CacheSetup.CacheKeys.PageId(pageId))
        .Return<Page>(() =>
            _database.GetSingle<Page?? int>(DropthingsDataContext.SubsystemEnum.Page??
                pageId?? LinqQueries.CompiledQuery_GetPageById).Detach());
}
????????????е?????????????????

????????PageRepository??и???????????????仯?????????????????????????????и????????????????????????????????????????????????????????????????????????????????з?????????????????????????????????????齫?????

[Specification]
public void GetPage_Should_Return_A_Page_from_cache_when_it_is_already_cached()
{
    var cache = new Mock<ICache>();
    var database = new Mock<IDropthingsDataContext>();
    IPageRepository pageRepository = new PageRepository(database.Object?? cache.Object);
    const int pageId = 1;
    var page = default(Page);
    var samplePage = new Page() { ID = pageId?? Title = "Test Page"??
            ColumnCount = 3?? LayoutType = 3?? UserId = Guid.Empty?? VersionNo = 1??
            PageType = Enumerations.PageTypeEnum.PersonalPage??
            CreatedDate = DateTime.Now };
    "Given PageRepository and the requested page in cache".Context(() =>
    {
        cache.Expect(c => c.Get(CacheSetup.CacheKeys.PageId(samplePage.ID)))
            .Returns(samplePage);
    });
    "when GetPageById is called".Do(() =>
        page = pageRepository.GetPageById(1));           
    "it checks in the cache first and finds the object is in cache".Assert(() =>
    {
        cache.VerifyAll();
    });
    "it returns the page as expected".Assert(() =>
    {
        Assert.Equal<int>(pageId?? page.ID);
    });
}
????????????????????????????Context ???????趨?????????????????????????????????????samplePage???????????κα????ú???????????????????Mock???????????????????????????κ?database??????κζ??????????cache???????????????????????????????????????????????

??????????BDD

?????????ζ???????????Щ??????????????????????????????????????????????????????д????????????????????????????????κ????????????????????????????????????????б????????????????????????

??β?????????????????????????????????????????ó???????????????????????????????????????????磬??Dropthings ???????????μ?????????????????????????????С????????Щ????С?????????????塣????????anon_user@dropthings.com???????????????????С?????????????????????С?????????????????????С?????????????????????????????????????????

???????η????Default.aspx????FirstVisitHomePage?????Facade????????????????????????????楨С????????????????????????????????????FirstVisitHomePage?????ò??????????μ??????????????????????????????????????????????????????????? ??????????????????????????????????? ?????????η???? ??????????к?λ?????anon_user??????′???????洴????С??????

public class TestUserVisit
{
  public TestUserVisit()
  {
    Facade.BootStrap();
  }
  /// <summary>
  /// Ensure the first visit produces the pages and widgets defined in the template user
  /// </summary>
  [Specification]
  public void First_visit_should_create_same_pages_and_widgets_as_the_template_user()
  {
    MembershipHelper.UsingNewAnonUser((profile) =>
    {
      using (var facade = new Facade(new AppContext(string.Empty?? profile.UserName)))
      {
        UserSetup userVisitModel = null;

        // Load the anonymous user pages and widgets
        string anonUserName = facade.GetUserSettingTemplate()
             .AnonUserSettingTemplate.UserName;
        var anonPages = facade.GetPagesOfUser(facade.GetUserGuidFromUserName(anonUserName));

        "Given anonymous user who has never visited the site before"
           .Context(() => { });

        "when the user visits for the first time".Do(() =>
        {
          userVisitModel = facade.FirstVisitHomePage(profile.UserName??
             string.Empty?? true?? false);
        });

        "it creates widgets on the newly created page at exact columns and
         positions as the anon user's pages".Assert(() =>
        {
          anonPages.Each(anonPage =>
          {
            var userPage = userVisitModel.UserPages.First(page =>
                    page.Title == anonPage.Title
                    && page.OrderNo == anonPage.OrderNo
                    && page.PageType == anonPage.PageType);

            facade.GetColumnsInPage(anonPage.ID).Each(anonColumn =>
            {
              var userColumns = facade.GetColumnsInPage(userPage.ID);
              var userColumn = userColumns.First(column =>
                      column.ColumnNo == anonColumn.ColumnNo);
              var anonColumnWidgets =
                facade.GetWidgetInstancesInZoneWithWidget(anonColumn.WidgetZoneId);
              var userColumnWidgets =
                facade.GetWidgetInstancesInZoneWithWidget(userColumn.WidgetZoneId);
              // Ensure the widgets from the anonymous user template's columns are
              // in the same column and row.
              anonColumnWidgets.Each(anonWidget =>
                 Assert.True(userColumnWidgets.Where(userWidget =>
                  userWidget.Title == anonWidget.Title
                  && userWidget.Expanded == anonWidget.Expanded
                  && userWidget.State == anonWidget.State
                  && userWidget.Resized == anonWidget.Resized
                  && userWidget.Height == anonWidget.Height
                  && userWidget.OrderNo == anonWidget.OrderNo).Count() == 1));
            });
          });
        });
      }
    });
  }
???????????????????????????????????????????????????????????????????????檔?????????????????????С??????????????????????????仯???????????????????????????????λ?????????????????м??????????????????????????????????????????????????????κε???? ????xunit.console.exe?????е??????????????????????html???棺

??????????????????в?????
d:xunitxunit.console.exe
d: runksrcDropthings.Business.Facade.TestsinDebugDropthings.Business.Facade.Tests.dll
/html FacadeTest.html

?????????GUI xUnit:

???BDD???????????????????
???????????????????????д??????????????????д?????й???????????????????????????????????PageRepository ???? Insert??????????????????????в?????棬?????????????棬???????κλ??漯????????2??????檔