使用FormView Unity和LINQ实现的3层架构----MasterDataManage模型

来源:互联网 发布:为什么淘宝没有展现 编辑:程序博客网 时间:2024/05/16 14:57

代码下载:FormviewUse

花了两天的空闲时间coding这个东西。虽然不算一个完整的项目,但是它完全是一个架构,所谓的3层结构,它主要是介绍了一个控件Formview的使用,如果你没使用过Formview希望这篇文章里的知识能够让你在以后的项目里使用到formview,因为它确实是个比较好用的控件。可能你现在有一些生成3层结构代码的工具,或者你很熟悉3层结构了,尽管如此你还是可以在这里学到很多不一样的东西。至少是在framework2.0之后的知识。我的架构如下图:

 

 

 

是不是不大一样啊?????

数据访问层面这里不再需要去生成那么多无聊的SQL,修改时都会很麻烦,而如果使用Linq,就算是表结构的修改去我们数据访问层的影响都是微乎其微的。对于性能方面,请不要担忧,它不比SQL差多少,你可以使用Profiler去测试下。

   如果你是个Linq的小白,那么建议你可以读下面这部分,但是如果你很熟悉Dlinq,Xlinq的话建议直接跳过这部分。

Part 1: Linq的学习资料:

Wikipedia:
Codeproject:[url=http://www.codeproject.com/KB/linq/]
LINQ Video:
http://windowsclient.net/learn/videos_LINQ.aspx,
http://www.asp.net/learn/linq-videos/

LINQ Article by Anders:
http://msdn.microsoft.com/en-us/library/bb308959.aspx
LINQ at MSDN:
http://msdn.microsoft.com/en-us/library/bb397926.aspx
LINQ Project Home page at MSDN:
http://msdn.microsoft.com/en-us/netframework/aa904594.aspx
LINQ FAQ:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=307705&SiteID=1

ScottGu's Blog:

Part1:
http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to-sql-part-1.aspx
Part2:
http://weblogs.asp.net/scottgu/archive/2007/05/29/linq-to-sql-part-2-defining-our-data-model-classes.aspx
Part3:
http://weblogs.asp.net/scottgu/archive/2007/06/29/linq-to-sql-part-3-querying-our-database.aspx
Part4:
http://weblogs.asp.net/scottgu/archive/2007/07/11/linq-to-sql-part-4-updating-our-database.aspx
Part5:
http://weblogs.asp.net/scottgu/archive/2007/07/16/linq-to-sql-part-5-binding-ui-using-the-asp-linqdatasource-control.aspx
Part6:
http://weblogs.asp.net/scottgu/archive/2007/08/16/linq-to-sql-part-6-retrieving-data-using-stored-procedures.aspx
Part7:
http://weblogs.asp.net/scottgu/archive/2007/08/16/linq-to-sql-part-6-retrieving-data-using-stored-procedures.aspx
Part8:
http://weblogs.asp.net/scottgu/archive/2007/08/27/linq-to-sql-part-8-executing-custom-sql-expressions.aspx
Part9:
http://weblogs.asp.net/scottgu/archive/2007/09/07/linq-to-sql-part-9-using-a-custom-linq-expression-with-the-lt-asp-linqdatasource-gt-control.aspx
ScottGu
的博客有中文翻译的:
http://blog.joycode.com/scottgu/
或许能找到上面的文章。


Step by step...
Introducing LINQ – Part 1
http://dotnetslackers.com/articles/csharp/IntroducingLINQ1.aspx
Introducing LINQ – Part 2
http://dotnetslackers.com/articles/csharp/IntroducingLINQ2.aspx
Introducing LINQ – Part 3
http://dotnetslackers.com/articles/csharp/IntroducingLINQ2.aspx
Introducing LINQ – Part 4
http://dotnetslackers.com/articles/csharp/IntroducingLINQ4.aspx
Introducing LINQ – Part 5
http://dotnetslackers.com/articles/csharp/IntroducingLINQ5.aspx

 

Part2 : 关于Business层面的不同。

不知道你是否用过Microsoft.PracticesEL,我这里使用的是Microsoft.PracticesUnity。关于Unity的介绍在codeplex上有它的opensource项目,如果你英文还好那你可以过去看看。我有写过如果使用它,文章地址:

http://blog.csdn.net/dujingjing1230/archive/2009/12/22/5055613.aspx

Omardropthings写的一段话告诉你Unity绝对颠覆了一些传统的coding.

 

使用Unity你不再需要在business layerdata access layer之间通过实例化那么多的类来实现两个层级的通讯,你只需要使用Resolve<interface>()来代替 new Classname(),它的灵活性也是比传统的不停地使用Instance要好很多。至少能减少你很多代码的编写。

关于Unity的介绍这里不再多说。

Part 3Formview的使用

Formview控件的使用比较适合对于GridView记录的编辑查看等。至少这是我目前感觉到的。GridViewedittemplate,InsertTemplate确实可以让你的更新和新增更加容易,但是如果每个页面显示50条记录时,使用本身的edittemplate客户提样将会很差,所以使用formview可以有更好的用户体验。

 

你也可以使用Popup Window来实现。比如jQuery的一些popupplugin,或者是ajaxcontroltoolkitModalPopup控件来实现。这里有个例子:

http://blog.csdn.net/dujingjing1230/archive/2009/11/03/4763791.aspx

 

废话不多说了,进入正题吧,先详细说明下数据访问层的实现:

D1 创建一个solution,然后添加一个project,我起名为LinqDAL,然后添加一个Linqtosql类:

 

D2:数据库的链接和表的创建:

我已经在数据库中创建了一个Club的表,包括字段ID,ClubNameURL三个字段。ID为自增字段。把这个表拽入第一步中创建的LINQ to SQL 类的设计模式:

 

   然后是数据访问的代码编写,先看看数据访问层类图:

 

D3:可以说是分成三部分,第一部分是Linq to SQL自动生成的代码,而clubcontext2是继承它的一个获取数据库连接字符串的类,这样在Web层面clubcontext2可以得到config文件中的连接字符。IclubDataContent接口中定义了所有的可能用到的delete,insert,updategetlist的方法,它的存在就是为了在Business层面使用Microsoft.practices.Unity

看看它的Methods代码:

   void Delete<TSource>(ClubDataContextDataContext.SubsystemEnum subsystem, TSource entity)

            where TSource : class;

 

        void DeleteByPK<TSource, TPK>(ClubDataContextDataContext.SubsystemEnum subsystem, TPK pk)

            where TSource : class;

 

        void DeleteByPK<TSource, TPK>(TPK[] pkArray, System.Data.Linq.DataContext data)

            where TSource : class;

 

        void DeleteByPK<TSource, TPK>(ClubDataContextDataContext.SubsystemEnum subsystem, TPK[] pkArray)

            where TSource : class;

 

        void DeleteByPK<TSource, TPK>(TPK pk, System.Data.Linq.DataContext dc)

            where TSource : class;

 

        void DeleteList<TSource>(ClubDataContextDataContext.SubsystemEnum subsystem, System.Collections.Generic.List<TSource> list)

            where TSource : class;

 

        void Dispose();

        //-------------------------------------------------------Return type is a Generic List   ---------------------------------------------

        System.Collections.Generic.List<TSource> GetList<TSource>(ClubDataContextDataContext.SubsystemEnum subsystem, Func<ClubDataContextDataContext, System.Linq.IQueryable<TSource>> func);

 

        System.Collections.Generic.List<TSource> GetList<TSource, TArg0>(ClubDataContextDataContext.SubsystemEnum subsystem, TArg0 arg0, Func<ClubDataContextDataContext, TArg0, System.Linq.IQueryable<TSource>> func, System.Data.Linq.DataLoadOptions options);

 

        System.Collections.Generic.List<TSource> GetList<TSource, TArg0, TArg1>(ClubDataContextDataContext.SubsystemEnum subsystem, TArg0 arg0, TArg1 arg1, Func<ClubDataContextDataContext, TArg0, TArg1, System.Linq.IQueryable<TSource>> func);

 

        System.Collections.Generic.List<TSource> GetList<TSource, TArg0, TArg1, TArg2>(ClubDataContextDataContext.SubsystemEnum subsystem, TArg0 arg0, TArg1 arg1, TArg2 arg2, Func<ClubDataContextDataContext, TArg0, TArg1, TArg2, System.Linq.IQueryable<TSource>> func);

 

        System.Collections.Generic.List<TSource> GetList<TSource, TArg0>(ClubDataContextDataContext.SubsystemEnum subsystem, TArg0 arg0, Func<ClubDataContextDataContext, TArg0, System.Linq.IQueryable<TSource>> func);

 

        //------------------------------------------------------ Return type is a Custom type  ----------------------------------------------

        TReturnType GetQueryResult<TSource, TArg0, TArg1, TReturnType>(ClubDataContextDataContext.SubsystemEnum subsystem, TArg0 arg0, TArg1 arg1, Func<ClubDataContextDataContext, TArg0, TArg1, System.Linq.IQueryable<TSource>> func, Func<System.Linq.IQueryable<TSource>, TReturnType> returnExpected);

 

        TReturnType GetQueryResult<TSource, TArg0, TArg1, TArg2, TReturnType>(ClubDataContextDataContext.SubsystemEnum subsystem, TArg0 arg0, TArg1 arg1, TArg2 arg2, Func<ClubDataContextDataContext, TArg0, TArg1, TArg2, System.Linq.IQueryable<TSource>> func, Func<System.Linq.IQueryable<TSource>, TReturnType> returnExpected);

 

        TReturnType GetQueryResult<TSource, TArg0, TArg1, TReturnType>(ClubDataContextDataContext.SubsystemEnum subsystem, TArg0 arg0, TArg1 arg1, Func<ClubDataContextDataContext, TArg0, TArg1, System.Linq.IQueryable<TSource>> func, Func<System.Linq.IQueryable<TSource>, TReturnType> returnExpected, System.Data.Linq.DataLoadOptions options);

 

        TReturnType GetQueryResult<TSource, TArg0, TReturnType>(ClubDataContextDataContext.SubsystemEnum subsystem, TArg0 arg0, Func<ClubDataContextDataContext, TArg0, System.Linq.IQueryable<TSource>> func, Func<System.Linq.IQueryable<TSource>, TReturnType> returnExpected, System.Data.Linq.DataLoadOptions options);

 

        TReturnType GetQueryResult<TSource, TArg0, TReturnType>(ClubDataContextDataContext.SubsystemEnum subsystem, TArg0 arg0, Func<ClubDataContextDataContext, TArg0, System.Linq.IQueryable<TSource>> func, Func<System.Linq.IQueryable<TSource>, TReturnType> returnExpected);

 

        TReturnType GetQueryResult<TSource, TReturnType>(ClubDataContextDataContext.SubsystemEnum subsystem, Func<ClubDataContextDataContext, System.Linq.IQueryable<TSource>> func, Func<System.Linq.IQueryable<TSource>, TReturnType> returnExpected);

 

 

        //------------------------------------------------------------Return value is a param  ------------------------------------------------

        TSource GetSingle<TSource, TArg0, TArg1>(ClubDataContextDataContext.SubsystemEnum subsystem, TArg0 arg0, TArg1 arg1, Func<ClubDataContextDataContext, TArg0, TArg1, System.Linq.IQueryable<TSource>> func);

    

        TSource GetSingle<TSource, TArg0>(ClubDataContextDataContext.SubsystemEnum subsystem, TArg0 arg0, Func<ClubDataContextDataContext, TArg0, System.Linq.IQueryable<TSource>> func);

 

        TSource GetSingle<TSource>(ClubDataContextDataContext.SubsystemEnum subsystem, Func<ClubDataContextDataContext, System.Linq.IQueryable<TSource>> func);

 

        TSource GetSingle<TSource, TArg0, TArg1, TArg2>(ClubDataContextDataContext.SubsystemEnum subsystem, TArg0 arg0, TArg1 arg1, TArg2 arg2, Func<ClubDataContextDataContext, TArg0, TArg1, TArg2, System.Linq.IQueryable<TSource>> func);

      

        //---------------------------------------------------------Whether in Context or Not  ----------------------------------------------------

        T InDataContext<T>(ClubDataContextDataContext.SubsystemEnum subsystem, bool nolock, Func<ClubDataContextDataContext,T> f);

        void InDataContext(ClubDataContextDataContext.SubsystemEnum subsystem,bool nolock,ClubDataContextDataContext.DataContextDelegate d);

 

//-------------------------------------------------------Object-Insert Methods for Club  ----------------

      TSource Insert<TSource>(ClubDataContextDataContext.SubsystemEnum subsystem,Action<TSource> populate)

             where TSource :class,new();

       

        void InsertList<TEntity,TSomething>(ClubDataContextDataContext.SubsystemEnum subsystem,System.Collections.Generic.IEnumerable<TSomething> items,Converter<TSomething,TEntity> converter)

             where TEntity:class

             where TSomething:class;

 

        //-------------------------------------------------------Object-Update Methods for Club  ----------------------------------------------------------

        void UpdateList<TEntity>(ClubDataContextDataContext.SubsystemEnum subsystem, System.Collections.Generic.IList<TEntity> list, Action<TEntity> detach,Action<TEntity> postAttachUpdate)

            where TEntity:class;

 

         void UpdateObject<TEntity>(ClubDataContextDataContext.SubsystemEnum subsystem, TEntity obj, Action<TEntity> detach, Action<TEntity> postAttachUpdate)

            where TEntity : class;

 

        void UpdateObject<TEntity, TArg0>(ClubDataContextDataContext.SubsystemEnum subsystem, TArg0 arg0, Func<ClubDataContextDataContext, TArg0, System.Linq.IQueryable<TEntity>> func, Action<TEntity> postAttachUpdate)

            where TEntity : class;

然后是第二部分DatabaseHelper类,说实话它并没有继承IClubDataContent,只是它的方法和IClubDataContent完全相同,它只是为了可以使用不同的数据库而已。在第二部分还有个LinqQueries类,Linq的语法这里就用到了。呵呵。因为我这里只有一个实体Club只需要用到两个方法:

  public static readonly Func<ClubDataContextDataContext, int, IQueryable<Club>> CompiledQuery_GetClubById =

            CompiledQuery.Compile<ClubDataContextDataContext, int, IQueryable<Club>>((dc, clubId) =>

                from club in dc.Clubs

                where club.ID == clubId

                select club

                );

 

        public static readonly Func<ClubDataContextDataContext,IQueryable<Club>> CompiledQuery_GetClubs =

            CompiledQuery.Compile<ClubDataContextDataContext, IQueryable<Club>>((dc) =>

                from club in dc.Clubs

                select club

                );

这里就是Linq代替SQLSelect功能体现。

第三部分是ClubRespository,Business层面对Club的操作方法都这里定义,因为我们使用Unity,所以这里同样需要一个IClubRespository接口和ClubRespository类。

 

注意这里的IClubDataContext的定义,看到了吧,完全是用接口来定义,不涉及到ClubDataContext的实例化。

Timelog是个帮助类,这里就不说了。

下面我们来完成Business层面的代码

B1 创建一个项目命名为Business,注意一定要添加引用:

 

B2 它里面的东西不多,就三部分,一个是DashboardFace用来通过sqlhelper类得到Clublist,还有最重要的就是那个Business.Container命名空间中的类,它封装好了使用Unity的所有方法,后面我们对于数据层接口的访问全靠它来实例化了:

 

代码比较长我就不贴了,主要说明下它的构成:

1.                  RegisterInstance部分:在Web层面,当加载页面时使用这些方法可以让Interface直接实例化,

2.                  Resolve部分: 真正的去把Interface转化为Instance

3.                  InjectIntoConstructor 方法所属的类的实例化。

B3 Dashboardfacade类:

它主要是Business层面提供Clublist的方法:

    public static  List<Club> GetClubList()

        {

           return  DatabaseHelper.GetList<Club>(DatabaseHelper.SubsystemEnum.Club, LinqQueries.CompiledQuery_GetClubs);  

        }

web层面会把这个list添加到一个datatable中绑定到Gridview

 

下面一部分是Business.Facede部分:

BF1:  Facade部分的Façade类有两个partial类组成,一个是façade,它包含Façade的构造函数和接口的注册函数:

 

        public static void BootStrap()

        {

            ServiceLocator.RegisterType<IClubDataContext, ClubDataContext2>();

            ServiceLocator.InjectIntoConstructor<ClubDataContext2>();  //dummy injection for empty constructor

            ServiceLocator.RegisterType<IClubRepository, ClubRepository>();

          

        }

 

public Facade(AppContext context) :

            this( context, ServiceLocator.Resolve<IClubRepository>())

        {

            this.Context = context;

        }

因为这里只有一个Club实体,所以Resolve()只有一个。。。

BF2 façade.club文件中我们定义了Club文件在Business层面的Insert,DeleteUpdate的方法。贴个Update方法的代码:

 

Update()方法是个的cl是个delegate,它在数据访问层定义好了,使用的是Collection.Generic中的委托。

上图中的Model3层结构中的Model部分,这里只包含了个ClubClub的结构和数据库中Club表的结构相同。就不再说Model了。

 

现在我们已经搞定了DALBLL层的代码和两部分的连接。最后我们来看Web层如何实现了,上面有说到我会用到Formview

W1.创建一个Website,然后把各个生成好的dll引用进来。创建一个User Control

ClubControl.ascx

它包含一个Formview和一个Gridview。页面代码:

 

具体代码在我提供的代码中看吧,这里不贴了。后台的代码实现我介绍下:

  protected void Page_Load(object sender, EventArgs e)

    {

        if (!Page.IsPostBack)

        {

            gvClubList.DataSource = ClubList.BuildClubList();

            gvClubList.DataBind();

            FormView1.ChangeMode(FormViewMode.ReadOnly);

        }

    }

这里的BuildClubList()ClubList类中,它从Business层面得到ClubList并把这些Club存到一个datatable中。

 

W2Formview的时间绑定:

  protected void FormView1_ItemCommand(object sender, FormViewCommandEventArgs e)

    {

        switch (e.CommandName)

        {

            case "UpdateInfo":

                UpdateClub();

                FormView1.ChangeMode(FormViewMode.ReadOnly);

                gvClubList.DataSource = ClubList.dt;

                gvClubList.DataBind();

                break;

            case "SubmitInfo":

                AddClub();

                FormView1.ChangeMode(FormViewMode.ReadOnly);

                gvClubList.DataSource = ClubList.dt;

                gvClubList.DataBind();

                break;

            case "DeleteInfo":

               DeleteClub();

                FormView1.ChangeMode(FormViewMode.ReadOnly);

                gvClubList.DataSource = ClubList.dt;

                gvClubList.DataBind();

                break;

 

            case "CancelUpdate":

                FormView1.ChangeMode(FormViewMode.ReadOnly);

                break;

            case "CancelInsert":

                FormView1.ChangeMode(FormViewMode.ReadOnly);

                break;

            default:

                break;

        }

 

        FormView1.DataBind();

    }

例如:

   <asp:LinkButton ID="LinkButton20" runat="server" CommandName="SubmitInfo" Text="SubmitInfo">Commit</asp:LinkButton>

当点击它时,ItemCommandSubmitInfo,也就是说会执行AddClub()方法,

AddClub方法中,我们使用到了Façade

 

 

façade被实例化时,它会自动把Façade.Club.cs里面的类实例化,这就是Unity的魅力所在,我完全不需要去定义一个Club类的实例,然后club.InsertClubInstance()。其它几个方法和这个差不多。

对了,忘了一个很重要的事情:在Global.asax文件中去执行Business.Facade.Facade.BootStrap();

 

最后就是在default.aspx页面中把ClubControl.ascx添加到UpdatePanel中。

 

编译下,看看结果:

 

但是当我在Update一条记录时出错了,提示如下:

An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.

Google了下,找到了原因,是因为Linq本身对于每条记录都有个时间的记录,参考文章:

http://www.west-wind.com/weblog/posts/134095.aspx

http://geekswithblogs.net/michelotti/archive/2007/12/17/117791.aspx

http://geekswithblogs.net/AzamSharp/archive/2008/05/17/122222.aspx

最后设置timestamptrue,再次去update记录就OK了。

圣诞节到了,这个算是我送给.net用户的礼物吧。如果有不好的地方希望大家多多指教。。如果你觉得有啥建议,请留言吧。

原创粉丝点击