0

SubSonic Framework: Tạo 1 Blog Engine (Phần 2)

(Phần 1: ...)

Thêm loại hỗ trợ

Có bài viết blog là hữu ích, nhưng việc tổ chức các bài viết thành các loại làm cho nó dễ dàng hơn cho người đọc theo chủ đề quan tâm, vì vậy tôi sẽ hỗ trợ thêm thể loại cho blog.

Thêm một bảng mới để các cơ sở dữ liệu tổ chức các loại gọi là blog_categories (xem Hình 6). Như trước đây, CategoryID là một trường ID tự tăng. Hoặc là thêm một vài loại bằng cách sử dụng cơ sở dữ liệu, hoặc tạo một trang scaffolding để quản lý danh sách các loại và thêm một vài cách sử dụng nó.

Hình 6: Bảng Blog_Categories

alt

Tại thời điểm này, không có mối quan hệ giữa các bài viết và các loại bảng. Khi mỗi bài có thể xuất hiện trong nhiều loại và mỗi loại sẽ tổ chức nhiều bài viết, các mối quan hệ tốt nhất giữa các bảng là một mối quan hệ nhiều-nhiều. Để mô hình này trong cơ sở dữ liệu, các phương pháp tiêu chuẩn là thêm một bảng liên kết. Thêm một bảng mới (blog_post_category_map) cho mối quan hệ này (xem hình 7). Hai cột liên quan đến hai bảng quan trọng đối với blog_posts và blog_categories và kết hợp để tạo thành khóa chính cho bảng.

Hình 7: bảng Blog_Post_Category_Map

alt

Ngoài ra, bạn cũng nên tạo ra các mối quan hệ giữa ba bảng. Nhấp vào nút mối quan hệ trên thanh công cụ Table Designer. Thêm hai khóa ngoại giữa hai bảng chính và blog_post_categories (xem hình 8).

Hình 8: Bổ sung thêm một khóa ngoại

alt

Scaffold control được xây dựng để hỗ trợ cho việc chỉnh sửa mối quan hệ nhiều-nhiều. Thêm blog_post_category_map vào thuộc tính ManyToManyTable. Chạy Scaffold và chỉnh sửa một bài hiện có (không thêm một mục mới, trong một thời điểm). Bạn sẽ thấy một danh sách hộp kiểm của các item có sẵn (xem hình 9). Kiểm tra tắt các item thích hợp và lưu các mục nhập. Bạn sẽ thấy các mục được thêm vào xuất hiện trong bảng mapping (xem Hình 10).

Hình 9: Việc chỉnh sửa một mục hiện có sử dụng Scaffolding

alt

Hình 10: Dữ liệu trong mapping table

alt

Nếu bạn cố gắng để thêm một kỷ lục mới, bạn sẽ không thấy category checkboxes. Tại sao? Xem xét các bước mà các scaffolding phải thực hiện để lưu các bản ghi. Nó phải thêm một mục mới vào blog_posts và blog_post_category_map. Trong khi thêm dữ liệu vào hai bảng là dễ dàng, postID phải available để lưu vào các bảng mapping. Thật không may, subsonic không có giá trị này (vì nó là trong quá trình tạo ra bản ghi mới), vì vậy nó không thể hiển thị danh sách các mục mà bạn có sẵn để thêm vào bài đăng. Làm thế nào bạn có thể khắc phục điều này? Có hai cách, giả sử bạn không chỉ cần chỉnh sửa mã subsonic để hỗ trợ thêm cho việc này. Việc đầu tiên là không sử dụng auto-incrementing. Thay vào đó, bạn có thể sử dụng một số giá trị khác được thêm vào bởi người sử dụng như một chìa khóa, có nghĩa là nó sẽ có sẵn để lưu bản ghi. Thứ hai - và theo quan điểm của tôi tốt hơn - giải pháp là không sử dụng các thuộc tính ManyToMany trực tiếp với các scafold khi bạn sử dụng một chìa khóa tự tăng. Thay vào đó, bạn có thể sử dụng các ManyToMany control sau khi thêm các bài đăng blog mới để lựa chọn các loại.

