冷眼程序人生----对一些ORM框架的使用心得

序:
    信不信由你:
        谁都可以写出机器能执行的代码,但不是谁都写得出其他程序员也看得懂的代码.(流醒鱼的感慨)
        任何一个工具都很难被使用者真正掌握,工具越强大,配置越复杂,复杂性从code变成config

    有时自己觉得真的不年轻了,怎么也多少有些唠叨了?

一)问题描述
最近一直在尝试几个ORM框架,我感觉他们都不错的,只要你真正掌握,对付一般小Mis系统应该都不成问题.

几乎每个工具都有现成的例子,但是这些例子往往过于简单.请看Hibernate的例子:
Public class Blog
{
...
public IList Posts;
}

Public class Post
{
...
public Blog owner;
}
这个例子本来也没有什么问题,只是很多人依葫芦画瓢就搞起了商业系统,看到他们的一些代码,实在是能预感到维护阶段的一幕幕。

二)避免问题的一点经验
经过自己看别人的程序感触,我把这些要点记下来,大约如下:
1) 逻辑变化和扩展要对实体类关闭频繁修改
   比如有个User类,那我们做人事系统时可能有TimeCard;而我们做项目管理系统时又可能有Task等
2) 实体和逻辑类必须在完全没有数据库支持的情况下测试通过,证明自己的清白
   当数据库操作一上来后,会发生大量异常,千万不要把宝贵的时间放在联调上,分布式跟踪代价“高,实在是高”!

所以我通常用下面的图来提醒自己:

            业务实体、业务逻辑
---------------实体关系------------
              数据操作

可以看到实体关系处在一个灰色地带,放到那里可以根据具体情况决定

三)一个较为清晰的实例
为了突出思路和要点,下面将采用Teddy的ORM框架来解释,其他的类似,主要调整一下数据访问层。


3.1)业务背景
  每个应用都重复着组、用户的这些逻辑,用这个例子大家一定不会陌生
  规则1:一个组可以再包含下面的组,但是系统只有一个最顶层的组
  规则2:一个组可能有多个用户(也可能没有)
  规则3:一个用户至少一个组

下面的例子主要针对规则2。

3.2)代码演示
首先定义数据实体
注意,vUsersOfGroup是一个视图,只是改进效率,原则上不应该出现在实体类中
为了能够表达规则2,建立下面的实体关系类

using System;
using System.Data;
using Ilungasoft.Framework.Common;
namespace Teddy.ORM.Test
{
    
public abstract class Entity
    
{
    
        
public interface Group : IEntity
        
{
            
int ID getset; }
            
string Title getset; }
            
string Description getset; }
            System.DateTime CreateTime 
getset; }
            
int ParentID getset; }
        }


       

        
public interface User : IEntity
        
{
            
int ID getset; }
            
string Name getset; }
            
bool Gender getset; }
            
double Salary getset; }
        }

        
public interface vUsersOfGroup : IEntity
        
{
            
int ID getset; }
            
string Name getset; }
            
bool Gender getset; }
            
double Salary getset; }
            
int GroupID getset; }
        }

    }

}

public  class MemberShip
    
{
        
public Entity.Group Group;
        
public Entity.User Administrator;
        
public Entity.User[] Members;


        
public MemberShip()
        
{
            
        }

        
public MemberShip(Entity.Group g,Entity.User u,Entity.User[] members)
        
{
            Group 
= g;
            Administrator 
= u;
            Members 
= members;

        }

    
      
    }

关系数据的操作类也比较简单

using System;
using System.Data;

using Ilungasoft.Framework.Common;
using Ilungasoft.Framework.Data.Facade;
using Teddy.ORM;
namespace Teddy.ORM.Test
{
    
public  class MemberShipLoader
    
{
        
private Gateway _gw;

        
public MemberShipLoader(Gateway gw)
        
{
            _gw 
= gw;
        }

      
        
// todo 如果框架中使用反射自动做这类事就好了!
        public Entity.User clone(Entity.vUsersOfGroup vUser)
        
{
            Entity.User u 
= EntityFactory<Entity.User>.CreateObject();
            u.ID 
= vUser.ID;
            u.Name 
= vUser.Name;
            u.Salary 
= vUser.Salary;
            u.Gender 
= vUser.Gender;
            
return u;
        }

        
public MemberShip getMemberShip(Entity.Group group)
        
{
            Entity.vUsersOfGroup[] temp_users 
= _gw.Select<Entity.vUsersOfGroup>("GroupID=" + group.ID);
            Entity.User[] us 
= new Entity.User[temp_users.Length];
            
int i = 0;
            
foreach (Entity.vUsersOfGroup t in temp_users)
               us[i
++= clone(t);
            
return new MemberShip(group, us[0], us);

        }


    }

}

最后就是证明自己的清白了:

using System;
using System.Data;
using System.Collections;
using NUnit.Framework;
using Ilungasoft.Framework.Common;
using Ilungasoft.Framework.Data;
using Ilungasoft.Framework.Data.Facade;
using Ilungasoft.Framework.Data.MsAccess;
using Teddy.ORM;
    [TestFixture]
    
public class Tester {
        
        [SetUp]
        
public void Init() {
                
        }

        [TearDown]
        
public void Finish() {
            
        }

        
       
        [Test]
        
public void testSimpleDataAccess()
        
{

            
string connstr = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=E:\SORM\App_Data\TestRelation.mdb";
         
            Database db 
= new Database(new AccessDbProvider(connstr));
            Gateway gw 
= new Gateway(db);
            MemberShipLoader msl 
= new MemberShipLoader(gw);

            Entity.Group g 
= EntityFactory<Entity.Group>.CreateObject();
            g.ID 
= 1;  g.Description ="Demo 公司";
         
            MemberShip ms 
= msl.getMemberShip(g);
            Console.WriteLine(
"Group:" + ms.Group.Description);
            Console.WriteLine(
"Administrator:"+ms.Administrator.Name);
            Console.WriteLine(
"====Members======");
            Assert.AreEqual(
3, ms.Members.Length);
            
foreach (Entity.User u in ms.Members) Console.WriteLine(u.Name);
        }

       }

我机器上的结果如下:
Group:Demo 公司
Administrator:Alex
====Members======
Alex
cxiong
yxiuquan

四)小结
  4.1 先梳理出清晰的概念,然后再编码,选择合适的工具。
  4.2 因为时间和篇幅,我并没有按照自己的要求在没有数据联入时进行单元测试,因为这个例子太过于简单,所以万事不要绝对。
  4.3 Teddy的框架如果能够方便吧Entity导出到XML今后就更加实用,不知何时能看到。
  4.4 Teddy的框架能加上MySQL和Oracle的DbProvider就会更加实用了。

Alex 4-6

posted @ 2006-04-06 12:03  成为-行动-拥有(BeDoHave)  阅读(6351)  评论(6编辑  收藏  举报