<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-283476008699860275</atom:id><lastBuildDate>Sat, 05 Oct 2024 01:58:07 +0000</lastBuildDate><category>.NET</category><category>C#</category><category>SQL Server</category><category>TDD</category><category>C# 4</category><category>WinForms</category><category>DLR</category><category>Silverlight</category><category>ASP.NET</category><category>ClickOnce</category><category>Java</category><category>SQL</category><category>Unit Test</category><category>ADO.NET</category><category>Android</category><category>Apache</category><category>Bing Maps</category><category>Crystal Report</category><category>Entity Framework</category><category>Interview Questions</category><category>LINQ</category><category>Linux</category><category>NHibernate</category><category>Prism</category><category>Reflection</category><category>SQL Server 2005</category><category>Security</category><category>Software Engineer</category><category>Sorting</category><category>Tiếng Nhật</category><category>Tiếng Việt</category><category>Tài liệu tham khảo</category><category>VS Color Scheme</category><category>WCF</category><category>WCF Ria Service</category><category>Windows 7</category><title>Ho Hai Thanh&#39;s Blog</title><description>&lt;b&gt;Console.WriteLine(&quot;Hello world!&quot;);&lt;/b&gt;</description><link>http://thanhhh.blogspot.com/</link><managingEditor>noreply@blogger.com (Anonymous)</managingEditor><generator>Blogger</generator><openSearch:totalResults>67</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-5232365497080718290</guid><pubDate>Thu, 06 Jun 2013 06:34:00 +0000</pubDate><atom:updated>2013-06-06T13:34:56.313+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Android</category><title>ViewPaper and Fragment: How to update data of active fragment item</title><description>&lt;p&gt;For example, I use ViewPaper to flip left and right through pages of data. But when a fragment item has been active, I need to update some related data and state.&lt;/p&gt; &lt;p&gt;So, I need to implement the fragment adapter that extends from &lt;a href=&quot;http://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html&quot;&gt;FragmentStatePagerAdapter&lt;/a&gt; and override methods to keep reference from position to fragment item, in this case, we can use HashMap but in Android SDK recommend, I used &lt;a href=&quot;http://developer.android.com/reference/android/util/SparseArray.html&quot;&gt;SparseArray&lt;/a&gt; to keep this reference. On the activity or fragment parent, implement listener OnPageChangeListener for PageIndicator to detect the position of fragment item has been activated and update related data&#39;s state.&lt;/p&gt; &lt;p&gt;The adapter can implement as following:&lt;/p&gt; &lt;pre class=&quot;brush: java&quot;&gt;
public static class MyAdapter extends FragmentStatePagerAdapter {
    private SparseArray&amp;lt;TestFragment&amp;gt; mPageReferenceMap 
                              = new SparseArray&amp;lt;TestFragment&amp;gt;();
    ...

    @Override 
    public Object instantiateItem(ViewGroup viewGroup, int position) {
        Object obj = super.instantiateItem(viewGroup, position);

        //Add the reference when fragment has been create or restore
        if (obj instanceof TestFragment) {
                TestFragment f= (TestFragment)obj;
                mPageReferenceMap.put(position, f);
        }

        return obj;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        //Remove the reference when destroy it
        mPageReferenceMap.remove(position);

        super.destroyItem(container, position, object);
    }

    public TestFragment getFragment(int key) {

        return mPageReferenceMap.get(key);
    }
    ...
}
&lt;/pre&gt;

&lt;p&gt;Hope this help.&lt;/p&gt;  </description><link>http://thanhhh.blogspot.com/2013/06/viewpaper-and-fragment-how-to-update.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-3383321762350699727</guid><pubDate>Sun, 03 Jun 2012 04:42:00 +0000</pubDate><atom:updated>2013-06-06T11:27:15.599+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C#</category><category domain="http://www.blogger.com/atom/ns#">NHibernate</category><category domain="http://www.blogger.com/atom/ns#">SQL</category><title>Create select sub-query on NHibernate</title><description>&lt;p&gt;On this sample, I will show you how to create select sub-query on NHibernate by using DetachedCriteria and Projections.&lt;/p&gt; &lt;p&gt;For example, I have a model with following structure:&lt;/p&gt; &lt;div id=&quot;scid:812469c5-0cb0-4c63-8c15-c81123a09de7:d727a9b0-5a3b-403a-b9a8-77ea88a7789e&quot; class=&quot;wlWriterSmartContent&quot; style=&quot;float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px&quot;&gt;&lt;pre class=&quot;brush: c#&quot;&gt;public partial class Auction
{
    public virtual long AuctionID { get; set; }
    public virtual IList&amp;lt;MaxBid&amp;gt; MaxBids { get; set; }

    public virtual DateTimeOffset EndDate { get; set; }
}&lt;/pre&gt;&lt;/div&gt;
&lt;div id=&quot;scid:812469c5-0cb0-4c63-8c15-c81123a09de7:1294b18b-1788-4320-a4cb-841db31a2ccf&quot; class=&quot;wlWriterSmartContent&quot; style=&quot;float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px&quot;&gt;&lt;pre class=&quot;c#&quot; name=&quot;code&quot;&gt;public partial class Account {
  public virtual long AccountID { get; set; }
}&lt;/pre&gt;&lt;/div&gt;
&lt;div id=&quot;scid:812469c5-0cb0-4c63-8c15-c81123a09de7:81cdd529-1268-48cd-b2e5-dc80764eed62&quot; class=&quot;wlWriterSmartContent&quot; style=&quot;float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px&quot;&gt;&lt;pre class=&quot;brush: c#&quot;&gt;    
public partial class MaxBid {
        public virtual long MaxBidID { get; set; }
        public virtual Auction Auction { get; set; }
        public virtual Account BuyerAccount { get; set; }
    }&lt;/pre&gt;&lt;/div&gt;
&lt;div id=&quot;scid:812469c5-0cb0-4c63-8c15-c81123a09de7:b6661e88-1b81-4f3e-b5b5-8edd68e32560&quot; class=&quot;wlWriterSmartContent&quot; style=&quot;float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px&quot;&gt;&lt;pre class=&quot;brush: c#&quot;&gt;public partial class Bid {
	public virtual long BidID { get; set; }
	public virtual MaxBid MaxBid { get; set; }
    public virtual bool IsRetracted { get; set; }
}&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We have to create a query to get all auction, on each auction, we have to determine the current bid price (the latest bid record) and the current buyer’s max bid price (the latest max bid record by buyer) without retraction status. We can easily create a SQL Transact query as following (on this example, buyer account id = 1):&lt;/p&gt;
&lt;div id=&quot;scid:812469c5-0cb0-4c63-8c15-c81123a09de7:67514c20-e0c8-4ac2-9836-6d2e0b1742d7&quot; class=&quot;wlWriterSmartContent&quot; style=&quot;float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px&quot;&gt;&lt;pre class=&quot;brush: sql&quot;&gt;SELECT Auction.AuctionID
	, Auction.AssetClassID
	, Auction.EndDate
	, (SELECT MAX(Bid.BidPrice) FROM MaxBid LEFT JOIN Bid ON MaxBid.MaxBidID = Bid.MaxBidID 
		WHERE MaxBid.AuctionID = Auction.AuctionID AND MaxBid.IsRetracted = 0
		) AS CurrentBid
	, (SELECT MAX(MaxBid.MaxBidPrice) FROM MaxBid 
		WHERE MaxBid.AuctionID = Auction.AuctionID 
		AND MaxBid.IsRetracted = 0 AND MaxBid.BuyerAccountID = 1
		) AS MyMaxBid  
FROM Auction&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This query select all auction, each auction, we have to get maximum bib price without retraction and buyer’s maximum bid price.&lt;/p&gt;
&lt;p&gt;We can create NHibernate query to do this as following:&lt;/p&gt;
&lt;div id=&quot;scid:812469c5-0cb0-4c63-8c15-c81123a09de7:7dbb9792-852d-436f-8e26-6f6675524a01&quot; class=&quot;wlWriterSmartContent&quot; style=&quot;float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px&quot;&gt;&lt;pre class=&quot;brush: c#&quot;&gt;var myMaxBidQuery =
    DetachedCriteria.For&amp;lt;MaxBid&amp;gt;(&quot;mb&quot;)
    .Add(Restrictions.Eq(&quot;mb.IsRetracted&quot;, false))
    .Add(Restrictions.Eq(&quot;mb.BuyerAccount.AccountID&quot;, 1L))
    .Add(Property.ForName(&quot;mb.Auction.AuctionID&quot;).EqProperty(&quot;a.AuctionID&quot;))
    .SetProjection(Projections.Max(&quot;mb.MaxBidPrice&quot;));

var currentBidQuery =
    DetachedCriteria.For&amp;lt;MaxBid&amp;gt;(&quot;mb&quot;)
    .CreateAlias(&quot;mb.Bids&quot;, &quot;b&quot;, JoinType.LeftOuterJoin)
    .Add(Restrictions.Eq(&quot;mb.IsRetracted&quot;, false))
    .Add(Property.ForName(&quot;mb.Auction.AuctionID&quot;).EqProperty(&quot;a.AuctionID&quot;))
    .SetProjection(Projections.Max(&quot;b.BidPrice&quot;));

var query =
    DetachedCriteria.For&amp;lt;Auction&amp;gt;(&quot;a&quot;)
        .SetProjection(Projections.ProjectionList()
        .Add(Projections.Property(&quot;a.AuctionID&quot;))
        .Add(Projections.Property(&quot;a.AssetClass.AssetClassID&quot;))
        .Add(Projections.Property(&quot;EndDate&quot;))
        .Add(Projections.SubQuery(currentBidQuery))
        .Add(Projections.SubQuery(myMaxBidQuery)))
        .SetResultTransformer(Transformers.AliasToBean&amp;lt;DataResult&amp;gt;());

var results = query.GetExecutableCriteria(session).List&amp;lt;DataResult&amp;gt;();&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In this case, I created two DetachedCriteria objects to perform sub-queries, each sub-query has a Property.ForName to put the relation to main query by equal property (EqProperty). And on the main query, they will be add into main query by using Projections.SubQuery. And the result, I used Transformers to transform the result to customized bean based on the structure of query, we can create transformer result class as following code snippet:&lt;/p&gt;
&lt;div id=&quot;scid:812469c5-0cb0-4c63-8c15-c81123a09de7:5cc6277a-39ec-4237-97f2-2e8944484026&quot; class=&quot;wlWriterSmartContent&quot; style=&quot;float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px&quot;&gt;&lt;pre class=&quot;brush: c#&quot;&gt;public class DataResult
{
    public long AuctionID { get; set; }
    public int AssetClassID { get; set; }
    public DateTimeOffset EndDate { get; set; }
    public decimal MaxBidPrice { get; set; }
    public decimal BidPrice { get; set; }
}&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Hope that by this sample, I can help you to create more complex queries using DetachedCriteria, Projections and Transformer on NHibernate and of course, we can translate it to Hibernate on Java.&lt;/p&gt;  </description><link>http://thanhhh.blogspot.com/2012/06/create-select-sub-query-on-nhibernate.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-8318464617878768023</guid><pubDate>Thu, 20 Oct 2011 08:50:00 +0000</pubDate><atom:updated>2011-10-25T09:38:58.424+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">C# 4</category><category domain="http://www.blogger.com/atom/ns#">Entity Framework</category><category domain="http://www.blogger.com/atom/ns#">LINQ</category><title>LINQ to Entities: PredicateBuilder and Dynamic Linq</title><description>When you want to build dynamic filter for a query on LINQ to SQL or LINQ to Entities (Entity Framework, NHibernate, ..), you can use a famous &lt;a href=&quot;http://www.albahari.com/nutshell/predicatebuilder.aspx&quot;&gt;Albahari&#39;s PredicateBuilder&lt;/a&gt;. But sometime, it may be doesn&#39;t work perfect with LINQ to Entities. To solve this problem, Pete Montgomery introduced a universal PredicateBuilder that combined the original version of Albahari&#39;s PredicateBuilder and &lt;a href=&quot;http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx&quot;&gt;LINQ to Entities: Combining Predicates&lt;/a&gt;.  &lt;br /&gt;  &lt;a name=&#39;more&#39;&gt;&lt;/a&gt;  &lt;br /&gt;The source code of this PredicateBuilder as following:   &lt;br /&gt;  &lt;pre class=&quot;brush: csharp;&quot;&gt;/// &amp;lt;summary&amp;gt;
/// Enables the efficient, dynamic composition of query predicates.
/// &amp;lt;/summary&amp;gt;
public static class PredicateBuilder
{
    /// &amp;lt;summary&amp;gt;
    /// Creates a predicate that evaluates to true.
    /// &amp;lt;/summary&amp;gt;
    public static Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; True&amp;lt;T&amp;gt;()
    {
        return param =&amp;gt; true;
    }

    /// &amp;lt;summary&amp;gt;
    /// Creates a predicate that evaluates to false.
    /// &amp;lt;/summary&amp;gt;
    public static Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; False&amp;lt;T&amp;gt;()
    {
        return param =&amp;gt; false;
    }

    /// &amp;lt;summary&amp;gt;
    /// Creates a predicate expression from the specified lambda expression.
    /// &amp;lt;/summary&amp;gt;
    public static Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; Create&amp;lt;T&amp;gt;(
        Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate)
    {
        return predicate;
    }

    /// &amp;lt;summary&amp;gt;
    /// Combines the first predicate with the second using the logical &amp;quot;and&amp;quot;.
    /// &amp;lt;/summary&amp;gt;
    public static Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; And&amp;lt;T&amp;gt;(
        this Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; first, Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; second)
    {
        return first.Compose(second, Expression.AndAlso);
    }

    /// &amp;lt;summary&amp;gt;
    /// Combines the first predicate with the second using the logical &amp;quot;or&amp;quot;.
    /// &amp;lt;/summary&amp;gt;
    public static Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; Or&amp;lt;T&amp;gt;(
        this Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; first, Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; second)
    {
        return first.Compose(second, Expression.OrElse);
    }

    /// &amp;lt;summary&amp;gt;
    /// Negates the predicate.
    /// &amp;lt;/summary&amp;gt;
    public static Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; Not&amp;lt;T&amp;gt;(
        this Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; expression)
    {
        var negated = Expression.Not(expression.Body);
        return Expression.Lambda&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt;(negated, expression.Parameters);
    }

    /// &amp;lt;summary&amp;gt;
    /// Combines the first expression with the second using the specified merge function.
    /// &amp;lt;/summary&amp;gt;
    static Expression&amp;lt;T&amp;gt; Compose&amp;lt;T&amp;gt;(
        this Expression&amp;lt;T&amp;gt; first, Expression&amp;lt;T&amp;gt; second, Func&amp;lt;Expression, 
        Expression, Expression&amp;gt; merge)
    {
        // zip parameters (map from parameters of second to parameters of first)
        var map = first.Parameters
            .Select((f, i) =&amp;gt; new { f, s = second.Parameters[i] })
            .ToDictionary(p =&amp;gt; p.s, p =&amp;gt; p.f);

        // replace parameters in the second lambda expression 
        // with the parameters in the first
        var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);

        // create a merged lambda expression with parameters from the first expression
        return Expression.Lambda&amp;lt;T&amp;gt;(merge(first.Body, secondBody), first.Parameters);
    }

    class ParameterRebinder : ExpressionVisitor
    {
        readonly Dictionary&amp;lt;ParameterExpression, ParameterExpression&amp;gt; _map;

        ParameterRebinder(Dictionary&amp;lt;ParameterExpression, 
            ParameterExpression&amp;gt; map)
        {
            _map = map ?? new Dictionary&amp;lt;ParameterExpression, ParameterExpression&amp;gt;();
        }

        public static Expression ReplaceParameters(
            Dictionary&amp;lt;ParameterExpression, ParameterExpression&amp;gt; map, Expression exp)
        {
            return new ParameterRebinder(map).Visit(exp);
        }

        protected override Expression VisitParameter(ParameterExpression p)
        {
            ParameterExpression replacement;

            if (_map.TryGetValue(p, out replacement))
            {
                p = replacement;
            }

            return base.VisitParameter(p);
        }
    }
}&lt;/pre&gt;

&lt;br /&gt;But please stay on, we have a problem if we use this PredicateBulder with dynamic filter that is same as (A OR B) AND (X OR Y).

&lt;br /&gt;When you try to build this filter, at run-time, you it throws an exception message as following:

&lt;br /&gt;The parameter &#39;f&#39; was not bound in the specified LINQ to Entities query expression.

&lt;br /&gt;To solve the above issue, we must use Expand method to combine between PredicateBuilder expressions.

&lt;br /&gt;And here is the sample for this problem:

&lt;br /&gt;

&lt;br /&gt;Sample in SQL syntax: 

&lt;br /&gt;