Thay thế Scaffoling

Để làm điều này, tôi sẽ thay thế Scaffoling với mã số, và bao gồm khả năng để lựa chọn các loại khi thêm bài viết mới. Bây giờ bạn có thể đơn giản đi và tạo trang riêng của bạn. Tuy nhiên, tôi lười, và tôi không muốn có subsonic tạo trang edit.

Tin vui là command-line tool chấp nhận một tham số để tạo ra các trang soạn thảo. Tin xấu là như các phiên bản 2.0.3 của subsonic, nó không hoạt động. Mã thực hiện là AWOL (Absent WithOut Leave). Tin tốt cho những người lười như tôi là các mã vẫn hoạt động trong các trang web SubSonicCentral mà đi kèm với SubSonic (đó là trong thư mục src). Chỉnh sửa connectionstrings.config và web.config cho rằng trang Web bao gồm các con trỏ đến MyBlog - cơ sở dữ liệu, bắt đầu các trang Web SubSonicCentral, và chuyển sang các trang Generators. Phần scaffold được sinh ra (xem Hình 11) là trên nửa dưới của trang. Chọn blog_posts và tạo. Bây giờ bạn cần phải có một trang PostEditor.aspx được include trong project của bạn.

Hình 11: Generating the new edit page alt

Các code được sinh ra bởi SubSonicCentral là lớn hơn một chút, vì vậy bạn có thể sẽ phải thay đổi một vài cái tên (nó tạo ra những class như blogpost, ví dụ). Một khi bạn clean up, tuy nhiên, bạn nên có một trang handy mà hoạt động giống như Scaffoling, nhưng có thể được sửa đổi một cách dễ dàng hơn (xem Hình 12).

Hình 12: Tạo trang soạn thảo alt

Kéo một ManyToMany control vào phần biên tập (ở dưới GridView) của PostEditor.aspx và thiết lập các thuộc tính như trong danh sách dưới đây. Những đặc tính thiết lập ánh xạ giữa ba bảng có liên quan.

<sub:ManyManyList ID="PostCategories" runat="server"
                  MapTableName="blog_post_category_map"
                  ForeignTableName="blog_categories" ForeignTextField="Name"
                  PrimaryTableName="blog_posts" />

Ngoài ra, sửa đổi hàm BindAndSave trong các mã sau cho trang soạn thảo để thiết lập thuộc tính PrimaryKeyValue của ManyToMany control và lưu bản ghi (xem VD 8).

Ví dụ 8: BindAndSave

void BindAndSave(string id) {

    Post item;
    if (!String.IsNullOrEmpty(id) && id != "0") {
        //it's an edit
        item = new Post(id);
    } else {
        //add
        item = new Post();
    }

    object valctrlTitle =
        Utility.GetDefaultControlValue(Post.Schema.GetColumn("Title"),
        ctrlTitle, isAdd, false);

    item.Title = Convert.ToString(valctrlTitle);

    object valctrlPostedOn =
        Utility.GetDefaultControlValue(Post.Schema.GetColumn("PostedOn"),
        ctrlPostedOn, isAdd, false);

    item.PostedOn = Convert.ToDateTime(valctrlPostedOn);

    object valctrlBody =
        Utility.GetDefaultControlValue(Post.Schema.GetColumn("Body"),
        ctrlBody, isAdd, false);

    item.Body = Convert.ToString(valctrlBody);

    //bind it
    item.Save(User.Identity.Name);

    //now save the ManyToMany control's data
    this.PostCategories.PrimaryKeyValue = item.PostID.ToString();
    this.PostCategories.Save();
}
Private Sub BindAndSave(ByVal id As String)

    Dim item As Post
    If Not String.IsNullOrEmpty(id) AndAlso id <> "0" Then
        'it's an edit
        item = New Post(id)
    Else
        'add
        item = New Post()
    End If

    Dim valctrlTitle As Object = _
      Utility.GetDefaultControlValue(Post.Schema.GetColumn("Title"), _
      ctrlTitle, isAdd, False)

    item.Title = Convert.ToString(valctrlTitle)

    Dim valctrlPostedOn As Object = _
      Utility.GetDefaultControlValue(Post.Schema.GetColumn("PostedOn"), _
      ctrlPostedOn, isAdd, False)

    item.PostedOn = Convert.ToDateTime(valctrlPostedOn)

    Dim valctrlBody As Object = _
      Utility.GetDefaultControlValue(Post.Schema.GetColumn("Body"), _
      ctrlBody, isAdd, False)

    item.Body = Convert.ToString(valctrlBody)

    'bind it
    item.Save(User.Identity.Name)
    'now save the ManyToMany control's data
    Me.PostCategories.PrimaryKeyValue = item.PostID.ToString()
    Me.PostCategories.Save()
End Sub

Bây giờ bạn sẽ có thể thêm và chỉnh sửa các bài viết, với các thể loại được lựa chọn. Các bản ghi mapping sẽ xuất hiện trong blog_post_category_map như là bạn tạo các bài mới.

Truy vấn khác

Các độc giả của blog có thể không muốn xem tất cả các bản ghi, hoặc họ có thể muốn xem các bản ghi cũ, vì vậy tôi sẽ bổ sung thêm khả năng truy vấn các bài viết theo thể loại hoặc ngày. Thay vì tạo ra một trang mới, tôi sẽ sử dụng default.aspx, và thêm hỗ trợ cho các URL truy vấn. Các định dạng URL cho các truy vấn danh mục sẽ dạng Default.aspx? Category = cái gì đó, mà tên của danh mục là một chuỗi URL được mã hóa. Ngày truy vấn sẽ bao gồm năm, tháng, ngày (hoặc chỉ có năm, hoặc năm, tháng) của các truy vấn, ví dụ:? Default.aspx năm = #, default.aspx năm = # & tháng? = #, Hoặc mặc định aspx? năm = # & tháng = # & ngày = #. Các trang sẽ xác định xem nó là một truy vấn thể loại, hoặc một truy vấn ngày tháng, hoặc chỉ đơn giản là trả lại bài viết gần đây nhất. Sau đó, nó sẽ lấy các bài viết thích hợp và hiển thị chúng theo thứ tự ngày ngược. VD 9 cho thấy việc cập nhật Page_Load xử lý sự kiện.

Ví dụ 9: Lấy các bản ghi thích hợp