&lt;pre class=&quot;brush: sql;&quot;&gt;SELECT * 
FROM dbo.Customers c
WHERE 
    (c.FirstName LIKE &#39;%A%&#39; 
    OR c.LastName LIKE &#39;%B%&#39;)
AND (c.Birthday = &#39;1998-01-01&#39; 
    OR c.Birthday = &#39;1999-12-31&#39;)&lt;/pre&gt;

&lt;p&gt;And here is in C# Statements: &lt;/p&gt;

&lt;pre class=&quot;brush: csharp;&quot;&gt;using (var db = new CustomerContext())
{
    //With OR block, you must use FALSE
    var wBlockOr1 = PredicateBuilder.False();

    wBlockOr1 = wBlockOr1.Or(c =&amp;gt; c.FirstName.Contains(&amp;quot;A&amp;quot;));
    wBlockOr1 = wBlockOr1.Or(c =&amp;gt; c.LastName.Contains(&amp;quot;B&amp;quot;));

    var wBlockOr2 = PredicateBuilder.False();

    wBlockOr1 = wBlockOr1.Or(
        c =&amp;gt; c.Birthday == new DateTime(1998, 1, 1));
    wBlockOr1 = wBlockOr1.Or(
        c =&amp;gt; c.Birthday == new DateTime(1999, 12, 31));

    //With AND block, you must use TRUE
    var wAndBlock = PredicateBuilder.True();

    //Combine between Or blocks
    wAndBlock = wAndBlock.And(wBlockOr1.Expand());
    wAndBlock = wAndBlock.And(wBlockOr2.Expand());

    var customers = db.Customers.AsExpandable().Where(wAndBlock);
}&lt;/pre&gt;

&lt;p&gt;Hope that this article help you to solve the problem on LINQ to Entities. &lt;/p&gt;  </description><link>http://thanhhh.blogspot.com/2011/10/linq-to-entities-predicatebuilder-and.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-7656231583833347344</guid><pubDate>Thu, 11 Aug 2011 03:59:00 +0000</pubDate><atom:updated>2011-08-11T10:59:16.235+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Apache</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><title>Apache: Deny access from everyone, except your IP address</title><description>&lt;p&gt;To block all visitors to access your website except your IP address and customers’. You can add&amp;#160; following codes at the end of httpd.conf file and restart your httpd.&lt;/p&gt;  &lt;pre class=&quot;brush: bash;&quot;&gt;# Deny access from everyone, except those IP listed here:
&amp;lt;Files *&amp;gt;
  Order deny,allow
  Deny from all
  Allow from xxx.xxx.xxx.xxx
  Allow from yyy.yyy.yyy.yyy
&amp;lt;/Files&amp;gt;&lt;/pre&gt;  </description><link>http://thanhhh.blogspot.com/2011/08/apache-deny-access-from-everyone-except.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-4803574030259215790</guid><pubDate>Wed, 20 Jul 2011 06:10:00 +0000</pubDate><atom:updated>2011-10-11T23:28:37.458+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL</category><title>SQL: Lấy dữ liệu dòng cuối cùng trong mỗi group</title><description>Bài toán này đặt ra trong trường hợp khi bảng dữ liệu có từ hai khóa chính (primary key) trở lên, trong đó yêu cầu xác định dữ liệu sao cho một khóa chính nào đó là duy nhất và lớn nhất.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Bài toán: Ta có bảng &lt;strong&gt;Table1&lt;/strong&gt; với dữ liệu như sau:&lt;br /&gt;
&lt;table border=&quot;1&quot; cellpadding=&quot;2&quot; cellspacing=&quot;0&quot; style=&quot;border: solid #C3C3C3;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;58&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col1 (PK)&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;66&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col2&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;(PK)&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;76&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col3&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;(PK)&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;193&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col4&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;61&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;68&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;76&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;190&quot;&gt;&lt;div align=&quot;center&quot;&gt;
A_1_1&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;63&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;69&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;76&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;189&quot;&gt;&lt;div align=&quot;center&quot;&gt;
A_1_2&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;64&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;75&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;188&quot;&gt;&lt;div align=&quot;center&quot;&gt;
A_2_2&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;65&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;75&quot;&gt;&lt;div align=&quot;right&quot;&gt;
4&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;187&quot;&gt;&lt;div align=&quot;center&quot;&gt;
A_2_4&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;65&quot;&gt;&lt;div align=&quot;right&quot;&gt;
B&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;75&quot;&gt;&lt;div align=&quot;right&quot;&gt;
4&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;187&quot;&gt;&lt;div align=&quot;center&quot;&gt;
B_1_4&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;65&quot;&gt;&lt;div align=&quot;right&quot;&gt;
B&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;75&quot;&gt;&lt;div align=&quot;right&quot;&gt;
5&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;187&quot;&gt;&lt;div align=&quot;center&quot;&gt;
B_1_5&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;65&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;75&quot;&gt;&lt;div align=&quot;right&quot;&gt;
3&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;187&quot;&gt;&lt;div align=&quot;center&quot;&gt;
C_1_3&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;65&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;75&quot;&gt;&lt;div align=&quot;right&quot;&gt;
6&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;187&quot;&gt;&lt;div align=&quot;center&quot;&gt;
C_2_6&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;65&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;75&quot;&gt;&lt;div align=&quot;right&quot;&gt;
7&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;187&quot;&gt;&lt;div align=&quot;center&quot;&gt;
C_2_7&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;65&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;75&quot;&gt;&lt;div align=&quot;right&quot;&gt;
9&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;187&quot;&gt;&lt;div align=&quot;center&quot;&gt;
C_2_9&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Bây giờ, vấn đề đặt ra là làm thế nào để nhóm dữ liệu theo &lt;strong&gt;Col1&lt;/strong&gt; và &lt;strong&gt;Col2&lt;/strong&gt;, sau đó lấy thêm dữ liệu của &lt;strong&gt;Col3&lt;/strong&gt; và &lt;strong&gt;Col4&lt;/strong&gt; sao cho &lt;strong&gt;Col3&lt;/strong&gt; là lớn nhất trong mỗi dòng dữ liệu mà tôi muốn có.&lt;br /&gt;
Kết quả sẽ như sau:&lt;br /&gt;
&lt;table border=&quot;1&quot; cellpadding=&quot;2&quot; cellspacing=&quot;0&quot; style=&quot;border: solid #C3C3C3;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;58&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col1&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;66&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col2&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;76&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col3&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;194&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col4&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;61&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;68&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;76&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;191&quot;&gt;&lt;div align=&quot;center&quot;&gt;
A_1_2&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;63&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;69&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;76&quot;&gt;&lt;div align=&quot;right&quot;&gt;
4&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;190&quot;&gt;&lt;div align=&quot;center&quot;&gt;
A_2_4&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;64&quot;&gt;&lt;div align=&quot;right&quot;&gt;
B&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;76&quot;&gt;&lt;div align=&quot;right&quot;&gt;
5&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;189&quot;&gt;&lt;div align=&quot;center&quot;&gt;
B_1_5&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;65&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;75&quot;&gt;&lt;div align=&quot;right&quot;&gt;
3&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;188&quot;&gt;&lt;div align=&quot;center&quot;&gt;
C_1_3&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;66&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;75&quot;&gt;&lt;div align=&quot;right&quot;&gt;
9&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;188&quot;&gt;&lt;div align=&quot;center&quot;&gt;
C_2_9&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Giải pháp trong trường hợp này có thể viết như sau:&lt;br /&gt;
&lt;pre class=&quot;brush: sql;&quot;&gt;SELECT T1.*
FROM Table1 T1 LEFT JOIN Table1 T2
    ON  T1.Col1 = T2.Col1
    AND T1.Col2 = T2.Col2
    AND T1.Col3 &amp;lt; T2.Col3
WHERE T2.Col3 IS NULL;     &lt;/pre&gt;
Giải thích: Để giải thích cho câu lệnh trên, ta có thể sử dụng câu lệnh như sau:&lt;br /&gt;
&lt;pre class=&quot;brush: sql;&quot;&gt;SELECT T1.Col1, T1.Col2, T1.Col3, T2.Col1, T2.Col2, T2.Col3
FROM Table1 T1 LEFT JOIN Table1 T2
    ON T1.Col1 = T2.Col1
    AND T1.Col2 = T2.Col2
    AND T1.Col3 &amp;lt; T2.Col3;&lt;/pre&gt;
Kết quả sẽ như sau:&lt;br /&gt;
&lt;table border=&quot;1&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; style=&quot;border: solid #C3C3C3;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;66&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col1&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;69&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col2&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;71&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col3&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;82&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col1&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;80&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col2&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;81&quot;&gt;&lt;div align=&quot;center&quot;&gt;
&lt;strong&gt;Col3&lt;/strong&gt;&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;67&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;71&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;82&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;80&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;81&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;67&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;71&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;82&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;80&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;81&quot;&gt;&lt;div align=&quot;right&quot;&gt;
NULL&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;67&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;71&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;82&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;80&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;81&quot;&gt;&lt;div align=&quot;right&quot;&gt;
4&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;67&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;71&quot;&gt;&lt;div align=&quot;right&quot;&gt;
4&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;82&quot;&gt;&lt;div align=&quot;right&quot;&gt;
A&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;80&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;81&quot;&gt;&lt;div align=&quot;right&quot;&gt;
NULL&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;67&quot;&gt;&lt;div align=&quot;right&quot;&gt;
B&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;71&quot;&gt;&lt;div align=&quot;right&quot;&gt;
4&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;82&quot;&gt;&lt;div align=&quot;right&quot;&gt;
B&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;80&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;81&quot;&gt;&lt;div align=&quot;right&quot;&gt;
5&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;67&quot;&gt;&lt;div align=&quot;right&quot;&gt;
B&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;71&quot;&gt;&lt;div align=&quot;right&quot;&gt;
5&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;82&quot;&gt;&lt;div align=&quot;right&quot;&gt;
B&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;80&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;81&quot;&gt;&lt;div align=&quot;right&quot;&gt;
NULL&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;67&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;71&quot;&gt;&lt;div align=&quot;right&quot;&gt;
3&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;82&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;80&quot;&gt;&lt;div align=&quot;right&quot;&gt;
1&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;81&quot;&gt;&lt;div align=&quot;right&quot;&gt;
NULL&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;67&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;71&quot;&gt;&lt;div align=&quot;right&quot;&gt;
6&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;82&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;80&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;81&quot;&gt;&lt;div align=&quot;right&quot;&gt;
7&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;67&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;71&quot;&gt;&lt;div align=&quot;right&quot;&gt;
6&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;82&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;80&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;81&quot;&gt;&lt;div align=&quot;right&quot;&gt;
9&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;67&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;71&quot;&gt;&lt;div align=&quot;right&quot;&gt;
7&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;82&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;80&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;81&quot;&gt;&lt;div align=&quot;right&quot;&gt;
9&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;tr&gt;       &lt;td valign=&quot;top&quot; width=&quot;67&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;70&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;71&quot;&gt;&lt;div align=&quot;right&quot;&gt;
9&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;82&quot;&gt;&lt;div align=&quot;right&quot;&gt;
C&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;80&quot;&gt;&lt;div align=&quot;right&quot;&gt;
2&lt;/div&gt;
&lt;/td&gt;        &lt;td valign=&quot;top&quot; width=&quot;81&quot;&gt;&lt;div align=&quot;right&quot;&gt;
NULL&lt;/div&gt;
&lt;/td&gt;     &lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
Với điều kiện LEFT JOIN là T1.Col3 &amp;lt; T2.Col3, với mỗi nhóm (T1.Col1, T1.Col2), T1.Col3 là lớn nhất thì T2.Col3 sẽ là NULL.&lt;br /&gt;
Do đó, khi bổ sung thêm điều kiện sau khi LEFT JOIN là T2.Col3 IS NULL thì ta sẽ lấy được kết quả mong muốn.</description><link>http://thanhhh.blogspot.com/2011/07/sql-phuong-phap-lay-du-lieu-dong-cuoi.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-8654306742572161133</guid><pubDate>Wed, 06 Apr 2011 08:20:00 +0000</pubDate><atom:updated>2011-09-29T09:39:24.133+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ASP.NET</category><category domain="http://www.blogger.com/atom/ns#">C#</category><category domain="http://www.blogger.com/atom/ns#">Security</category><title>Sử dụng Impersonation để truy cập tài nguyên trong mạng</title><description>Với các ứng dụng cần thực hiện truy cập và sử dụng đến các tài nguyên có trong hệ thống&amp;nbsp; mạng nội bộ hoặc VPN (ví dụ như đọc/ghi file vào thư mục sharing trên serrver). Nếu ứng dụng của bạn chạy trong cùng một Domain và sử dụng Windows Authentication thì việc thiết lập này khá đơn giản. &lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;Tuy nhiên, có thể trong một hệ thống mạng có nhiều Domain khác nhau, thì đoạn code sau sẽ giúp bạn làm việc sử dụng các tài nguyên không cùng trong Domain bằng user account được phân quyền truy cập (đọc/ghi/..) vào Domain đó. &lt;br /&gt;
&lt;pre class=&quot;brush: csharp;&quot;&gt;using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Security.Permissions;

namespace Security
{
    public enum LogonType : int
    {
        LOGON32_LOGON_INTERACTIVE = 2,
        LOGON32_LOGON_NETWORK = 3,
        LOGON32_LOGON_BATCH = 4,
        LOGON32_LOGON_SERVICE = 5,
        LOGON32_LOGON_UNLOCK = 7,
        LOGON32_LOGON_NETWORK_CLEARTEXT = 8, // Only for Win2K or higher
        LOGON32_LOGON_NEW_CREDENTIALS = 9 // Only for Win2K or higher
    } ;

    public enum LogonProvider : int
    {
        LOGON32_PROVIDER_DEFAULT = 0,
        LOGON32_PROVIDER_WINNT35 = 1,
        LOGON32_PROVIDER_WINNT40 = 2,
        LOGON32_PROVIDER_WINNT50 = 3
    } ;

    internal class SecureUtil32
    {
        [DllImport(&quot;advapi32.dll&quot;, SetLastError=true)]
        public static extern bool LogonUser(
            String lpszUsername, String lpszDomain, String lpszPassword,
            int dwLogonType, int dwLogonProvider, ref IntPtr TokenHandle);

        [DllImport(&quot;kernel32.dll&quot;, CharSet=CharSet.Auto)]
        public static extern bool CloseHandle(IntPtr handle);

        [DllImport(&quot;advapi32.dll&quot;, CharSet=CharSet.Auto, SetLastError=true)]
        public static extern bool DuplicateToken(
            IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL, 
            ref IntPtr DuplicateTokenHandle);
    }

    public class ImpersonateUser : IDisposable
    {
        private static IntPtr tokenHandle = new IntPtr(0);
        
        private WindowsImpersonationContext impersonatedUser;

        // If you incorporate this code into a DLL, be sure to demand that it
        // runs with FullTrust.
        [PermissionSetAttribute(SecurityAction.Demand, Name = &quot;FullTrust&quot;)]
        public void Impersonate(string domainName, string userName, string password)
        {
            m_UserName = userName;

            tokenHandle = IntPtr.Zero;
            
            // ---- Step - 1
            // Call LogonUser to obtain a handle to an access token.
            bool returnValue = SecureUtil32.LogonUser(
                userName,
                domainName,
                password,
                (int)LogonType.LOGON32_LOGON_NEW_CREDENTIALS,
                (int)LogonProvider.LOGON32_PROVIDER_DEFAULT,
                ref tokenHandle); // tokenHandle - new security token
                
            if (false == returnValue)
            {
                impersonatedUser = null;
                return;
                //int ret = Marshal.GetLastWin32Error();
                //throw new System.ComponentModel.Win32Exception(ret);
            }
            
            // ---- Step - 2
            WindowsIdentity newId = new WindowsIdentity(tokenHandle);
            
            // ---- Step - 3
            impersonatedUser = newId.Impersonate();
        }
        
        // Stops impersonation
        public void Undo()
        {
            if (string.IsNullOrEmpty(m_UserName))
                return;

            if (impersonatedUser != null)
                impersonatedUser.Undo();
            // Free the tokens.
            if (tokenHandle != IntPtr.Zero)
                SecureUtil32.CloseHandle(tokenHandle);
        }

        #region IDisposable Members
        public void Dispose()
        {
            Undo();
        }
        #endregion
    }
}&lt;/pre&gt;
Và phương pháp sử dụng sẽ như dưới đây:&lt;br /&gt;
&lt;pre class=&quot;brush: csharp;&quot;&gt;using (ImpersonateUser imp = new ImpersonateUser())
{
    imp.Impersonate(domainName, userName, password);

    //Code truy cập tài nguyên trong hệ thống ở đây
}
&lt;/pre&gt;
Hy vọng đoạn code sẽ giúp ích được bạn trong việc giải quyết các vấn đề của mình.  </description><link>http://thanhhh.blogspot.com/2011/04/su-dung-impersonation-e-truy-cap-tai.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-168289028241526317</guid><pubDate>Fri, 18 Mar 2011 14:22:00 +0000</pubDate><atom:updated>2011-10-11T23:31:07.608+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">TDD</category><category domain="http://www.blogger.com/atom/ns#">Unit Test</category><title>Unit Testing: Giới thiệu về Moq</title><description>Moq cũng giống như RhynoMock hay TypeMock, là một thư viện .NET framework cho việc tạo các mock object. Nó hầu như sử dụng C# Lambda expression để tạo các kỳ vọng và kết quả trả về. Tuy nhiên, nó cũng bị chỉ trích vì hầu như không có sự phân biệt nào giữa Mock và Stub.&lt;br /&gt;
Moq cho phép bạn dể dàng thay đổi behaviour (hành vi) từ “Bắt buộc” sang “Tùy chọn” (Trong đó tùy chọn là ngầm định). Hành vi bắt buộc không cho phép bất kỳ triệu gọi nào đến mock object trừ khi trước đó đã được khai báo kỳ vọng tương ứng. Hành vi tùy chọn sẽ trả lại giá trị ngầm định nếu không được khai báo kỳ vọng. Ngoài ra bạn có thể thực hiện cấu hình hành vi theo ý đồ của bạn khi sử dụng.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Để giới thiệu về cách sử dụng Moq, chúng ta sẽ sử dụng lại ví dụ có trong bài viết về &lt;a href=&quot;http://thanhhh.blogspot.com/2011/03/unit-testingmock-khong-phai-la-stub.html&quot; target=&quot;_blank&quot;&gt;Unit Testing–Mock không phải là Stub&lt;/a&gt;. Với yêu cầu của bài toán, ta sẽ tạo interface IWarehouse và class Order với phương thức Order.Fill sử dụng IWarehouse như sau:&lt;br /&gt;
&lt;pre class=&quot;brush: csharp;&quot;&gt;public interface IWarehouse
{
    bool HasInventory(string productName, int quantity);
    
    void Remove(string productName, int quantity);
}

public class Order
{
    public string ProductName { get; private set; }

    public int Quantity { get; private set; }

    public bool IsFilled { get; private set; }

    public Order(string productName, int quantity)
    {
        ProductName = productName;
        Quantity = quantity;

        IsFilled = false;
    }

    public void Fill(IWarehouse warehouse)
    {
        if (!warehouse.HasInventory(ProductName, Quantity))
        {
            return;
        }
        
        warehouse.Remove(ProductName, Quantity);

        IsFilled = true;
    }
}&lt;/pre&gt;
Phương thức mà bạn muốn kiểm thử ở đây chính là &lt;strong&gt;Order.Fill(IWarehouse)&lt;/strong&gt;. Tuy nhiên, rõ ràng bạn không muốn thực hiện truy cập cơ sở dữ liệu hoặc web service để lấy về thông tin tồn kho (nếu sử dụng đối tượng thật), bởi vì khi đó bạn phải cài đặt nó, và do đó có thể xảy ra lỗi trong quá trình thực thi cũng như chưa chắc đảm bảo được việc cài đặt của bạn là chính xác và chạy đúng. &lt;br /&gt;
Do đó, để giải quyết vấn đề này, thì ta sẽ sử dụng mock obect và thiết lập sao cho phương thức &lt;strong&gt;warehouse.HasInventory(ProductName, Quantity)&lt;/strong&gt; được thực hiện đúng theo mong muốn của từng trường hợp kiểm thử, người ta gọi đó là kỳ vọng.&lt;br /&gt;
Dưới đây là đoạn code thiết lập kỳ vọng:&lt;br /&gt;
&lt;pre class=&quot;brush: csharp;&quot;&gt;// Khởi tạo đối tượng Order và thiết lập dữ liệu
Order order = new Order(TALISKER, 50);

// Khởi tạo một đối tượng mock sử dụng Moq
var warehouseMock = new Mock&amp;lt;IWarehouse&amp;gt;();

// Đảm bảo rằng sẽ trả lại true 
// nếu kiểm tra số lượng tồn kho 50 đối với sản phẩm TALISKER
warehouseMock
    .Setup(warehouse =&amp;gt; warehouse.HasInventory(TALISKER, 50))
    .Returns(true);

// Đảm bảo rằng sản phẩm TALISKER sẽ được xuất kho 50 sản phẩm
warehouseMock
    .Setup(warehouse =&amp;gt; warehouse.Remove(TALISKER, 50));&lt;/pre&gt;
Tiếp theo, tùy thuộc vào quan điểm của bạn để kiểm tra kết quả, bạn có thể sử dụng kiểm tra bằng xác nhận trạng thái (state verification) hoặc xác nhận hành vi (behaviour verification) để viết đoạn code xác nhận. Ở đây, tôi đã sử dụng cả hai việc kiểm tra xác nhận trạng thái và xác nhận hành vi.&lt;br /&gt;
&lt;pre class=&quot;brush: csharp;&quot;&gt;// Thực thi việc giao hàng theo đơn hàng
order.Fill(warehouseMock.Object);

// Xác nhận hành vi cho cả 2 kỳ vọng
// 1. Việc kiểm tra tồn kho được thực thi 
//    và trả về đúng giá trị mong muồn
// 2. Việc xuất kho được thực hiện
warehouseMock.Verify();

// Hoặc bạn có thể viết như sau để xác nhận từng kỳ vọng
// warehouseMock
//    .Verify(warehouse =&amp;gt; warehouse.HasInventory(TALISKER, 50));
// warehouseMock
//    .Verify(warehouse =&amp;gt; warehouse.Remove(TALISKER, 50));

// Xác nhận trạng thái: Đảm bảo đơn hàng sau khi  
// được tiến hành giao hàng đã đổi trạng thái đúng
Assert.IsTrue(order.IsFilled);&lt;/pre&gt;
Việc xác nhận hành vi sẽ xác nhận để đảm bảo cả hai phương thức kỳ vọng đã được gọi một cách chính xác. Trong trường hợp xác nhận, bạn không cần quan tâm giá trị trả về. Điều quan trọng nhất trong trường hợp này là các phương thức HasInventory và Remove đã được gọi. Mỗi khi điều này được thực hiện, ta có thể xem như hành vi được kỳ vọng đã được xác nhận.&lt;br /&gt;
Đối việc xác nhận trạng thái, vì trong trường hợp kiểm thử này ta muốn kiểm tra xem khi đủ điều kiện giao hàng, giao dịch giao hàng được tiến hành thì liệu trạng thái của đơn hàng có được tự động chuyển sang trạng thái đã giao hàng hay chưa. Do đó, ta cần phải xác nhận trạng thái để đảm bảo rằng nghiệp vụ mong muốn của chúng ta được thực hiện đúng.&lt;br /&gt;
Nhiều người cho rằng xác nhận hành vi sẽ tốt hơn xác nhận trạng thái (hoặc ngược lại). Đối với quan điểm của tôi, tôi thích sử dụng cả hai xác nhận này. Trường hợp kiểm thử ở trên là ví dụ rất tốt cho quan điểm này, tôi có thể xác nhận hành vi của các kỳ vọng cũng như xác nhận trạng thái của đơn hàng sau khi nó được giao hàng.&lt;br /&gt;
Qua bài giới thiệu này, hy vọng rằng các bạn đã hình thành được khái niệm và cách sử dụng Moq để tạo các mock object trong việc kiểm thử TDD. Các ví dụ chi tiết hơn về Moq và tài liệu mô tả về nó, bạn có thể truy cập vào &lt;a href=&quot;http://code.google.com/p/moq/wiki/QuickStart&quot; target=&quot;_blank&quot;&gt;Moq Quick Start&lt;/a&gt;. </description><link>http://thanhhh.blogspot.com/2011/03/gioi-thieu-ve-moq.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-7934427050527909493</guid><pubDate>Thu, 17 Mar 2011 17:10:00 +0000</pubDate><atom:updated>2011-10-11T23:30:53.091+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">TDD</category><category domain="http://www.blogger.com/atom/ns#">Unit Test</category><title>Unit Testing: Mock không phải là Stub</title><description>&lt;em&gt;Thuật ngữ &#39;Mock Object (Đối tượng giả định)&#39; đã trở nên phổ biến, nó mô tả về các đối tượng trong trường hợp đặc biệt bắt chước các đối tượng thật trong kiểm thử. Hầu hết các ngôn ngữ lập trình bây giờ đều có framework giúp đơn giản hóa việc tạo các mock object. Mock object thường không thực tế, tuy nhiên là hình mẫu của đối tượng kiểm thử trong trường hợp đặc biệt và cho phép một cách khác của việc kiểm thử. Trong bài viết này, tôi sẽ giải thích các mock object làm việc như thế nào, bằng cách nào chúng giúp cho việc kiểm thử dựa trên xác nhận tình trạng thạng thái, và bằng cách nào sử dụng chúng để phát triển một cách khác của việc kiểm thử.&lt;/em&gt;&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;h3&gt;
Phương pháp kiểm thử cổ điển&lt;/h3&gt;
Tôi sẽ bắt đầu bằng việc minh họa hai phương pháp kiểm thử khác nhau của một ví dụ đơn giản. Chúng tôi muốn tạo một đối tượng đơn đặt hàng và thực hiện giao hàng cho đơn đặt hàng này từ một đối tượng kho hàng. Đơn hàng trong trường hợp rất đơn giản, chỉ có một sản phẩm và số lượng đặt hàng. Kho hàng giữ số lượng tồn kho của các sản phẩm. Khi chúng ta yêu cầu giao hàng cho một đơn hàng từ kho hàng, sẽ có hai trường hợp xảy ra. Nếu đủ số lượng trong kho hàng theo yêu cầu của đơn hàng thì tiến hành giao hàng, đơn hàng có trạng thái được giao hàng và số lượng tồn kho của sản phẩm tương ứng trong kho được giảm xuống đúng bằng số lượng đã giao. Nếu không đủ số lượng tồn kho theo yêu cầu của đơn hàng, đơn hàng sẽ không thể tiến hành giao hàng và kho hàng vẫn giữ nguyên trạng thái cũ.&lt;br /&gt;
Có hai trạng thái bao hàm trong hai trường hợp kiểm thử đối với đối tượng Order, sau đây là đoạn code kiểm thử được cài đặt bằng ngôn ngữ C# sử dụng NUnit.&lt;br /&gt;
&lt;pre class=&quot;brush: csharp;&quot;&gt;[TestFixture]
public class OrderStateTester
{
    private static String TALISKER = &quot;Talisker&quot;;
    private static String HIGHLAND_PARK = &quot;Highland Park&quot;;

    private IWarehouse warehouse;

    [SetUp]
    protected void Setup()
    {
        warehouse = new WarehouseImpl();
        warehouse.Add(TALISKER, 50);
        warehouse.Add(HIGHLAND_PARK, 25);
    }

    [Test]
    public void TestOrderIsFilledIfEnoughInWarehouse()
    {
        Order order = new Order(TALISKER, 50);

        order.Fill(warehouse);

        Assert.IsTrue(order.IsFilled);

        Assert.IsTrue(0 == warehouse.GetInventory(TALISKER));
    }

    [Test]
    public void TestOrderDoesNotRemoveIfNotEnough() 
    {
        Order order = new Order(TALISKER, 51);
        
        order.Fill(warehouse);
    
        Assert.IsFalse(order.IsFilled);

        Assert.IsTrue(50 == warehouse.GetInventory(TALISKER));
    }
}&lt;/pre&gt;
Unit test thông thường có bốn bước theo thứ tự: thiết lập (setup), thực thi (execute), xác minh (verify) và kết thúc (teardown). Trong ví dụ trên thì bước thiết lập đã được thực hiện trong phương thức Setup (thiết lập kho hàng) và một phần của trường hợp kiểm thử (thiết lập đơn hàng). Việc gọi order.Fill chính là bước thực thi. Đây chính là đối tượng bị tác động để thực hiện những gì mà ta muốn kiểm thử. Các câu lệnh Assert.IsTrue và Assert.IsFalse là bước xác minh trạng thái, kiểm tra xem kết quả của việc thực thi phương thức có chính xác không. Trong trường hợp này không cần bước kết thúc, bởi vì thì trình thu dọn rác (garbage collector) đã tự động làm việc này.&lt;br /&gt;
Trong bước thiết lập có hai loại đối tượng mà chúng ta thực hiện cùng nhau. Order là lớp mà chúng ta kiểm thử, nhưng để Order.Fill thực hiện được chúng ta cần một instance của Warehouse. Trong trường hợp này Order là đối tượng mà chúng ta tập trung vào để kiểm thử. Có nhiều thuật ngữ để ám chỉ đến đối tượng này, nhưng trong bài này ta sẽ sử dụng chung một thuật ngữ &quot;System Under Test (SUT)&quot;.&lt;br /&gt;
Vì vậy, đối với việc kiểm thử này chúng ta cần SUT (Order) và một cộng tác viên (Warehouse). Chúng ta cần kho hàng cho hai lý do: một là để lấy trạng thái đã được kiểm thử trong hầu hết trường hợp (vì Order.Fill gọi các phương thức của kho hàng) và thứ hai là cần nó để xác nhận (vì một trong những kết quả của Order.Fill là một trường hợp thay đổi trạng thái của kho hàng). &lt;br /&gt;
Phương pháp kiểm thử này sử dụng cách &lt;b&gt;xác nhận trạng thái (state verification)&lt;/b&gt;: có nghĩa là chúng ta xác định phương thức đã làm việc một cách chính xác hay không bằng cách xem xét trạng thái của SUT và cộng tác viên của nó sau khi phương thức đó được thực hiện. Như bạn sẽ thấy, mock object đưa ra một cách tiếp cận khác để xác nhận.&lt;br /&gt;
&lt;h3&gt;
Kiểm thử với đối tượng giả định (Mock Object)&lt;/h3&gt;
Bây giờ chúng ta sẽ tạo một kịch bản tương tự bằng cách sử dụng mock object. Trong trường hợp này, tôi sẽ sử dụng thư viện Moq (&lt;a href=&quot;http://code.google.com/p/moq/&quot;&gt;http://code.google.com/p/moq/&lt;/a&gt;) để định nghĩa mock.&lt;br /&gt;
&lt;pre class=&quot;brush: csharp;&quot;&gt;[TestFixture]
public class OrderInteractionTester
{
    private static String TALISKER = &quot;Talisker&quot;;

    [Test]
    public void TestFillingRemovesInventoryIfInStock()
    {

        //thiết lập - dữ liệu
        Order order = new Order(TALISKER, 50);

        //thiế lập - kỳ vọng
        var warehouseMock = new Mock&amp;lt;IWarehouse&amp;gt;();

        warehouseMock
            .Setup(warehouse =&amp;gt; warehouse.HasInventory(TALISKER, 50))
            .Returns(true);
        warehouseMock
            .Setup(warehouse =&amp;gt; warehouse.Remove(TALISKER, 50));

        //thực thi
        order.Fill(warehouseMock.Object);

        //xác nhận
        warehouseMock.Verify();

        Assert.IsTrue(order.IsFilled);
    }

    [Test]
    public void TestFillingDoesNotRemoveIfNotEnoughInStock()
    {

        Order order = new Order(TALISKER, 51);

        var warehouseMock = new Mock&amp;lt;IWarehouse&amp;gt;();
        warehouseMock
            .Setup(warehouse =&amp;gt; warehouse.HasInventory(TALISKER, 51))
            .Returns(false);

        order.Fill(warehouseMock.Object);

        warehouseMock.Verify();

        Assert.IsFalse(order.IsFilled);
    }
}&lt;/pre&gt;
Chúng ta hãy tập trung vào phân tích trường hợp kiểm thử TestFillingRemovesInventoryIfInStock.&lt;br /&gt;
Việc thiết lập (setup) cho trường hợp kiểm thử được được chia thành hai phần: dữ liệu và sự kỳ vọng. &lt;br /&gt;
Phần dữ liệu thiết lập các đối tượng SUT mà chúng ta sẽ tập trung vào làm việc với nó, phần này tương tự như bước thiết lập ở trên. Tuy nhiên, cộng tác viên lúc này không phải là một đối tượng warehouse thật, mà được thay thế bằng một mock warehouse giả định - một instance của class Mock. Phần thứ hai của việc thiết lập là tạo các kỳ vọng của mock object. Các kỳ vọng chỉ ra các phương thức được gọi từ các mock khi SUT được thực thi và giá trị trả về kỳ vọng của phương thức đó (tức là xem như các phương thức của mock luôn thực hiện đúng). &lt;br /&gt;
Toàn bộ các kỳ vọng đều được thực thi trong SUT. Sau khi thực thi tôi tiến hành xác minh hai khía cạnh. Tôi chạy các xác nhận dựa trên SUT như ở ví dụ trước. Tuy nhiên, tôi cũng xác minh cả mock - kiểm tra xem chúng được gọi dựa trên các kỳ vọng của chúng.&lt;br /&gt;
Điểm khác biệt chính ở đây là bằng cách nào chúng ta xác minh được đơn hàng đã được thực hiện đúng khi tương tác với kho hàng. Chúng ta xác minh trạng thái bằng cách dựa vào trạng thái của kho hàng. Mock sử dụng &lt;b&gt;xác nhận hành vi (behavior verification)&lt;/b&gt;, thay vì kiểm tra để thấy rằng đơn hàng tạo các lời gọi đúng đến kho hàng. Chúng ta thực hiện việc kiểm tra này bằng cách nói với mock cái mà chúng ta kỳ vọng khi thiết lập và yêu cầu mock tự xác minh lại sau đó. Chỉ có đơn hàng là cần được kiểm tra bằng cách sử dụng các lệnh xác nhận, và nếu phương thức không thay đổi trạng thái của đơn hàng thì không có gì thay đổi cả.&lt;br /&gt;
&lt;h3&gt;
Sự khác nhau giữ Mock và Stub&lt;/h3&gt;
Khi bạn đang thực hiện kiểm thử, lúc đó bạn đang tập trung vào một phần tử của phần mềm do đó thuật ngữ chung là unit testing (kiểm thử đơn vị). Vấn đề là khi bạn tạo một đơn vị công việc riêng lẻ, bạn thường cần các đơn vị khác - như trong ví dụ trên cần đến kho hàng.&lt;br /&gt;
Trong hai phương pháp kiểm thử ở trên, trường hợp đầu tiên sử dụng một đối tượng kho hàng thật và trong trường hợp thứ hai sử dụng một kho hàng giả định (mock), mà tất nhiên không phải là một đối tượng kho hàng thực tế. Sử dụng mock là một cách để không phải sử dụng các kho hàng thực tế khi kiểm thử, trong thực tế có nhiều kiểu đối tượng phi thực tế khác được sử dụng trong phương pháp kiểm thử này.&lt;br /&gt;
Có nhiều từ ngữ để mô tả về kiểu đối tượng này: stub, mock, fake, dummy. Trong bài này sẽ sử dụng từ vựng theo sách của Gerard Meszaros. Meszaros sử dụng thuật ngữ &lt;b&gt;Test Double (Kiểm thử đôi)&lt;/b&gt; như một thuật ngữ chung cho bất kỳ loại đối tượng phi thực tế nào thay thế cho đối tượng thật để phục vụ cho mục đích kiểm thử. Meszaros định nghĩa bốn từ ngữ cụ thể cho thuật ngữ này:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Dummy&lt;/b&gt; là các đối tượng được di chuyển khắp nơi nhưng không bao được sử dụng thật sự. Thường chúng chỉ được sử dụng để truyền danh sách tham số. &lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fake&lt;/b&gt; là các đối tượng thường được cài đặt một cách đầy đủ, nhưng thường tạo một số rút gọn dẫn đến chúng không phù hợp cho môi trường thật (một database in memory là một ví dụ tốt). &lt;/li&gt;
&lt;li&gt;&lt;b&gt;Stubs&lt;/b&gt; cung cấp hộp câu trả lời cho các cuộc gọi trong khi kiểm thử, thông thường không đáp lại bất kỳ thứ gì ngoài những gì đã được lập trình cho kiểm thử. Stubs cũng có thể ghi lại thông tin về các lệnh gọi, như một email gateway stub ghi nhớ các thông điệp nó &quot;đã gửi&quot;, hoặc có lẽ chỉ là có bao nhiêu thông điệp nó &#39;đã gửi&#39;. &lt;/li&gt;
&lt;li&gt;&lt;b&gt;Mocks&lt;/b&gt; là cái mà ta đang nói đến: là các đối tượng được lập trình trước với kỳ vọng tạo thành một đặc tả màcác lệnh gọi được mong chờ sẽ nhận được. &lt;/li&gt;
&lt;/ul&gt;
Trong các loại trên, chỉ có mock khẳng định tính xác nhận hành vi (behavior). Với những cái còn lại, thường thực hiện sử dụng xác nhận trạng thái. Mocks thường tương tự như các loại khác trong pha thực thi, chúng cần tạo cho SUT tin rằng nó đang nói chuyện với các cộng tác viên thật - nhưng mock khác trong các bước thiết lập và xác nhận.&lt;br /&gt;
Để minh họa cho vấn đề này, tôi sẽ tạo một ví dụ. Nhiều người chỉ sử dụng kiểm thử đôi nếu khó có thể làm việc được với đối tượng thực tế. Ví dụ đơn giản cho kiểm thử đôi là chúng ta sẽ gửi một email message nếu gặp thất bại khi tiến hành giao hàng cho đơn đặt hàng. Vấn đề là ta không muốn gửi các email thật tới khách hàng trong khi kiểm thử. Do đó ta tạo một kiểm thử đôi cho hệ thống email, ta có thể điều khiển và thao tác với nó.&lt;br /&gt;
Giờ ta có thể bắt đầu xem xét sự khác nhau giữa mock và stub. Nếu ta viết một kiểm thử cho hành vi của việ gửi mail, có lẽ đó chính là một stud đơn giản.&lt;br /&gt;
&lt;pre class=&quot;brush: csharp;&quot;&gt;public interface IMailService
{
    void Send(Message msg);

}
public class MailServiceStub : IMailService
{
    private IList&amp;lt;Message&amp;gt; messages = new List&amp;lt;Message&amp;gt;();

    public void Send(Message msg)
    {
        messages.Add(msg);
    }

    public int NumberSent()
    {
        return messages.Count;
    }
}&lt;/pre&gt;
Chúng ta có thể sử dụng stub để xác minh trạng thái như sau.&lt;br /&gt;
&lt;pre class=&quot;brush: csharp;&quot;&gt;[TestFixture]
public class OrderStateTester
{
    ...
    
    [Test]
    public void TestOrderSendsMailIfUnfilled()
    {
        Order order = new Order(TALISKER, 51);
        
        MailServiceStub mailer = new MailServiceStub();
        
        order.SetMailer(mailer);
        order.Fill(warehouse);

        Assert.IsTrue(1 == mailer.NumberSent());
    }
}&lt;/pre&gt;
Tất nhiên đây là một kiểm thử rất đơn giản – chỉ là việc gửi đi một message. Ở đây chúng ta cũng không thực hiện kiểm thử email được gửi đến đúng đối tượng không hoặc đúng nội dung gửi có đúng không, nhưng tôi nghĩ rằng trường hợp này cũng đủ để hình dung vấn đề.&lt;br /&gt;
Sử dụng mock để kiểm thử trong trường hợp này khá khác biệt.&lt;br /&gt;
&lt;pre class=&quot;brush: csharp;&quot;&gt;[TestFixture]
public class OrderInteractionTester
{
    ...
    
    [Test]
    public void TestOrderSendsMailIfUnfilled() {
        Order order = new Order(TALISKER, 51);

        var warehouseMock = new Mock&amp;lt;IWarehouse&amp;gt;();
        var mailServiceMock = new Mock&amp;lt;IMailService&amp;gt;();


        mailServiceMock
            .Setup(mailService =&amp;gt; mailService.Send(new Message()));

        warehouseMock
            .Setup(warehouse =&amp;gt; warehouse.HasInventory(It.IsAny&amp;lt;string&amp;gt;(), It.IsAny&amp;lt;int&amp;gt;()))
            .Returns(false);

        order.SetMailer(mailServiceMock.Object);

        order.Fill(warehouseMock.Object);
    }
}&lt;/pre&gt;
Trong cả hai trường hợp tôi sử dụng kiểm thử đôi thay vì sử dụng một dịch vụ mail thực tế. Có một điểm khác đó là stub sử dụng xác nhận trạng thái còn mock sử dụng xác nhận hành vi.&lt;br /&gt;
Để sử dụng xác nhận trạng thái trong stub, ta cần tạo một số phương thức mở rộng trong stub để giúp cho việc xác nhận. Do đó, stub cài đặt IMailService nhưng phải thêm một phương thức kiểm thử mở rộng là NumberSent.&lt;br /&gt;
&lt;h3&gt;
Kết luận&lt;/h3&gt;
Hy vọng rằng qua nội dung mô tả ở trên, bạn có thể phần nào hình dung được việc sử dụng Mock trong kiểm thử bằng Unit Test, cũng như phân biệt sự khác nhau giữa Mock và Stub.&lt;br /&gt;
Đây là bài được dịch sơ lược và diễn giải lại theo ý hiểu của tôi, bạn có thể thao khảo bài gốc bằng tiếng Anh từ địa chỉ &lt;a href=&quot;http://martinfowler.com/articles/mocksArentStubs.html&quot; target=&quot;_blank&quot;&gt;Mocks Aren&#39;t Stubs&lt;/a&gt;. Trong bài tiếp theo, tôi sẽ cố gắng giải thích với bạn về cách sử dụng Moq trong Unit Testing và TDD.</description><link>http://thanhhh.blogspot.com/2011/03/unit-testingmock-khong-phai-la-stub.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-6140482908638605009</guid><pubDate>Tue, 15 Mar 2011 03:33:00 +0000</pubDate><atom:updated>2011-03-15T10:34:40.154+07:00</atom:updated><title>Suy ngẫm từ lời nói của người Nhật</title><description>&lt;p&gt;Hôm nay, mình nhận được mail từ một khách hàng người Nhật, đất nước mà vừa trải qua một trận động đất 9 độ Richter và sống thần vào chiều ngày 11/3. Trong mail này, có đoạn họ viết về suy nghĩ của bản thân thật sự làm mình rất cảm động và cảm phục về ý chí và nghị lực của người Nhật. Và trông người lại ngẫm đến ta...&lt;/p&gt;  &lt;p&gt;Japanese:&lt;/p&gt;  &lt;p&gt;まだまだ混乱は続きそうですが、&lt;/p&gt;  &lt;p&gt;我々のように、直接の被害を受けなかった者が、&lt;/p&gt;  &lt;p&gt;可能な限り早く日常を取り戻すことが&lt;/p&gt;  &lt;p&gt;何より復興につながると思っています。&lt;/p&gt;  &lt;p&gt;Vietnamese:&lt;/p&gt;  &lt;p&gt;Có vẻ như tình trạng hỗn loạn này vẫn còn tiếp tục kéo dài.&lt;/p&gt;  &lt;p&gt;Nhưng những người không phải trực tiệp gánh chịu thiệt hại như chúng tôi sẽ cố gắng hết sức trong khả năng để có thể nhanh chóng trở lại nhịp sống ngày thường.&lt;/p&gt;  &lt;p&gt;Hơn bao giờ hết,tôi nghĩ điều đó sẽ giúp cho đất nươc chúng tôi nhanh chóng phục hồi.&lt;/p&gt;  </description><link>http://thanhhh.blogspot.com/2011/03/suy-ngam-tu-loi-noi-cua-nguoi-nhat.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-4271791413121216387</guid><pubDate>Sun, 13 Mar 2011 17:21:00 +0000</pubDate><atom:updated>2011-03-30T10:41:57.896+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C# 4</category><category domain="http://www.blogger.com/atom/ns#">DLR</category><title>Dynamic trong C# 4 - Phần 3</title><description>Trong phần này tôi sẽ tiếp tục nói đến việc tận dụng khả năng của DLR trong việc mở rộng khả năng của một lớp sẳn có.   &lt;br /&gt;
&lt;h3&gt;Mở rộng lớp đang tồn tại bằng DynamicObject&lt;/h3&gt;&lt;p&gt;Với việc sử dụng lớp DynamicObject, bạn có thể cung cấp cú pháp tốt hơn cho mã nguồn của bạn hoặc mở rộng khả năng của các thư viện đang tồn tại. Bạn có thể tận dụng các phương thức sẳn có của một class cùng với khả năng thêm các thuộc tính và phương thức một cách tự động tại thời điểm runtime mà không phải viết toàn bộ code này một cách rõ ràng.    &lt;br /&gt;
&lt;br /&gt;
Dưới đây là một ví dụ tận dụng khả năng của DynamicObject kết hợp với ADO.NET trong việc lấy dữ liệu bằng câu lệnh SELECT và lưu từng cột giá trị nhận được của mỗi dòng dữ liệu trong các đối tượng IDataRecord. &lt;/p&gt;&lt;p&gt;Để chạy được ví dụ dưới đây, bạn cần sử dụng AdventureWorks2008 Database sample có thể download từ địa chỉ: &lt;a href=&quot;http://msftdbprodsamples.codeplex.com/&quot; target=&quot;_blank&quot;&gt;Microsoft SQL Server Product Samples: Database&lt;/a&gt;.    &lt;br /&gt;
Thông thường, nếu bạn dùng ADO.NET để truy vấn và hiển thị dữ liệu như ví dụ sau đây: &lt;/p&gt;&lt;pre class=&quot;brush:csharp&quot;&gt;private static void ReadOrderData(string connectionString)
{
    string queryString = &amp;quot;SELECT [SalesOrderID], [CustomerID] FROM [Sales].[SalesOrderHeader];&amp;quot;;
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        SqlCommand command = new SqlCommand(queryString, connection);
        connection.Open();
        SqlDataReader reader = command.ExecuteReader();
        try
        {
            while (reader.Read())
            {
                Console.WriteLine(String.Format(&amp;quot;{0}, {1}&amp;quot;,
                    reader[0], reader[1]));
            }
        }
        finally
        {
            // Always call Close when done reading.
            reader.Close();
        }
    }
}&lt;/pre&gt;Trong ví dụ này, bạn thấy rằng để truy cập vào từng cột giá trị trong từng dòng đọc được, ta sẽ phải dùng reader[0] (hoặc reader[&amp;quot;SaleOrderID&amp;quot;]. Với việc mở rộng IDataRecord bằng DynamicObject, bạn có thể sử dụng reader.SaleOrderID, trong ví dụ dưới đây định nghĩa một lớp có tên DataRecordDynamic:   &lt;br /&gt;
&lt;pre class=&quot;brush: csharp;&quot;&gt;public class DataRecordDynamic : DynamicObject
{
    private IDataRecord m_dataRecord;

    public DataRecordDynamic(IDataRecord dataRecord)
    {
        m_dataRecord = dataRecord;
    }

    public override bool TryGetMember(
        GetMemberBinder binder, out object result)
    {
        result = m_dataRecord[binder.Name];
        return (result != null);
    }
}&lt;/pre&gt;Lớp này đã thực hiện định nghĩa lại phương thức xác định thao tác Get của một thuộc tính động, nó thực hiện xác định giá trị bằng cách sử dụng tên của thuộc tính xem như tên cột và lấy dữ liệu cột tương ứng với tên cột đó trong row dữ liệu nhận được (= Item[string]).   &lt;br /&gt;
Và dưới đây là ví dụ ban đầu đã được viết lại sử dụng lớp DataRecordDynamic, bạn sẽ thấy rằng bây giờ sẽ dùng row.OrderID và row.CustomerID.   &lt;br /&gt;
&lt;pre class=&quot;brush: csharp;&quot;&gt;private static IList&amp;lt;dynamic&amp;gt; ExecuteOrderDataReader(string connectionString)
{
    var orderDataRows = new List&amp;lt;dynamic&amp;gt;();

    string queryString = &amp;quot;SELECT [SalesOrderID], [CustomerID] FROM [Sales].[SalesOrderHeader];&amp;quot;;
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        SqlCommand command = new SqlCommand(queryString, connection);
        connection.Open();
        SqlDataReader reader = command.ExecuteReader();
        try
        {
            foreach (IDataRecord record in reader)
            {
                orderDataRows.Add(new DataRecordDynamic(record));
            }

            return orderDataRows;
        }
        finally
        {
            // Always call Close when done reading.
            reader.Close();
        }
    }
}

private static void ReadOrderData2(string connectionString)
{
    var orderDataRows = ExecuteOrderDataReader(connectionString);

    foreach (var row in orderDataRows)
    {
        Console.WriteLine(String.Format(&amp;quot;{0}, {1}&amp;quot;, row.SalesOrderID, row.CustomerID));
    }
}&lt;/pre&gt;Qua ví dụ trên, bạn cũng có thể thấy được sự khác nhau giữa ExpandoObject và DynamicObject, đó là DynamicObject còn cho phép bạn tận dụng được các phương thức, thao tác và thuộc tính sẳn có của một class.</description><link>http://thanhhh.blogspot.com/2011/03/dynamic-trong-c-4-phan-3.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-8373236713474293084</guid><pubDate>Sat, 12 Mar 2011 04:10:00 +0000</pubDate><atom:updated>2011-03-30T10:41:07.501+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C# 4</category><category domain="http://www.blogger.com/atom/ns#">DLR</category><title>Dynamic trong C# 4 - Phần 2</title><description>Đây là phần tiếp theo của các bài viết về từ khóa dynamic và Dynamic Language Runtime (DLR), đây là tính năng mới trong C# 4 và .NET Framework 4. Trong &lt;a href=&quot;http://thanhhh.blogspot.com/2011/03/dynamic-trong-c-4-phan-1.html&quot;&gt;phần 1&lt;/a&gt; đã giới thiệu về từ khóa dynamic, nội dung của bài này sẽ mô tả về DLR và hai trong số các trường hợp sử dụng thông thường của nó.&lt;br /&gt;
&lt;h3&gt;Dynamic Language Runtime&lt;/h3&gt;Thuật ngữ &quot;dynamic&quot; trong C# thường nhắc đến một trong hai khái niệm: từ khóa dynamic trong C# 4 hoặc là Dynamic Language Runtime (DLR).&lt;br /&gt;
&lt;br /&gt;
DLR phục vụ hai mục đích chính. Một là cho phép liên kết giữa ngôn ngữ động và .NET Framework serves two main goals. Và hai là mang khả năng động vào C# và Visual Basic.&lt;br /&gt;
&lt;br /&gt;
DLR đã được tạo ra dựa trên các bài học trong khi xây dựng IronPython (ironpython.net), là ngôn ngữ động đầu tiên được cài đặt trong .NET Framework. Trong khi làm việc với IronPython, nhóm đã tìm ra phương pháp sử dụng lại cài đặt của mình cho nhiều ngôn ngữ khác, vì vậy họ đã tạo ra một nền tảng cơ bản dùng chung cho các ngôn ngữ động trong .NET.&lt;br /&gt;
&lt;br /&gt;
Sau đó DLR đã được giới thiệu trong .NET Framework 4 để hỗ trợ các tính năng động trong C# và Visual Basic. Nếu bạn chỉ cần từ khóa dynamic trong C# 4, bạn chỉ cần sử .NET Framework và trong hầu hết trường hợp nó sẽ xử lý tất cả các tương tác với DLR trong chính bản thân mình. Nhưng nếu bạn muốn cài đặt hoặc mang một ngôn ngữ động mới vào .NET, bạn cần trợ giúp từ các lớp trợ giúp bên ngoài trong dự án nguồn mở (dlr.codeplex.com) có nhiều tính năng và dịch cho việc cài đặt ngôn ngữ.&lt;br /&gt;
&lt;h3&gt;Gọi động các phương thức và truyền các tham số&lt;/h3&gt;Đây không phải là sự kỳ vọng rằng các bạn nên sử dụng động bất kỳ khi nào có thể thay vì các khai báo kiểu tĩnh. Việc kiểm tra lúc biên dịch là một công cụ mạnh mẽ và nhiều lợi ích giúp bạn làm việc tốt hơn. Và hơn nữa, các đối tượng động trong C# không hỗ trợ IntelliSense, mà có thể làm năng suất công việc của bản giảm xuống.&lt;br /&gt;
&lt;br /&gt;
Sau đây là một ví dụ tuyệt vời cho việc sử dụng DLR, đó là việc thay thế các API reflection để triệu gọi đến các phương thức có truyền tham số.&lt;br /&gt;
&lt;br /&gt;
1. Bạn hãy sử dụng Visual Studio, tạo một ứng dụng C# Console Application có tên LateBindingWithDynamic. &lt;br /&gt;
2. Tiếp theo, bạn hãy thêm một Class Library project trong solution hiện tại với tên MathLibrary.&lt;br /&gt;
3. Đặt lại tên Class1.cs của MathLibrary thành SimpleMath.cs và cài đặt lớp như sau:&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;public class SimpleMath { 
    public int Add(int x, int y) 
    { return x + y; } 
}&lt;/pre&gt;4. Tiến hành build MathLibrary và copy MathLibrary.dll vào thư mục bin/Debug của LateBindingWithDynamic (Bạn có thể dùng tính năng ShowAllFiles trong VS và kéo thả MathLibrary.dll từ thư mục bin/Debug của MathLibrary vào thư mục bin/Debug của LateBindingWithDynamic).&lt;br /&gt;
5. Triệu gọi phương thức Add của thư viện MathLibrary trong LateBindingWithDynamic: Có hai phương pháp triệu gọi ở đây là sử dụng các API reflection và sử dụng từ khóa dynamic.&lt;br /&gt;
a/ Phương thức sau sẽ gọi phương thức Add sử dụng các triệu gọi API reflection:&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;private static void AddWithReflection()
{
    Assembly asm = Assembly.Load(&quot;MathLibrary&quot;);

    // Get metadata for the SimpleMath type. 
    Type math = asm.GetType(&quot;MathLibrary.SimpleMath&quot;);

    // Create a SimpleMath on the fly. 
    object obj = Activator.CreateInstance(math);

    // Get info for Add. 
    MethodInfo mi = math.GetMethod(&quot;Add&quot;);

    // Invoke method (with parameters). 
    object[] args = { 10, 70 };
    Console.WriteLine(&quot;Result is: {0}&quot;, mi.Invoke(obj, args));

}&lt;/pre&gt;b/ Trường hợp sử dụng từ khóa dynamic:&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;private static void AddWithDynamic()
{
    Assembly asm = Assembly.Load(&quot;MathLibrary&quot;);

    // Get metadata for the SimpleMath type. 
    Type math = asm.GetType(&quot;MathLibrary.SimpleMath&quot;);

    // Create a SimpleMath on the fly. 
    dynamic obj = Activator.CreateInstance(math);
    Console.WriteLine(&quot;Result is: {0}&quot;, obj.Add(10, 70));
}&lt;/pre&gt;&lt;br /&gt;
Trong hai trường hợp trên, bạn sẽ thấy được lợi điểm của việc sử dụng DLR, với việc sử dụng DLR giúp cho việc gọi phương thức và truyền các tham số trong đơn giản và dể hiểu hơn so với cách sử dụng API Reflection.&lt;br /&gt;
&lt;br /&gt;
&lt;h3&gt;Khai báo phương thức và thuộc tính động lúc thực thi (Dynamic Method Bags)&lt;/h3&gt;Một ví dụ khác mà của dynamic có thể làm được là tạo ra nơi chứa các phương thức động (method bag), tức là các đối tượng có thể thêm và xoá các thuộc tính và phương thức lúc thực thi, và sau đó triệu gọi các phương thức này thông qua triệu gọi động (dynamic dispatch) trong C#.&lt;br /&gt;
&lt;br /&gt;
Trong .NET Framework 4 có một namespace mới: System.Dynamic. Namespace này thực ra là một phần của DLR và được định nghĩa trong gói (assembly) System.Core. Các lớp System.Dynamic.ExpandoObject và System.Expando.DynamicObject cùng với từ khóa dynamic có thể giúp bạn tạo ra các cấu trúc (structure) động và hệ thống thứ bậc (hierarchic) động theo một cách đơn giản và dễ hiểu.&lt;br /&gt;
Sau đây là ví dụ diễn tả cách bạn có thể thêm một thuộc tính và phương thức bằng cách sử dụng lớp ExpandoObject như ví dụ dưới đây:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;dynamic expando = new ExpandoObject();
expando.SampleProperty = &quot;Đây là thuộc tính được thêm lúc thực thi&quot;;
expando.SampleMethod = (Action)(
    () =&gt; Console.WriteLine(expando.SampleProperty));
expando.SampleMethod();
&lt;/pre&gt;&lt;br /&gt;
Dưới đây là một ví dụ về việc đọc dữ liệu từ XML. Trong ví dụ này đã sử dụng class ExpandoObject để lưu trữ dữ liệu nhận được từ XML, bạn có thể truy cập dữ liệu này thông qua các thuộc tính tự động được thêm vào lúc đọc dữ liệu và thực hiện thêm phương thức GetFullName để mô tả khả năng đưa thêm phương thức trong lúc chạy chương trình.&lt;br /&gt;
Dữ liệu trong XML có cấu trúc như sau:&lt;br /&gt;
&lt;pre class=&quot;brush:xml&quot;&gt;&lt;persons&gt;
  &lt;person&gt;
    &lt;firstname&gt; Thanh &lt;/firstname&gt;
    &lt;lastname&gt; Ho Hai &lt;/lastname&gt;
  &lt;/person&gt;
  &lt;person&gt;
    &lt;firstname&gt; Phuong Linh &lt;/firstname&gt;
    &lt;lastname&gt; Ho Hoang &lt;/lastname&gt;
  &lt;/person&gt;
&lt;/persons&gt;
&lt;/pre&gt;Và dưới đây là toàn bộ đoạn code :&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;public static IList&amp;lt;dynamic&amp;gt; GetExpandoFromXml(String file)
{
    var persons = new List&amp;lt;dynamic&amp;gt;();

    var doc = XDocument.Load(file);
    var nodes =
        from node in doc.Root.Descendants(&amp;quot;Person&amp;quot;)
        select node;

    foreach (var n in nodes) {
        dynamic person = new ExpandoObject();

        //Đọc c&amp;#225;c gi&amp;#225; trị tương ứng với FirstName v&amp;#224; LastName của mỗi Person
        foreach (var child in n.Descendants()) {
            // Bởi v&amp;#236; ExpandoObject được c&amp;#224;i đặt interface IDictionary&amp;lt;string, object&amp;gt;
            // v&amp;#224; xem như mỗi phần tử của Dictionary gồm c&amp;#243; thuộc t&amp;#237;nh v&amp;#224; gi&amp;#225; trị
            var p = person as IDictionary&amp;lt;string, object&amp;gt;;

            //person được th&amp;#234;m 2 thuộc t&amp;#237;nh l&amp;#224; FirstName v&amp;#224; LastName
            p[child.Name.ToString()] = child.Value.Trim();
        }
        
        //C&amp;#224;i đặt phương thức lấy đầy đủ họ t&amp;#234;n cho person
        person.GetFullName = (Func&amp;lt;string&amp;gt;)(() =&amp;gt;
        {
            return string.Format(&amp;quot;{0}, {1}&amp;quot;, person.FirstName, person.LastName);
        });

        persons.Add(person);
    }

    return persons;
}
&lt;/pre&gt;Và bạn có thể truy cập dữ liệu đọc được ở trên bằng các thuộc tính và phương thức đã được thêm vào cho mỗi đối tượng ExpandoObject lúc thực thi như sau:&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;IList&amp;lt;dynamic&amp;gt; persons = GetExpandoFromXml(&amp;quot;Persons.xml&amp;quot;);
foreach (var p in persons)
{
    Console.WriteLine(p.GetFullName());
}
&lt;/pre&gt;Trên đây là hai trong số những kịch bản mà bạn có thể sử dụng DLR trong C# 4, trong những bài tiếp theo, tôi sẽ mô tả về các tính năng cao cấp khác của DLR.</description><link>http://thanhhh.blogspot.com/2011/03/tu-khoa-dynamic-trong-c-4-phan-2.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-7801744074644280933</guid><pubDate>Fri, 11 Mar 2011 09:12:00 +0000</pubDate><atom:updated>2011-03-30T10:42:45.265+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C# 4</category><category domain="http://www.blogger.com/atom/ns#">DLR</category><title>Dynamic trong C# 4 - Phần 1</title><description>Từ khóa dynamic và Dynamic Language Runtime (DLR) là những tính năng mới trong C# 4 và Microsoft .NET Framework 4. Bài viết này cung cấp những cái nhìn khái quát về tính năng dynamic trong C# 4.&lt;br /&gt;
&lt;h3&gt;Dynamic là gì?&lt;/h3&gt;C# và Java thường được xem là ngôn ngữ lập trình tĩnh, trong khi Python, Ruby và JavaScript là những ví dụ của ngôn ngữ động.&lt;br /&gt;
Thông thường, ngôn ngữ động không thực hiện kiểm tra kiểu tại thời điểm biên dịch chương trình và chỉ nhận dạng kiểu đối tượng tại thời điểm chạy chương trình. Điều này giúp cho việc viết code đơn giản và nhanh hơn, nhưng lúc đó bạn không nhận được lỗi biên dịch và chỉ đến khi thực hiện kiểm thử rất cẩn thận mới đảm bảo được chương trình bạn được viết chính xác.&lt;br /&gt;
Trong C# 4, từ khóa dynamic đã được bổ sung để cải thiện khả năng tương thích với các ngôn ngữ và nền tảng (framework) động..&lt;br /&gt;
Dưới đây là ví dụ về việc sử dụng từ khóa dynamic:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;static void ChangeDynamicDataType()
{
 dynamic t = &quot;Hello!&quot;;
 Console.WriteLine(&quot;t is of type: {0}&quot;, t.GetType());

 t = false;
 Console.WriteLine(&quot;t is of type: {0}&quot;, t.GetType());

 t = new System.Collections.Generic.List&lt;int&gt;();
 Console.WriteLine(&quot;t is of type: {0}&quot;, t.GetType());
}
&lt;/pre&gt;Output:&lt;br /&gt;
&lt;pre class=&quot;brush:text&quot;&gt;t is of type: System.String
t is of type: System.Boolean
t is of type: System.Collections.Generic.List`1[System.Int32]
&lt;/pre&gt;&lt;br /&gt;
Như bạn thấy, nó có thể gắn với các đối tượng có kiểu dữ liệu khác nhau vào biến được khai báo dynamic. Đoạn code được biên dịch bình thường và được định danh tại thời điểm chạy (runtime). Tuy nhiên, đoạn code sau sẽ được biên dịch bình thường, nhưng sẽ xuất hiện ngoại lệ (exception) tại thời điểm chạy:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;dynamic d = &quot;Hello!&quot;;

//Dòng sau sẽ xuất hiện ngoại lệ thời điểm chạy.
d++;
&lt;/pre&gt;Output:&lt;br /&gt;
&lt;pre class=&quot;brush:text&quot;&gt;Microsoft.CSharp.RuntimeBinder.RuntimeBinderException was unhandled
  Message=Operator &#39;++&#39; cannot be applied to operand of type &#39;string&#39;
  Source=Anonymously Hosted DynamicMethods Assembly
&lt;/pre&gt;&lt;br /&gt;
Bởi vì trình biên dịch không thể biết kiểu của đối tượng tại thời điểm chạy và do đó không thể nói với bạn rằng thao tác &#39;++&#39; không được hổ trợ trong trường hợp này.&lt;br /&gt;
&lt;h3&gt;Sử dụng từ khóa dynamic, object hay var?&lt;/h3&gt;Vậy sự khác nhau giữa dynamic, object và var như thế nào và khi nào bạn nên dùng chúng?&lt;br /&gt;
Từ khóa object mô tả cho kiểu System.Object, là lớp cha trên cùng trong phân cấp lớp C#, và có thể diễn tả cho bất cứ lớp nào khác. Nó được sử dụng khi không thể định danh được kiểu đối tượng tại thời điểm biên dịch và bạn cần sử dụng lệnh chuyển đổi sang một kiểu định nghĩa trước nếu muốn sử dụng các phương thức hoặc thuộc tính của kiểu đó.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;static void UseObjectVarible()
{
 // Giả sử có một lớp tên Person.
 object o = new Person() { FirstName = &quot;Thanh&quot;, LastName = &quot;Ho&quot; };

 // Phải đổi kiểu đối tượng thành Person
 // để có thể sử dụng các thuộc tính của Person.
 Console.WriteLine(&quot;Person&#39;s first name is {0}&quot;, ((Person)o).FirstName);
}
&lt;/pre&gt;&lt;br /&gt;
Từ khóa var được đưa ra từ C# 3.0, được sử dụng đại diện các kiểu đã được định nghĩa sẳn hoặc kiểu nặc danh. Một biến được khai báo var sẽ được định danh kiểu tại thời điểm biên dịch và không thể thay đổi kiểu lúc runtime. Nếu trình biên dịch không thể xác định được kiểu, nó sẽ tạo ra lỗi biên dịch:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;var varExample = 10;
Console.WriteLine(varExample.GetType()); // In ra System.Int32

//Biến varExample là kiểu System.Int32:
varExample = varExample + 10;

//Dòng này sẽ bị lỗi khi biên dịch và chỉ có thể gán giá trị số nguyên cho varExample:
varExample = &quot;test&quot;;
&lt;/pre&gt;&lt;br /&gt;
Từ khóa dynamic, được giới thiệu trong C# 4, tạo ra các kịch bản khác dựa trên từ khóa object truyền thống nhưng đơn giản hơn trong viết và maintain code. Thực tế, kiểu dynamic sử dụng kiểu  System.Object nhưng không như object, nó không đòi hỏi việc đổi kiểu lúc biên dịch, mà chỉ định danh thời điểm chạy:&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:csharp&quot;&gt;static void UseDynamicVarible()
{
 // Giả sử có một lớp tên Person.
 dynamic p = new Person() { FirstName = &quot;Thanh&quot;, LastName = &quot;Ho&quot; };

 //Có thể sử dụng các thuộc tính của Person mà không cần đổi kiểu.
 Console.WriteLine(&quot;Person&#39;s first name is {0}&quot;, p.FirstName);
}
&lt;/pre&gt;&lt;br /&gt;
Qua phần này, bạn có thể hiểu và sử dụng được từ khóa dynamic, một trong những tính năng mới của C# 4, Dynamic Language Runtime (DLR) sẽ được trình bày trong phần tiếp theo.</description><link>http://thanhhh.blogspot.com/2011/03/dynamic-trong-c-4-phan-1.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-741209938747166433</guid><pubDate>Wed, 23 Feb 2011 09:21:00 +0000</pubDate><atom:updated>2011-10-11T23:31:23.172+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ADO.NET</category><category domain="http://www.blogger.com/atom/ns#">C#</category><category domain="http://www.blogger.com/atom/ns#">SQL Server</category><title>Sử dụng Table-Valued Parameter trong SQL Server 2008 và ADO.NET</title><description>&lt;div&gt;
&lt;span style=&quot;-webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; border-collapse: separate; color: black; font-family: Tahoma; font-size: small; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;&quot;&gt;&lt;span style=&quot;-webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; border-collapse: separate; color: black; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;&quot;&gt;&lt;span style=&quot;font-family: Arial;&quot;&gt;Table-Valued Parameter đưa ra cho bạn một phương pháp đơn giản để truyền dữ liệu từ ứng dụng vào SQL Server mà không phải thực hiện các vòng lặp hoặc các xử lý dữ liệu logic phức tạp phía server. Bạn có thể chuyển toàn bộ các dòng dữ liệu trong ứng dụng và gửi về server chỉ bằng một tham số đơn của câu lệnh SQL.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;b&gt;Tạo kiểu Table-Valued Parameter trong SQL Server&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;b&gt;&lt;br /&gt;
&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;Kiểu dữ liệu Table-Valued Parameter được định nghĩa bằng cách sử dụng câu lệnh Transact-SQL CREATE TYPE.&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;Câu lệnh sau sẽ tiến hành tạo kiểu dữ liệu EmployeeTableType gồm có hai cột EmployeeID và EmployeeName:&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;brush: sql&quot;&gt;CREATE TYPE dbo.EmployeeTableType AS TABLE ( EmployeeID int, EmployeeName nvarchar(50) );
&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;b&gt;Tạo Stored Procedure sử dụng&amp;nbsp;Table-Valued Parameter&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;Ví dụ sau sẽ sử dụng Table-Valued Parameter để insert đồng thời nhiều dòng dữ liệu nhân viên vào table Employees:&lt;/span&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: sql&quot;&gt;CREATE PROCEDURE usp_InsertEmployees
(
     @Employees dbo.EmployeeTableType READONLY
)
AS
BEGIN

     INSERT INTO dbo.Employees
     SELECT EmployeeID, EmployeeName
     FROM @Employees

END
&lt;/pre&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;b&gt;Truyền một Table-Valued Parameter vào Stored Procedure sử dụng ADO.NET&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;Ví dụ sau viết bằng C# sẽ mô tả cho bạn thấy cách sử dụng các Stored Procedure có tham số là một Table-Valued. Trong ví dụ này sẽ sử dụng một phương thức tên là GetAddedEmployees() để lấy danh sách các nhân viên mà bạn đã nhập trong một GridView và trả về là kiểu DataTable. Các câu lệnh tiếp theo sẽ thực hiện việc truyền DataTable như một tham số cho Stored Procedure và thực hiện chỉ với một câu lệnh đơn giản.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;// Khởi tạo một kết nối SqlConnection.
using (SqlConnection connection = new SqlConnnection(&quot;...&quot;))
{
    // Lấy DataTable chứa dữ liệu nhân viên mới cần đưa vào Database
    DataTable addedEmployees = GetAddedEmployees();
        
    //Tạo câu lệnh insert
     SqlCommand insertCommand = new SqlCommand(
          &quot;usp_InsertCategories&quot;, connection);

     insertCommand.CommandType = CommandType.StoredProcedure;
   
     SqlParameter param = insertCommand.Parameters.AddWithValue(
          &quot;@Employees&quot;, addedEmployees);
     param.SqlDbType = SqlDbType.Structured;

     // Thực hiện lệnh insert dữ liệu.
     connection.Open();
     insertCommand.ExecuteNonQuery();
}
&lt;/pre&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: Arial;&quot;&gt;&lt;b&gt;Truyền một Table-Valued Parameter vào câu lệnh SQL&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;Trường hợp này sẽ là một ví dụ khác để mô tả cho bạn cách sử dụng trong trường hợp sử dụng một câu lệnh SQL trực tiếp thay vì dùng Stored Procedure.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush: csharp&quot;&gt;// Khởi tạo một kết nối SqlConnection.
using (SqlConnection connection = new SqlConnection(&quot;...&quot;))
{
 // Lấy DataTable chứa dữ liệu nhân viên mới cần đưa vào Database
 DataTable addedEmployees = GetAddedEmployees();

 // Định nghĩa câu lệnh INSERT
 string sqlInsert = 
  &quot;INSERT INTO dbo.Employees (EmployeeID, EmployeeName)&quot;
  + &quot; SELECT e.EmployeeID, e.EmployeeName&quot;
  + &quot; FROM @Employees AS e;&quot;;

 //Tạo câu lệnh INSERT
 SqlCommand insertCommand = new SqlCommand(
  sqlInsert, connection);
 
 SqlParameter param = insertCommand.Parameters.AddWithValue(
  &quot;@Employees&quot;, addedEmployees);
 //Khai báo kiểu cấu trúc và đặt kiểu Table-Valued
 param.SqlDbType = SqlDbType.Structured;
 param.TypeName = &quot;dbo.EmployeeTableType&quot;;

 //Thực thi câu lệnh
 connection.Open();
 insertCommand.ExecuteNonQuery();
}
&lt;/pre&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;&lt;b&gt;Kết luận&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: Arial;&quot;&gt;Table-Valued Parameter là một tính năng mới của SQL Server 2008 và .NET Framework 3.5. Bạn không thể sử dụng Table-Valued Parameter để gửi dữ liệu ra và cũng không chấp nhận tham số OUTPUT. Ngoài ra, bạn không thể sử dụng Table-Valued Parameter trong Function trong SQL Server.&lt;/span&gt;&lt;/div&gt;
</description><link>http://thanhhh.blogspot.com/2011/02/su-dung-table-valued-parameter-trong.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-9006391302086127776</guid><pubDate>Sat, 19 Feb 2011 01:43:00 +0000</pubDate><atom:updated>2011-02-19T08:43:57.747+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Interview Questions</category><category domain="http://www.blogger.com/atom/ns#">Software Engineer</category><title>What is SOLID?</title><description>&lt;p&gt;SOLID is a collection of best practices object-oriented design principles. The principles when applied together to make a system that is easy to maintain and extend over time. The principles of SOLID are guidelines that can be applied while working on software to remove code smells, to refactor the software&#39;s source code until it is both legible and extensible. It may be used with test-driven development, and is part of an overall strategy of agile and adaptive programming.&lt;/p&gt;  &lt;p&gt;SOLID is abbreviated by Single Responsibility, Open Closed, Liskov Substitution, Interface Segregation and Dependency Inversion.&lt;/p&gt;  &lt;p&gt;1. Single Responsibility Principle: A class or module should have one, and only one, reason to change. &lt;/p&gt;  &lt;p&gt;2. Open Closed Principle: Software Entities (Classes, Modules, Functions, and so on) should be open for extension but closed for modification.&lt;/p&gt;  &lt;p&gt;3. Liskov Substitution Principle: Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.&lt;/p&gt;  &lt;p&gt;4. Interface Segregation Principle: Many client specific interfaces are better than one general purpose interface.&lt;/p&gt;  &lt;p&gt;5. Dependency Inversion Principle:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;a. High level modules should not depend on low level modules. Both should depend on abstractions.&lt;/p&gt;    &lt;p&gt;b. Abstractions should not depend upon details. Details should depend upon abstractions.&lt;/p&gt;&lt;/blockquote&gt;  </description><link>http://thanhhh.blogspot.com/2011/02/what-is-solid.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-1989840863091820363</guid><pubDate>Thu, 13 May 2010 05:11:00 +0000</pubDate><atom:updated>2010-05-13T12:50:22.881+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Bing Maps</category><category domain="http://www.blogger.com/atom/ns#">Silverlight</category><title>Sử dụng Bing Maps trong Silverlight</title><description>&lt;p&gt;Việc sử dụng Bing Maps control trong các ứng dụng Silverlight rất đơn giản, bời vì Microsoft đã cung cấp cho chúng ta &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ee681884.aspx&quot;&gt;Bing Maps Silverlight Control SDK&lt;/a&gt; và bởi vì Silverlight là client-side, nên các truy vấn vào Bing Maps sẽ thông qua &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/cc980922.aspx&quot;&gt;Bing Maps Web Services SDK&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Trước khi bắt đầu việc phát triển ứng dụng Bing Maps Silverlight, bạn phải cài đặt các thành phần sau:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Microsoft Visual Studio 2008 SP1&lt;/p&gt; &lt;/blockquote&gt;  &lt;blockquote&gt;   &lt;p&gt;Microsoft Silverlight 3 Tools for Visual Studio 2008 SP1&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Trong bài viết này, tôi sẽ làm từng bước để bạn có thể sử dụng được Bing Maps trong Silverlight:&lt;/p&gt;  &lt;h4&gt;Tạo Bings Maps Key&lt;/h4&gt;  &lt;p&gt;1. Bạn hãy download Bing Maps Sỉverlight Control từ &lt;a href=&quot;http://www.microsoft.com/downloads/details.aspx?displaylang=en&amp;amp;FamilyID=beb29d27-6f0c-494f-b028-1e0e3187e830&quot;&gt;đây&lt;/a&gt; và tiến hành cài đặt.&lt;/p&gt;  &lt;p&gt;2. Tạo Bing Maps Developer account:&lt;/p&gt;  &lt;ul&gt;   &lt;ul&gt;     &lt;li&gt;Truy cập vào &lt;a href=&quot;https://www.bingmapsportal.com/&quot;&gt;Bing Maps Portal&lt;/a&gt; &lt;/li&gt;      &lt;li&gt;Tạo &lt;a href=&quot;https://login.live.com/login.srf?wa=wsignin1.0&amp;amp;wtrealm=urn:bing:maps:devportal&amp;amp;wctx=rm%3d0%26id%3dpassive%26ru%3d%252fapplication&amp;amp;wct=2010-05-13T04:35:38Z&quot;&gt;Windows Live ID&lt;/a&gt; &lt;/li&gt;      &lt;li&gt;Vào trang &lt;a href=&quot;https://www.bingmapsportal.com/application/index/1016790&quot;&gt;Create or view Bing Maps keys&lt;/a&gt; và nhập thông tin Application name (nhập tùy thích) và Application URL (bạn có thể nhập 1 URL tùy thích). &lt;/li&gt;      &lt;li&gt;Bing Maps sẽ cung cấp cho bạn Key để phát triển ứng dụng. &lt;/li&gt;   &lt;/ul&gt; &lt;/ul&gt;  &lt;h4&gt;&lt;/h4&gt;  &lt;h4&gt;&lt;/h4&gt;  &lt;h4&gt;&lt;/h4&gt;  &lt;h4&gt;Sử dụng Bing Maps Silverlight Control&lt;/h4&gt;  &lt;p&gt;1. Tạo ứng dụng Silverlight trong Visual Studio&lt;/p&gt;  &lt;p&gt;2. Hãy reference đến Microsoft.Maps.MapControl.dll và Microsoft.Maps.MapControl.Common.dll có trong thư mục C:\Program Files\Bing Maps Silverlight Control\V1\Libraries.&lt;/p&gt;  &lt;p&gt;3. Open MainPage.xml và thêm tham chiếu đến Bing Maps control như sau:&lt;/p&gt;  &lt;div style=&quot;padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px&quot; id=&quot;scid:812469c5-0cb0-4c63-8c15-c81123a09de7:a3e2ca4a-045c-4437-86ae-39936ba019a4&quot; class=&quot;wlWriterEditableSmartContent&quot;&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;xmlns:maps=&quot;clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl&quot;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;4. Tiếp theo, bạn hãy đưa Bing Maps control vào:&lt;/p&gt;

&lt;div style=&quot;padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px&quot; id=&quot;scid:812469c5-0cb0-4c63-8c15-c81123a09de7:bd412a2b-cf98-4acd-b072-4261300bcf9c&quot; class=&quot;wlWriterEditableSmartContent&quot;&gt;&lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;&amp;lt;Grid x:Name=&quot;LayoutRoot&quot;&amp;gt;
	&amp;lt;maps:Map x:Name=&quot;myMap&quot; CredentialsProvider=&quot;[Bing Maps Key]&quot; /&amp;gt;
&amp;lt;/Grid&amp;gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Chú ý: [Bing Map Key] chính là key đã được cung cấp bởi Bing Maps Portal ở trên.&lt;/p&gt;

&lt;p&gt;5. Khi chạy thử ứng dụng, bạn sẽ có kết quả như sau:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://lh3.ggpht.com/_06Xqd8BIw_M/S-uTAsjVRyI/AAAAAAAABME/jw0kpf0ZuC8/s1600-h/BingMapsSample1%5B4%5D.png&quot;&gt;&lt;img style=&quot;border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px&quot; title=&quot;BingMapsSample1&quot; border=&quot;0&quot; alt=&quot;BingMapsSample1&quot; src=&quot;http://lh3.ggpht.com/_06Xqd8BIw_M/S-uTG4s0ozI/AAAAAAAABMI/-MAC5cBCyX4/BingMapsSample1_thumb%5B2%5D.png?imgmax=800&quot; width=&quot;425&quot; height=&quot;358&quot; /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Như vậy, bạn đã có thể sử dụng được Bing Maps trong Silverlight, ở bài tiếp theo, tôi sẽ nói về việc sử dụng Bing Maps Web Services SDK để truy vấn các thông tin và hiển thị lên Bing Maps controls.&lt;/p&gt;  </description><link>http://thanhhh.blogspot.com/2010/05/su-dung-bing-maps-trong-silverlight.html</link><author>noreply@blogger.com (Anonymous)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh3.ggpht.com/_06Xqd8BIw_M/S-uTG4s0ozI/AAAAAAAABMI/-MAC5cBCyX4/s72-c/BingMapsSample1_thumb%5B2%5D.png?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-659505766345185145</guid><pubDate>Fri, 05 Mar 2010 03:32:00 +0000</pubDate><atom:updated>2010-03-05T11:04:59.769+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Prism</category><category domain="http://www.blogger.com/atom/ns#">Silverlight</category><category domain="http://www.blogger.com/atom/ns#">WCF Ria Service</category><title>How to EndEdit before changes can be submitted with Entity Ria Service</title><description>&lt;p&gt;In my application, i am using Silverlight, Prism and WCF Ria Service, when user ended edit a cell, value changes will be saved to server.&lt;/p&gt;  &lt;p&gt;In the View, i used SelectedItem binding to track change of the entity:&lt;/p&gt;  &lt;p&gt;&amp;lt;telerikGridView:RadGridView    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; ItemsSource=&amp;quot;{Binding Departments}&amp;quot;    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; SelectedItem=&amp;quot;{Binding Department, Mode=TwoWay}&amp;quot;    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; ...    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;gt;    &lt;br /&gt;&amp;lt;/telerikGridView:RadGridView&amp;gt;&lt;/p&gt;  &lt;p&gt;In the ViewModel, that is&lt;/p&gt;  &lt;pre class=&quot;brush: csharp&quot;&gt;public DepartmentEntity Department
{
	get { return department;}
	set { 
		department = value; 
        FirePropertyChanged(&amp;quot;Department&amp;quot;);
		
		if (department != value)
		{
			department.PropertyChanged += department_PropertyChanged;
		}
	}
}

void department_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
	if (e.PropertyName == &amp;quot;HasChanges&amp;quot; &amp;amp;&amp;amp; !context.IsSubmitting)
    {
        context.SubmitChanges();
	}
}&lt;/pre&gt;

&lt;p&gt;After i change value in a Cell of RadGridView, the following exception is thrown:&lt;/p&gt;

&lt;p&gt;&lt;font color=&quot;#ff0000&quot;&gt;Entity &#39;DepartmentEntity : 1&#39; is currently being edited and has uncommitted changes. A call to BeginEdit must be followed by a call to EndEdit or CancelEdit before changes can be submitted.&lt;/font&gt;&lt;/p&gt;

&lt;h4&gt;The solution:&lt;/h4&gt;

&lt;p&gt;Before context.SubmitChanges(), i inserted the following code lines:&lt;/p&gt;

&lt;pre class=&quot;brush: csharp&quot;&gt;IEditableObject editableObject = departmentEntity as IEditableObject;
editableObject.EndEdit();

context.SubmitChanges();&lt;/pre&gt;

&lt;p&gt;Now, the method for entity property changed is:&lt;/p&gt;

&lt;pre class=&quot;brush: csharp&quot;&gt;void department_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
	if (e.PropertyName == &amp;quot;HasChanges&amp;quot; &amp;amp;&amp;amp; !context.IsSubmitting)
    	{
		IEditableObject editableObject = ecranSelected as IEditableObject;
	        editableObject.EndEdit();

	        context.SubmitChanges();
	}
}&lt;/pre&gt;
And my application work fine. 

  </description><link>http://thanhhh.blogspot.com/2010/03/how-to-endedit-before-changes-can-be.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-9105527085418954629</guid><pubDate>Fri, 29 Jan 2010 17:10:00 +0000</pubDate><atom:updated>2010-07-16T15:15:20.960+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Silverlight</category><category domain="http://www.blogger.com/atom/ns#">WCF</category><title>Enabling cross-domain calls for Silverlight apps on WCF</title><description>&lt;h2&gt;&lt;/h2&gt;&lt;h3&gt;The problem&lt;/h3&gt;In my Silverlight application, it try to call a WCF web service hosted on the web server (IIS). When i run the Silverlight application, the follow exception was obtain:&lt;br /&gt;
“An error occurred while trying to make a request to URI &#39;&lt;a href=&quot;http://myserver/myservicehost/service1.svc&#39;&quot;&gt;http://myserver/myservicehost/service1.svc&#39;&lt;/a&gt;. This could be due to attempting to access a service in a cross-domain way without a proper cross-domain policy in place, or a policy that is unsuitable for SOAP services. You may need to contact the owner of the service to publish a cross-domain policy file and to ensure it allows SOAP-related HTTP headers to be sent. This error may also be caused by using internal types in the web service proxy without using the InternalsVisibleToAttribute attribute. Please see the inner exception for more details.”&lt;br /&gt;
&lt;h3&gt;Resolve&lt;/h3&gt;Create follow two xml files and add into C:\inetpub\wwwroot folder.&lt;br /&gt;
ClientAccessPolicy.xml&lt;br /&gt;
&lt;pre class=&quot;xml&quot; name=&quot;code&quot;&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&lt;access-policy&gt;
 &lt;cross-domain-access&gt;
  &lt;policy&gt;
   &lt;allow-from http-request-headers=&quot;*&quot;&gt;
    &lt;domain uri=&quot;*&quot;&gt;
   &lt;/domain&gt;&lt;/allow-from&gt;
   &lt;grant-to&gt;
    &lt;resource include-subpaths=&quot;true&quot; path=&quot;/&quot;&gt;
   &lt;/resource&gt;&lt;/grant-to&gt;
  &lt;/policy&gt;
 &lt;/cross-domain-access&gt;
&lt;/access-policy&gt;
&lt;/pre&gt;CrossDomain.xml&lt;br /&gt;
&lt;pre class=&quot;xml&quot; name=&quot;code&quot;&gt;&lt;cross-domain-policy&gt;
 &lt;allow-http-request-headers-from domain=&quot;*&quot; headers=&quot;*&quot;&gt;
 &lt;allow-http-request-headers-from domain=&quot;*&quot; headers=&quot;SOAPAction&quot;&gt;
&lt;/allow-http-request-headers-from&gt;&lt;/allow-http-request-headers-from&gt;&lt;/cross-domain-policy&gt;
&lt;/pre&gt;&lt;b&gt;Notice&lt;/b&gt;: You have to restart IIS and redeploy WCF service before using in Silverlight.</description><link>http://thanhhh.blogspot.com/2010/01/enabling-cross-domain-calls-for.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-567150806879660203</guid><pubDate>Tue, 08 Dec 2009 06:29:00 +0000</pubDate><atom:updated>2009-12-08T13:31:29.796+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL Server</category><title>How to configure SQL Server Linked Server to Oracle</title><description>&lt;p&gt;Nếu trong hệ thống của bạn sử dụng cả SQL Server và Oracle, thì đây là cách cho phép bạn thực hiện tạo 1 linked server từ SQL Server vào Oracle. Từ đó, bạn có thể thực hiện&amp;#160; các lệnh truy vấn trên Oracle và sử dụng dữ liệu nhận được trên SQL Server.&lt;/p&gt;  &lt;p&gt;Các bước thực hiện như sau:&lt;/p&gt;  &lt;h4&gt;Bước 1: Cài đặt Oracle client trên SQL Server host&lt;/h4&gt;  &lt;p&gt;Bạn có thể download trình cài đặt Oracle client từ trang web của Oracle. Bạn nên sử dụng Oracle client phiên bản tương tự phiên bản của Oracle server.&lt;/p&gt;  &lt;h4&gt;Bước 2: Cấu hình kết nối đến Oracle Instance&lt;/h4&gt;  &lt;p&gt;Sử dụng “Oracle Net Configuration Assistant” để cấu hình kết nối đến Oracle server, hoặc bạn có thể thêm cấu hình vào file &lt;strong&gt;tnsnames.ora&lt;/strong&gt;, format của cấu hình kết nối đến Oracle server như sau:&lt;/p&gt;  &lt;pre name=&quot;code&quot; class=&quot;xml&quot;&gt;
OracleServiceNameAlias = 
(DESCRIPTION = 
(ADDRESS_LIST = 
(ADDRESS = (PROTOCOL = TCP)(HOST = &lt;em&gt;your-host-name-or-ip&lt;/em&gt;)(PORT = &lt;em&gt;listener-port-usually-1521&lt;/em&gt;)) 
) 
(CONNECT_DATA = (SERVER = DEDICATED)(SERVICE_NAME = &lt;em&gt;oracle-service-name&lt;/em&gt;) 
) 
)
&lt;/pre&gt;

&lt;p&gt;Trong đó: &lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;OracleServiceNameAlias: Do bạn tự đặt, được sử dụng như là Data Source khi cấu hình Linked Server. &lt;/li&gt;

  &lt;li&gt;your-host-name-or-ip: Là hostname hoặc địa chỉ ip của Oracle server mà bạn kết nối đến. &lt;/li&gt;

  &lt;li&gt;listener-port-usually-1521: Là port của Oracle server (cổng ngầm đinh của Oracle là 1521). &lt;/li&gt;

  &lt;li&gt;oracle-service-name: là service name của Oracle server &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Bước 3: Test cấu hình đã được thiết lập&lt;/h4&gt;

&lt;p&gt;Bạn hãy dùng Command Prompt, đánh vào dòng lệnh sau: tnsping OracleServiceNameAlias&lt;/p&gt;

&lt;p&gt;Ví dụ:
&lt;pre name=&quot;code&quot; class=&quot;c#&quot;&gt;
 C:\&gt;tnsping OralceServiceNameAlias &lt;Enter&gt;
...
Used TNSNAMES adapter to resolve the alias 
Attempting to contact (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521)))(CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = OralceServiceNameAlias))) 
OK (10 msec)
&lt;/pre&gt;

&lt;p&gt;Khi bạn thấy dòng trên, thì bạn cấu hình đã OK.&lt;/p&gt;

&lt;h4&gt;Bước 4: Create link from SQL Server to Oracle&lt;/h4&gt;

&lt;p&gt;Bạn sử dụng SQL Server Management Studio và chạy các câu lệnh sau:&lt;/p&gt;

&lt;pre name=&quot;code&quot; class=&quot;sql&quot;&gt;
EXEC sp_addlinkedserver @server = &#39;MyOracleLink&#39;, @srvproduct = &#39;Oracle&#39;, @provider = &#39;MSDAORA&#39;, @datasrc = &#39;OralceServiceNameAlias&#39; 
EXEC sp_addlinkedsrvlogin &#39;MyOracleLink&#39;, false, &#39;NULL&#39;, &#39;ora_username&#39;, &#39;ora_password&#39;
&lt;/pre&gt;

&lt;p&gt;Trong đó:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;sp_addlinkedserver thực hiện đăng ký link server vào SQL Server &lt;/li&gt;

  &lt;li&gt;@server=Tên của Linked Server, tên này phải chưa tồn tại trong danh sách Linked Servers &lt;/li&gt;

  &lt;li&gt;@srvproduct= Tên của data source kết nối đến (tùy chọn) &lt;/li&gt;

  &lt;li&gt;@provider = Provider sẽ sử dụng, ở đây MSDAORA = Microsoft OLE DB Provider for Oracle &lt;/li&gt;

  &lt;li&gt;@datasrc&amp;#160; = Tên của Oracle Instance Alias mà bạn đã cấu hình ở bước 2. &lt;/li&gt;

  &lt;li&gt;sp_addlinkedsrvlogin đăng ký account đăng nhập vào Oracle, sử dụng account ora_username/ora_password. &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Bước 5: Tạo câu lệnh để test dữ liệu&lt;/h4&gt;

&lt;pre name=&quot;code&quot; class=&quot;sql&quot;&gt;SELECT * FROM OPENQUERY(MyOracleLink, &#39;SELECT * FROM TableTest&#39;)&lt;/pre&gt;

&lt;p&gt;Như vậy, với câu lệnh trên thực hiện được, thì quá trình cấu hình Linked Server đã hoàn tất.&lt;/p&gt;  </description><link>http://thanhhh.blogspot.com/2009/12/how-to-configure-sql-server-linked.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-7134550932251343098</guid><pubDate>Tue, 01 Dec 2009 15:38:00 +0000</pubDate><atom:updated>2009-12-01T22:41:48.447+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL Server</category><title>Reset Identity Column in SQL Server</title><description>&lt;p&gt;Khi thiết kế cơ sở dữ liệu, bạn có thể thiết lập một cột trong bảng với thuộc tính Is Identity = true. SQL Server sẽ tự động thiết lập giá trị tăng tự động cho cột đó.&lt;/p&gt;  &lt;p&gt;Tuy nhiên, sau một thời gian kiểm thử database, bạn muốn reset lại giá trị cho cột này về một giá trị nào đó bạn mong muốn, thì câu lệnh sau cho phép bạn thực hiện việc này.&lt;/p&gt;  &lt;pre name=&quot;code&quot; class=&quot;sql&quot;&gt;
USE [TenDatabase];

DBCC CHECKIDENT(TenBang, RESEED, Giatri);
&lt;/pre&gt;

&lt;p&gt;Trong đó:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt; TenBang là tên của bảng có chứ Identity Column mà bạn muốn thiết lập lại giá trị.&lt;/p&gt;

  &lt;p&gt;Giatri là giá trị mà bạn khởi tạo. Nếu bạn đặt giá trị này bằng 0, thì row được insert ngay sau đó sẽ có giá trị là 1 với cột tự tăng này. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Chú ý rằng việc insert sau đó có thể gặp lỗi nếu bạn thiết lập giá trị khởi tạo đến giá trị nhỏ hơn hoặc bằng giá trị lớn nhất đang tồn tại trên cột tự tăng của bảng đó.&lt;/p&gt;  </description><link>http://thanhhh.blogspot.com/2009/12/reset-identity-column-in-sql-server.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-2867483011563284200</guid><pubDate>Thu, 13 Aug 2009 01:59:00 +0000</pubDate><atom:updated>2009-08-13T09:01:15.701+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">SQL Server</category><title>Cách giải quyết Concurrency và xung đột khi tạo khóa tự động trong SQL Server</title><description>&lt;h4&gt;Yêu cầu&lt;/h4&gt;  &lt;p&gt;Trong yêu cầu thực tế của dự án mà tôi gặp phải, đó là trong trường hợp tạo mã khách hàng trong dữ liệu khách hàng, chúng tôi cần phải đảm bảo các yêu cầu sau:&lt;/p&gt;  &lt;p&gt;1. Mã khách hàng được sinh tự động gồm 13 ký tự số.&lt;/p&gt;  &lt;p&gt;2. Mã khách hàng được đánh theo nguyên tắc: [xxx]{0}[Số thứ tự tăng dần]&lt;/p&gt;  &lt;p&gt;Trong đó: [xxx] là 3 ký tự số quy định tùy theo nơi đăng ký sẽ khách nhau; {0} là được chèn vào để đảm bảo mã khách hàng luôn là 13 ký tự số; [Số thứ tự tăng dần] là số tăng dần bắt đầu từ 1.&lt;/p&gt;  &lt;h4&gt;Phân tích&lt;/h4&gt;  &lt;p&gt;Rõ ràng, với [Số thứ tự tăng dần] thì chúng tôi đơn giản tạo 1 cột số nguyên và sử dụng tính năng Auto-increment của SQL Server. Với mã khách hàng của khách hàng mới, thì sẽ thực hiện lần lượt các công việc:&lt;/p&gt;  &lt;p&gt;1. SELECT [Số thứ tự tăng dần] từ bảng Customers&lt;/p&gt;  &lt;p&gt;2. [Mã khách hàng mới] = [Số thứ tự tăng dần] + 1&lt;/p&gt;  &lt;p&gt;2. Gán các số 0 vào đầu [Mã khách hàng mới] để đủ 10 ký tự số&lt;/p&gt;  &lt;p&gt;3. Gán 3 ký tự số [xxx] cho sẳn vào đầu [Mã khách hàng mới] &lt;/p&gt;  &lt;p&gt;4. INSERT [Mã khách hàng mới] vào cơ sở dữ liệu&lt;/p&gt;  &lt;p&gt;Vậy, việc giải quyết vấn đề Concurrency và xung đột sẽ như thế nào cần được đặt ra.&lt;/p&gt;  &lt;h4&gt;Cách giải quyết vấn đề&lt;/h4&gt;  &lt;p&gt;Có một cách giải quyết vấn đề đơn giản và hiệu quả là sử dụng lời gợi ý (hint) UPDLOCK. Khóa UPDATE là một shared lock mà sẽ không khóa các lệnh đọc khác. Tuy nhiên, tại một thời điểm chỉ có một xử lý (process) được giữ UPDLOCK trên 1 dòng (row), vì vậy nếu đồng thời có 2 xử lý song song đồng thời, thì xử lý thứ hai sẽ phải chờ xử lý thứ nhất hoàn thành rồi mới tiến hành giữ khóa UPDATE để thực hiện. &lt;/p&gt;  &lt;p&gt;Và đây là đoạn code để mô tả cách giải quyết vấn đề trên:&lt;/p&gt;  &lt;div style=&quot;padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px&quot; id=&quot;scid:812469c5-0cb0-4c63-8c15-c81123a09de7:94f41e05-6f21-45df-9ac5-314436297447&quot; class=&quot;wlWriterEditableSmartContent&quot;&gt;&lt;pre name=&quot;code&quot; class=&quot;sql:firstline[1]&quot;&gt;BEGIN TRANSACTION

SELECT coalesce(MAX(CustomerIndex, 0)) + 1 FROM Customers WITH (UPDLOCK)

-- Thực hiện tạo Mã khách hàng mới dựa trên số thứ tự nhận được

INSERT Customers(...)

COMMIT TRANSACTION&lt;/pre&gt;&lt;/div&gt;  </description><link>http://thanhhh.blogspot.com/2009/08/cach-giai-quyet-concurrency-va-xung-ot.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-5439259781804887321</guid><pubDate>Sat, 08 Aug 2009 16:46:00 +0000</pubDate><atom:updated>2009-08-08T23:49:22.707+07:00</atom:updated><title>SQLite3 đã được viết bằng .NET</title><description>&lt;span xmlns=&#39;&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;Noah Hart đã viết lại SQLite3 bằng C#. Mặc dù bản đầu tiên này vẫn chậm hơn so với bản gốc, tuy nhiên dự án này đã mở ra cách mới cho việc sử dụng SQLite trong C# mà không phải sử dụng P/Invoke hoặc mã lệnh không-an-toàn (unsafe code).
&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;Dự án C#-SQLite được đưa trên trang &lt;a href=&#39;http://code.google.com/p/csharp-sqlite/&#39;&gt;Google Code&lt;/a&gt; với phiên bản của SQLite 3.6.16, mã nguồn này tuân thủ theo giấy phép (license)  &lt;a href=&#39;http://creativecommons.org/licenses/by/3.0/&#39;&gt;Create Common 3.0&lt;/a&gt;. C#-SQLite đã được kiểm thử với hơn 30,000 trường hợp test với chỉ có 9 trường hợp test vẫn chưa thực hiện được. Mã nhị phân hoàn chỉnh của nó là 528KB trong khi bản gốc là 506KB. Hiệu năng của nó là vẫn chậm hơn từ 3-5 lần so với bản gốc được cài đặt bằng C, nhưng mã nguồn C# hiện tại vẫn chưa được tối ưu hóa, và hiệu năng hiện tại vẫn ở mức chấp nhận được. Hiện tại, thì số dòng trên một giây được thể hiện ở bảng sau:
&lt;/span&gt;&lt;/p&gt;&lt;div style=&#39;margin-left: 25pt&#39;&gt;&lt;table border=&#39;0&#39; style=&#39;border-collapse:collapse&#39;&gt;&lt;colgroup&gt;&lt;col style=&#39;width:65px&#39;/&gt;&lt;col style=&#39;width:90px&#39;/&gt;&lt;col style=&#39;width:72px&#39;/&gt;&lt;/colgroup&gt;&lt;tbody valign=&#39;top&#39;&gt;&lt;tr&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  solid black 0.5pt; border-left:  solid black 0.5pt; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;&lt;strong&gt;Test&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  solid black 0.5pt; border-left:  none; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;&lt;strong&gt;C#-SQLite&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  solid black 0.5pt; border-left:  none; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;&lt;strong&gt;SQLite 3&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  solid black 0.5pt; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;Insert&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;300K&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;1300K&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  solid black 0.5pt; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;Select&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;1500K&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;8450K&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  solid black 0.5pt; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;Update&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;60K&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;300K&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  solid black 0.5pt; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;Delete&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;250K&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&#39;padding-left: 7px; padding-right: 7px; border-top:  none; border-left:  none; border-bottom:  solid black 0.5pt; border-right:  solid black 0.5pt&#39;&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;700K&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;p&gt;&lt;span style=&#39;font-family:Tahoma; font-size:9pt&#39;&gt;C#-SQLite tuy có hiệu năng chậm hơn so với SQLite được cài đặt trên C, tuy nhiên nó thật sự là sự lựa chọn tốt cho các lập trình viên C#, bởi vì tránh phải sử dụng P/Invoke bởi vì nó khá chậm và không linh hoạt. Một lý do khác nữa là mã nguồn C sử dụng lệnh goto trong hầu hết mọi nơi, có thể cản trở việc ngăn chặn các ngoại lệ.&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;</description><link>http://thanhhh.blogspot.com/2009/08/sqlite-uoc-ho-tro-ay-u-voi-net.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-739362223815960986</guid><pubDate>Tue, 14 Jul 2009 08:04:00 +0000</pubDate><atom:updated>2009-07-14T15:06:46.486+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">C#</category><category domain="http://www.blogger.com/atom/ns#">WinForms</category><title>Sử dụng SystemParametersInfo để thay đổi thiết lập giao diện</title><description>&lt;p&gt;&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ms724947%28VS.85,loband%29.aspx&quot;&gt;Hàm SystemParametersInfo&lt;/a&gt;&lt;code&gt;&lt;/code&gt; cho phép bạn truy cập và thay đổi hầu hết các thiết lập giao diện người sử dụng ở mức thấp (màn hình Display Properties của Windows). &lt;/p&gt;  &lt;p&gt;Bạn có thể đọc chi tiết trên MSDN, ở đây tôi chỉ đưa một số ví dụ cơ bản: &lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;SPI_GETICONTITLELOGFONT giúp bạn lấy thông tin font chữ được sử dụng cho các nhãn icon; SPI_SETICONTITLELOGFONT giúp bạn thay đổi thông tin này. &lt;/li&gt;    &lt;li&gt;SPI_GETNONCLIENTMETRICS giúp bạn lấy thông tin về font chữ được sử dụng trong các màn hình của Window bao gồm captions, menus, status bars, và message boxes; SPI_SETNONCLIENTMETRICS giúp bạn thay đổi chúng. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Và một số thiết lập trong Control Panel như: &lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;SPI_SETKEYBOARDDELAY và SPI_SETKEYBOARDSPEED giúp bạn thiết lập các tham số của keyboard. &lt;/li&gt;    &lt;li&gt;SPI_SETDOUBLECLICKTIME giúp bạn thiết lập tốc độ double-click của con chuột. &lt;/li&gt;    &lt;li&gt;SPI_SETMENUFADE giúp bạn thiết lập hoặc vô hiệu hóa menu fade animation. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Sau đây, tôi sẽ viết một đoạn chương trình demo để thay đổi thiết lập font chữ của MessageBox trên Windows. &lt;/p&gt;  &lt;pre class=&quot;brush: csharp;&quot;&gt;    public class WindowsUISettings
    {
        // Khai báo sử dụng API SystemParametersInfo
        [DllImport(&amp;quot;user32.dll&amp;quot;, CharSet = CharSet.Auto)]
        private static extern bool SystemParametersInfo(
            int Action, int uiParam, [In, Out] NONCLIENTMETRICS pvParam, int WinIni);

        [StructLayout(LayoutKind.Sequential)]
        private class NONCLIENTMETRICS
        {
            public int cbSize = Marshal.SizeOf(typeof(NONCLIENTMETRICS));
            public int iBorderWidth;
            public int iScrollWidth;
            public int iScrollHeight;
            public int iCaptionWidth;
            public int iCaptionHeight;
            [MarshalAs(UnmanagedType.Struct)]
            public LOGFONT lfCaptionFont;
            public int iSmCaptionWidth;
            public int iSmCaptionHeight;
            [MarshalAs(UnmanagedType.Struct)]
            public LOGFONT lfSmCaptionFont;
            public int iMenuWidth;
            public int iMenuHeight;
            [MarshalAs(UnmanagedType.Struct)]
            public LOGFONT lfMenuFont;
            [MarshalAs(UnmanagedType.Struct)]
            public LOGFONT lfStatusFont;
            [MarshalAs(UnmanagedType.Struct)]
            public LOGFONT lfMessageFont;
        }


        private const int LF_FACESIZE = 32;

        // A &amp;quot;logical font&amp;quot; used by old-school windows
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private class LOGFONT
        {
            public int lfHeight;
            public int lfWidth;
            public int lfEscapement;
            public int lfOrientation;
            public int lfWeight;
            public byte lfItalic;
            public byte lfUnderline;
            public byte lfStrikeOut;
            public byte lfCharSet;
            public byte lfOutPrecision;
            public byte lfClipPrecision;
            public byte lfQuality;
            public byte lfPitchAndFamily;
            
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LF_FACESIZE)]
            public string lfFaceName;

            // to shut it up about the warnings
            public LOGFONT(string lfFaceName)
            {
                this.lfFaceName = lfFaceName;
                lfHeight = lfWidth = lfEscapement = lfOrientation = lfWeight = 0;
                lfItalic = lfUnderline = lfStrikeOut = lfCharSet = lfOutPrecision
                = lfClipPrecision = lfQuality = lfPitchAndFamily = 0;
            }
        }

        //Khai báo SPI lấy và thay đổi thiết lập về 
        //caption, menu, status bar, scroll bar và message box 
        private const int SPI_GETNONCLIENTMETRICS = 0x0029;
        private const int SPI_SETNONCLIENTMETRICS = 0x002A;

        private NONCLIENTMETRICS currentMetrics;

        /// &amp;lt;summary&amp;gt;
        /// Lưu giữ lại các giá trị ngầm định
        /// &amp;lt;/summary&amp;gt;
        private static NONCLIENTMETRICS defaultMetrics;

        private WindowsUISettings()
        { }

        public static WindowsUISettings CurrentMetrics()
        {
            //Lưu giữ lại các giá trị ngầm định
            defaultMetrics = new NONCLIENTMETRICS();
            SystemParametersInfo(SPI_GETNONCLIENTMETRICS, defaultMetrics.cbSize, defaultMetrics, 0);

            NONCLIENTMETRICS pvParam = new NONCLIENTMETRICS();
            SystemParametersInfo(SPI_GETNONCLIENTMETRICS, pvParam.cbSize, pvParam, 0);

            WindowsUISettings instance = new WindowsUISettings();
            instance.currentMetrics = pvParam;

            return instance;
        }

        /// &amp;lt;summary&amp;gt;
        /// Lấy/Thiết lập font chữ cho MessageBox
        /// &amp;lt;/summary&amp;gt;
        public Font MessageFont
        {
            get
            {
                return Font.FromLogFont(currentMetrics.lfMessageFont);
            }
            set
            {
                value.ToLogFont(currentMetrics.lfMessageFont);
            }
        }

        /// &amp;lt;summary&amp;gt;
        /// Thiết lập các thay đổi
        /// &amp;lt;/summary&amp;gt;
        public void Apply()
        {
            SystemParametersInfo(SPI_SETNONCLIENTMETRICS, currentMetrics.cbSize, currentMetrics, 1);
        }

        /// &amp;lt;summary&amp;gt;
        /// Đặt lại các giá trị ngầm định
        /// &amp;lt;/summary&amp;gt;
        public void RestoreDefault()
        {
            SystemParametersInfo(SPI_SETNONCLIENTMETRICS, defaultMetrics.cbSize, defaultMetrics, 1);
        }
    }&lt;/pre&gt;

&lt;p&gt;Và đây là ví dụ chạy thử:&lt;/p&gt;

&lt;p&gt;Hãy tạo 1 form mới, trên đó có 2 button bao gồm, button1 và button2. Double-click vào button1 và sau đó button2, ví dụ xử lý sự kiện Click trên 2 button sẽ như sau:&lt;/p&gt;

&lt;pre class=&quot;brush: csharp;&quot;&gt;    public partial class Form1 : Form
    {
        WindowsUISettings metrics 
            = WindowsUISettings.CurrentMetrics();

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Font messageFont = new Font(
                metrics.MessageFont.FontFamily, 14);

            metrics.MessageFont = messageFont;
            metrics.Apply();

            MessageBox.Show(
            &amp;quot;MessageBox đã được thay đổi font size thành 14.&amp;quot;);
        }

        private void button2_Click(object sender, EventArgs e)
        {
            metrics.RestoreDefault();
            MessageBox.Show(
            &amp;quot;MessageBox đã được trở về font size 8.&amp;quot;);

        }
    }&lt;/pre&gt;
Qua ví dụ này, chắc các bạn đã hiểu cách để lấy thông tin và thay đổi các thông số khác.  </description><link>http://thanhhh.blogspot.com/2009/07/su-dung-systemparametersinfo-e-thay-oi.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-3604235795204880034</guid><pubDate>Mon, 06 Jul 2009 04:53:00 +0000</pubDate><atom:updated>2009-07-06T11:57:24.896+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">Crystal Report</category><category domain="http://www.blogger.com/atom/ns#">Windows 7</category><title>Cách chạy Crystal Report trên Windows Vista/7</title><description>&lt;h4&gt;Giới thiệu&lt;/h4&gt;  &lt;p&gt;Khi tôi chạy chương trình để hiển thị các báo cáo sử dụng Crystal Report XI trên Windows Vista hoặc Windows 7 thì luôn nhận được thông báo lỗi:&lt;/p&gt;  &lt;pre class=&quot;brush: bash;&quot;&gt;Description:
  Stopped working

Problem signature:
  Problem Event Name:    APPCRASH
  Application Name:    CRXISample.exe
  Application Version:    1.0.0.0
  Application Timestamp:    4a517191
  Fault Module Name:    unknown
  Fault Module Version:    0.0.0.0
  Fault Module Timestamp:    00000000
  Exception Code:    c0000005
  Exception Offset:    04b978c8
  OS Version:    6.1.7100.2.0.0.256.1
  Locale ID:    1041&lt;/pre&gt;

&lt;h4&gt;Mô tả lỗi&lt;/h4&gt;

&lt;p&gt;Lỗi này xuất hiện do tính năng DEP (Data Execution Prevention) trên Windows Vista/7 khi được kích hoạt (enabled) thì sẽ ngăn chặn (block) tất cả các chương trình&amp;#160; đang cố gắng chạy mà không nằm trong danh sách tin cậy (trusted list).&lt;/p&gt;

&lt;h4&gt;Khắc phục lỗi&lt;/h4&gt;

&lt;p&gt;Lỗi này có thể được khắc phục bằng cách vô hiệu hóa tính năng DEP, tuy nhiên khi tính năng này được khóa thì có thể dẫn đến một số nguy hại đối với máy tính của bạn. Do đó, ở đây tôi sẽ đưa ra cách vô hiệu hóa tính năng này và có thể kích hoạt tính năng này nếu cần thiết.&lt;/p&gt;

&lt;p&gt;Hình sau minh họa khi kích hoạt DEP và khi vô hiệu hóa DEP, ngầm định là DEP được kích hoạt.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://lh4.ggpht.com/_06Xqd8BIw_M/SlGDVOI6EEI/AAAAAAAAA-M/T1aZtD3PAnc/s1600-h/DEP_Enabled%5B5%5D.jpg&quot;&gt;&lt;img style=&quot;border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px&quot; title=&quot;DEP_Enabled&quot; border=&quot;0&quot; alt=&quot;DEP_Enabled&quot; src=&quot;http://lh6.ggpht.com/_06Xqd8BIw_M/SlGDWFW4T_I/AAAAAAAAA-Q/4ypJShYwTeg/DEP_Enabled_thumb%5B3%5D.jpg?imgmax=800&quot; width=&quot;283&quot; height=&quot;419&quot; /&gt;&lt;/a&gt; &lt;a href=&quot;http://lh3.ggpht.com/_06Xqd8BIw_M/SlGDXTM0d_I/AAAAAAAAA-U/L6AA-MfWDXU/s1600-h/DEP_Disabled%5B5%5D.jpg&quot;&gt;&lt;img style=&quot;border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px&quot; title=&quot;DEP_Disabled&quot; border=&quot;0&quot; alt=&quot;DEP_Disabled&quot; src=&quot;http://lh4.ggpht.com/_06Xqd8BIw_M/SlGDYn0AY-I/AAAAAAAAA-Y/8n2eLsJ8Nwk/DEP_Disabled_thumb%5B3%5D.jpg?imgmax=800&quot; width=&quot;286&quot; height=&quot;419&quot; /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h5&gt;Kích hoạt DEP (Enabled)&lt;/h5&gt;

&lt;p&gt;1. Mở màn hình Command Prompt: Nhấn tổ hợp phím Windows+R; nhấn cmd và Enter.&lt;/p&gt;

&lt;p&gt;2. Đánh dòng lệnh &lt;b&gt;bcdedit.exe /set {current} nx OptIn&lt;/b&gt; và nhấn &lt;b&gt;Enter.&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;3. Bạn có thể nhận được thông điệp báo thành công.&lt;/p&gt;

&lt;p&gt;4. Khởi động lại máy.&lt;/p&gt;

&lt;h5&gt;Vô hiệu hóa DEP (Disabled)&lt;/h5&gt;

&lt;p&gt;1. Mở màn hình Command Prompt: Nhấn tổ hợp phím Windows+R; nhấn cmd và Enter.&lt;/p&gt;

&lt;p&gt;2. Đánh dòng lệnh &lt;b&gt;bcdedit.exe /set {current} nx &lt;b&gt;AlwaysOff&lt;/b&gt;&lt;/b&gt; và nhấn &lt;b&gt;Enter.&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;3. Bạn có thể nhận được thông điệp báo thành công.&lt;/p&gt;

&lt;p&gt;4. Khởi động lại máy.&lt;/p&gt;

&lt;h4&gt;Kết luận&lt;/h4&gt;

&lt;p&gt;Cách vô hiệu hóa DEP có thể giúp nhiều chương trình chạy tốt trên môi trường Windows Vista hoặc Windows 7, tuy nhiên nó cũng có thể đem đến rắc rối với virus. Do đó, bạn nên cẩn thận khi sử dụng cách này.&lt;/p&gt;

&lt;p&gt;Về Crystal Report bạn sẽ gặp vấn đề này trên hầu hết các phiên bản khi chạy trên Windows Vista hoặc Windows 7, tuy nhiên hiện nay phiên bản Crystal Report 2008 V1 (SP1) đã được khắc phục lỗi này. Do đó, bạn nên dùng CR 2008 V1 nếu có thể.&lt;/p&gt;  </description><link>http://thanhhh.blogspot.com/2009/07/cach-chay-crystal-report-tren-windows.html</link><author>noreply@blogger.com (Anonymous)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh6.ggpht.com/_06Xqd8BIw_M/SlGDWFW4T_I/AAAAAAAAA-Q/4ypJShYwTeg/s72-c/DEP_Enabled_thumb%5B3%5D.jpg?imgmax=800" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-43957590526969220</guid><pubDate>Fri, 22 May 2009 09:00:00 +0000</pubDate><atom:updated>2009-05-22T16:01:43.751+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">ClickOnce</category><title>How to publish a ClickOnce Application without Visual Studio</title><description>&lt;p&gt;When i search a solution for the question “How to change .exe.config.deploy”, i cannot have a good answer. So, i try to create my solution and it is successful.&lt;/p&gt;  &lt;p&gt;In this solution, you can deploy a ClickOnce Application to customer’s server, it execute with requests:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Modify config in app.config, example: Connection String, Web Reference URL,… &lt;/li&gt;    &lt;li&gt;Change start location of a signed ClickOnce Application. &lt;/li&gt;    &lt;li&gt;Change Version of product. &lt;/li&gt;    &lt;li&gt;Publish application without VS and .NET Framework SDK.&lt;/li&gt; &lt;/ul&gt;  &lt;h5&gt;1. Create necessary files for ClickOnce Server&lt;/h5&gt;  &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; To publish the ClickOnce Application to a server, you must have necessary files includes: &lt;em&gt;MyApp.application&lt;/em&gt; (my application sample is MyApp), &lt;em&gt;index.html &lt;/em&gt;and&lt;em&gt; setup.exe&lt;/em&gt;. &lt;/p&gt;  &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Open the source code in Visual Studio and publish ClickOnce Application to local IIS . Next, open&amp;#160; publish folder (C:\Inetpub\wwwroot\MyApp) and copy only files &lt;em&gt;MyApp.application&lt;/em&gt;, &lt;em&gt;index.html&lt;/em&gt; and &lt;em&gt;setup.exe&lt;/em&gt; to the deployment folder (MyAppClickOnce). This folder will be provided the customer who will publish application to his server.&lt;/p&gt;  &lt;h5&gt;&amp;#160; 2. Copy the contents of the application to the deployment folder&lt;/h5&gt;  &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Copy the contents of the application from the build output folder (bin\Release) to folder’s name [ApplicationName_Version(a_b_c_d)] to the deployment folder (MyAppClickOnce\MyApp_1_0_0_0).&lt;/p&gt;  &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Copy the publisher certificate that was generated by Visual Studio when you first published to the deployment folder.&lt;/p&gt;  &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Open folder [Microsoft Visual Studio 8]\SDK\v2.0\Bin and copy file mageui.exe&amp;#160; to the deployment folder.&lt;/p&gt;  &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Now, you can compress the deployment folder to your customer with install guide and he should be install a ClickOnce Application without Visual Studio and .NET Framework SDK 2.0.&lt;/p&gt;  &lt;h5&gt;3. Publish application&lt;/h5&gt;  &lt;ul&gt;   &lt;li&gt;Open mageui.exe, on menu File –&amp;gt; New –&amp;gt; Application Manifest.&lt;/li&gt;    &lt;li&gt;In the tab “app1.exe.manifest”, select “Name” from list in the left, input name of application into Name textbox&amp;#160; (MyApp) and Version textbox (1.0.0.0).&lt;/li&gt;    &lt;li&gt;In the tab “app1.exe.manifest”, select “Files” from list in the left, &lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;At “Application directory:”, select folder containing the application files (MyApp_1_0_0_0) .&lt;/li&gt;      &lt;li&gt;Click check on checkbox “When populating add the .deploy extension to any files that does not have it”.&lt;/li&gt;      &lt;li&gt;Click button “Populate”.&lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;On menu of mageui, select File –&amp;gt; Save.&lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;In the screen “Signing Options”, select option “Sign with certificate file”, click button […] to select publisher certificate. Click OK to next.&lt;/li&gt;      &lt;li&gt;In the screen “Save As”, save with default file name to folder containing application files (MyApp_1_0_0_0).&lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;On menu of mageui, select File –&amp;gt; Open.&lt;/li&gt;    &lt;li&gt;Select file MyApp.application in the deployment folder.&lt;/li&gt;    &lt;li&gt;In the tab “MyApp.application”, select “Name” from list in the left, change Version to 1.0.0.0&lt;/li&gt;    &lt;li&gt;In the tab “MyApp.application”, select “Deployment Options”, modify “Start Location” to new path or url of the current server.&lt;/li&gt;    &lt;li&gt;In the tab “MyApp.application”, select “Application Reference”, click button “Select Manifest…” to select file manifest that is created above.&lt;/li&gt;    &lt;li&gt;On menu of mageui, select File –&amp;gt; Save.&lt;/li&gt;    &lt;li&gt;In the screen “Signing Options”, select option “Sign with certificate file”, click button […] to select publisher certificate. Click OK to save.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Now, you can install ClickOnce Application from new location.&lt;/p&gt;  &lt;p&gt;P.S: I am sorry about my English. :)&lt;/p&gt;  </description><link>http://thanhhh.blogspot.com/2009/05/how-to-publish-clickonce-application.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-283476008699860275.post-787671252913624914</guid><pubDate>Wed, 06 May 2009 09:14:00 +0000</pubDate><atom:updated>2009-05-07T10:36:03.608+07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">.NET</category><category domain="http://www.blogger.com/atom/ns#">TDD</category><title>Test-Driven Development (TDD) trong .NET bằng ví dụ – Phần 3</title><description>&lt;p&gt;&lt;/p&gt; Phần này sẽ cài đặt các Test 3 và Test 4 trong loại bài về Test-Driven Development trong .NET qua ví dụ. Các phần trước bao gồm:  &lt;p&gt;&lt;a href=&quot;http://thanhhh.blogspot.com/2009/05/test-driven-development-tdd-trong-net.html&quot;&gt;Test-Driven Development (TDD) trong .NET bằng ví dụ&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href=&quot;http://thanhhh.blogspot.com/2009/05/test-driven-development-tdd-trong-net_06.html&quot;&gt;Test-Driven Development (TDD) trong .NET bằng ví dụ – Phần 2&lt;/a&gt;&lt;/p&gt;  &lt;h4&gt;Test 3: &lt;em&gt;Push&lt;/em&gt; một đối tượng đơn, &lt;em&gt;Pop&lt;/em&gt; đối tượng đó và xác nhận &lt;em&gt;IsEmpty&lt;/em&gt; là true.&lt;/h4&gt;  &lt;p&gt;Trong Test case này, ta thêm một phương thức mới cho lớp Stack có nhiệm vụ lấy ra phần tử trên đỉnh của Stack. Nhưng trước tiên, ta cần tiến thành cài đặt unit test cho test case này, đoạn code như sau:&lt;/p&gt;  &lt;pre class=&quot;c-sharp&quot; name=&quot;code&quot;&gt;    [Test]
    public void Pop()
    {
        stack.Push(&amp;quot;first element&amp;quot;);
        stack.Pop();
        Assert.IsTrue(stack.IsEmpty,
            &amp;quot;Sau khi Push - Pop. IsEmpty phải bằng true.&amp;quot;);
    }&lt;/pre&gt;

&lt;p&gt;Và tất nhiên, ta phải cài đặt một phương thức Pop trong lớp Stack.&lt;/p&gt;

&lt;pre class=&quot;c-sharp&quot; name=&quot;code&quot;&gt;    public void Pop()
    {
    }&lt;/pre&gt;

&lt;p&gt;Tuy nhiên, nếu bây giờ bạn tiến hành complite và chạy test, thì sẽ trả về kết quả fail. Do đó, ta bổ sung thêm m_IsEmpty = true để hoàn thành test case này.&lt;/p&gt;

&lt;pre class=&quot;c-sharp&quot; name=&quot;code&quot;&gt;    public void Pop()
    {
        m_IsEmpty = true;
    }&lt;/pre&gt;

&lt;p&gt;Kết quả là chúng ta đã hoàn thành unit test cho Test case 3. Tiếp theo, ta sẽ cài đặt test case 4.&lt;/p&gt;

&lt;h4&gt;Test 4: &lt;em&gt;Push&lt;/em&gt; một đối tượng đơn, ghi nhớ đối tượng đó; &lt;em&gt;Pop&lt;/em&gt; đối tượng, và xác nhận hai đối tượng này là giống nhau. &lt;/h4&gt;

&lt;p&gt;Trong unit test của test case này, chúng ta sẽ thực hiện so sánh nội dung của 2 phần tử, đó là phần từ đưa vào Stack với phần tử lấy ra từ Stack ngay sau khi đưa vào. Do đó, ta sẽ đặt tên là PushPopContentCheck:&lt;/p&gt;

&lt;pre class=&quot;c-sharp&quot; name=&quot;code&quot;&gt;    [Test]
    public void PushPopContentCheck()
    {
        int expected = 1234;
        stack.Push(expected);   
        int actual = (int) stack.Pop();
        Assert.AreEqual(expected, actual);
    }&lt;/pre&gt;

&lt;p&gt;Tiếp theo, ta cần phải thay đổi lại phương thức Pop của lớp Stack.&lt;/p&gt;

&lt;pre class=&quot;c-sharp&quot; name=&quot;code&quot;&gt;    public object Pop()
    {
        m_IsEmpty = true;
        return null;
    }&lt;/pre&gt;

&lt;p&gt;Sau khi build thành công và chạy test, thì kết quả test trả về sẽ error vì lúc này ta chưa trả về giá trị trên đỉnh của Stack. Do đó, để pass cho unit test này, chúng ta lưu lại giá trị nhận được từ Push và trả lại giá trị trong Pop. Đoạn code sau khi thay đổi sẽ như sau:&lt;/p&gt;

&lt;pre class=&quot;c-sharp&quot; name=&quot;code&quot;&gt;using System;

public class Stack
{
    private bool m_IsEmpty = true;
    private object m_Element;

    public bool IsEmpty
    {
        get {
            return m_IsEmpty;
        }
    }

    public void Push(object element)
    {
        m_IsEmpty = false;
        m_Element = element;
    }

    public object Pop()
    {
        m_IsEmpty = true;
        object top = m_Element;
        m_Element = null;
        return top;
    }
}&lt;/pre&gt;

&lt;p&gt;Lúc này, ta thấy phương thức IsEmpty chỉ cần kiểm tra xem phần tử đang được lưu có bằng null hay không. Do đó, ta sẽ thay đổi ở phương thức IsEmpty như sau:&lt;/p&gt;

&lt;pre class=&quot;c-sharp&quot; name=&quot;code&quot;&gt;using System;

public class Stack
{
    private object m_Element = null;

    public bool IsEmpty
    {
        get {
            return (m_Element == null);
        }
    }

    public void Push(object element)
    {
        m_Element = element;
    }

    public object Pop()
    {
        object top = m_Element;
        m_Element = null;
        return top;
    }
}&lt;/pre&gt;

&lt;p&gt;Lúc này đã tốt hơn với code của Stack vì ta thấy rằng lúc này chỉ cần cập nhập lại một biến thay vì hai như trước đây.&amp;#160; Đến lúc này, khi thực hiện đưa vào 1 phần tử thì nó vẫn nằm trên đỉnh của Stack vì lúc lấy ra ta lấy đúng phần tử đã đưa vào. Tuy nhiên, sẽ có vấn đề lúc đưa nhiều hơn 1 phần từ vào Stack, ta sẽ được thấy điều này qua các test case tiếp theo.&lt;/p&gt;  </description><link>http://thanhhh.blogspot.com/2009/05/test-driven-development-tdd-trong-net_2189.html</link><author>noreply@blogger.com (Anonymous)</author><thr:total>0</thr:total></item></channel></rss>