protected void Page_Load(object sender, EventArgs e) {
    //set up possible querystrings
    String cat = SubSonic.Sugar.Web.QueryString<String>("category");
    Int32 yr = 0;
    Int32 mnth = 0;
    Int32 dy = 0;

    Int32.TryParse(Request.QueryString["year"], out yr);
    Int32.TryParse(Request.QueryString["month"], out mnth);
    Int32.TryParse(Request.QueryString["day"], out dy);

    if (!String.IsNullOrEmpty(cat)) {
        MyBlog.Category selectedCategory = new MyBlog.Category("Name", cat);
        this.PostList.DataSource = selectedCategory
            .GetPostCollection()
            .OrderByDesc("PostedOn");
    } else if (yr > 0) {
        DateTime startDate, endDate;

        if (mnth > 0) {
            if (dy > 0) {
                //search by day, month, year
                startDate = new DateTime(yr, mnth, dy, 0, 0, 0);
                endDate = new DateTime(yr, mnth, dy, 23, 59, 59);
            } else {
                //search by month, year
                startDate = new DateTime(yr, mnth, 1, 0, 0, 0);
                endDate = new DateTime(yr, mnth + 1, 1, 23, 59, 59).AddDays(-1);
            }
        } else {
            //search only by year
            startDate = new DateTime(yr, 1, 1, 0, 0, 0);
            endDate = new DateTime(yr + 1, 1, 1, 23, 59, 59).AddDays(-1);
        }
        MyBlog.PostCollection posts = new MyBlog.PostCollection()
            .BetweenAnd(MyBlog.Post.Columns.PostedOn, startDate, endDate)
            .OrderByDesc(MyBlog.Post.Columns.PostedOn)
            .Load();
        this.PostList.DataSource = posts;
    } else {
        //no querystring request
        Query q = new Query(MyBlog.Post.Schema);
        q.ORDER_BY(MyBlog.Post.Columns.PostedOn, "DESC");
        q.Top = "5";
        this.PostList.DataSource = q.ExecuteReader();
    }
    this.DataBind();
}
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)
    'set up possible querystrings
    Dim cat As String = Sugar.Web.QueryString(Of String)("category")
    Dim yr As Integer = 0
    Dim mnth As Integer = 0
    Dim dy As Integer = 0

    Int32.TryParse(Request.QueryString("year"), yr)
    Int32.TryParse(Request.QueryString("month"), mnth)
    Int32.TryParse(Request.QueryString("day"), dy)

    If Not String.IsNullOrEmpty(cat) Then
        Dim selectedCategory As New MyBlog.Category("Name", cat)
        Me.PostList.DataSource = selectedCategory _
            .GetPostCollection() _
            .OrderByDesc("PostedOn")
    ElseIf yr > 0 Then
        Dim startDate, endDate As DateTime

        If (mnth > 0) Then
            If (dy > 0) Then
                'search by day, month, year
                startDate = New DateTime(yr, mnth, dy, 0, 0, 0)
                endDate = New DateTime(yr, mnth, dy, 23, 59, 59)
            Else
                'search by month, year
                startDate = New DateTime(yr, mnth, 1, 0, 0, 0)
                endDate = New DateTime(yr, mnth + 1, 1, 23, 59, 59).AddDays(-1)
            End If

        Else
            'search only by year
            startDate = New DateTime(yr, 1, 1, 0, 0, 0)
            endDate = New DateTime(yr + 1, 1, 1, 23, 59, 59).AddDays(-1)

            Dim posts As New MyBlog.PostCollection()
            posts.BetweenAnd(MyBlog.Post.Columns.PostedOn, startDate, endDate) _
                .OrderByDesc(MyBlog.Post.Columns.PostedOn) _
                .Load()
            Me.PostList.DataSource = posts
        End If
    Else
        'no querystring request
        Dim q As New Query(MyBlog.Post.Schema)
        q.ORDER_BY(MyBlog.Post.Columns.PostedOn, "DESC")
        q.Top = "5"
        Me.PostList.DataSource = q.ExecuteReader()
    End If

    Me.DataBind()
End Sub

Hãy thử xem trang default.aspx sử dụng một số truy vấn để xem các bài viết trong một thể loại cụ thể hoặc ngày.

Blog hiện tại là chức năng, mặc dù rất hạn chế. Bạn có thể sẽ muốn thêm một trang Master và / hoặc một kiểu, cũng như mở rộng này để bao gồm RSS và / hoặc hỗ trợ Atom, và thêm an ninh cho các ứng dụng; Tuy nhiên, nó thể hiện một số phương pháp bạn có thể tận dụng subsonic trong các ứng dụng của bạn: sử dụng các scaffolding để cho phép bổ sung nhanh chóng các bản ghi nếu cần thiết, và mục đó, danh sách, và truy vấn các class để thay thế các lời gọi đến cơ sở dữ liệu. Khi truy vấn cơ sở dữ liệu bằng cách sử dụng bất kỳ của các đối tượng được tạo ra hoặc Query, hầu hết các phương pháp có thể được xích lại với nhau để tạo ra các câu lệnh SQL mong muốn.

Tổng kết

Subsonic cung cấp một cách dễ dàng việc thêm một lớp truy cập dữ liệu cho các ứng dụng của bạn, mà không buộc bạn phải đưa ra quyết định về việc làm thế nào để lấy dữ liệu của bạn. Nó cung cấp cho bạn sự linh hoạt để lấy dữ liệu, hoặc truy vấn freeform khi cần thiết. Các lớp được tạo ra làm việc tốt với các built-in controls, và để tăng tốc độ phát triển của chương trình. Bạn thực sự không muốn viết lớp truy cập dữ liệu của riêng bạn, phải không?

• "Blog Engine": http://dotnetslackers.com/articles/aspnet/UsingSubSonicToCreateASimpleBogEngine.aspx • Tham khảo: GrapeCity Vietnam


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.