Categories

Links

关于neo (修改中)

先介绍以下neo,通常的neo代码是这样的

IDataStore dataStore=new SqlDataStore(connectionString) //创建一个IDataStore实例,最好用factory 模式

ObjectContext objectContext=new ObjectContext(dataStore) //创建一个ObjectContext 实例

CustomerFactory factory=new CustomerFactory(objectContext) //创建一个具体对象的Factory类

ObjectList<Customer> customers=factory.FindAllObjects(); //执行查询

neo 让你编辑一个xml文件,然后根据这个文件自动生成entity,entityMap,entityFactory, xml文件的部分最后总是由具体的entityMap来描述的. 因此最终交付时并不需要交付xml 文件

neo 已经近一年没有更新了,最新的版本是在blog而不是在neo.codehaus.org 上发布的.

随着linq to sql和ado.net entity framework的发布,dataset(datatable)最终将会被他们所取代,但个人觉得的,从技术角度说,dataset 解决了许多问题,比方说,快速的装载,对象状态的跟踪,合并修改,更好的数据绑定等等, 如果有orm 建筑在dataset技术之上,也是不错的一个方案. 但是,我并没有发现这样的方式,neo也并没有完全发挥dataset的优势, 比方说 下面所说的3和4

btw : 最近看到的IdeaBlade DevForce是基于dataset技术的,它的entity是最终是从 DataRow继承下来的,但由于这个是商业项目,因此没有深究. 有兴趣的可访问 www.ideablade.com ,如果你是mvp ,你可以免费申请专业版.

1. neo 的entity是composite了DataRow ,但并不是继承DataRow,每个entity属性均使用datarow来存储,因此,entity的内存占用比一般常规的entity的小些.

2. neo 在内存中同时维持dataset和entity的,因此,相比仅使用数据集,内存占用还是大一些

3. neo 的ObjectList并不适合windows 绑定(确切的说这部分代码,实现IBindingList部分的没有完成)

4. neo 先是查询返回一个DataSet,然后将DataSet中每个表的记录导入到ObjectContext中的mainDataSet(DataSet属性)中,并创建每个对象,所以 ,它的性能大约就是直接使用dataset的2到3倍,注意, 创建对象时由于entitycompsoite了datarow,因此,每个对象只需要一次简单的赋值就行.所以,总体上看,性能还是比常规的entity要好一些.

5. neo 总是试图和dataset保持同步,当你执行一个查询,如

ObjectList<Customer> customers=new CustomerFactory(objectContext).FindAllObjects();

Customer对象所对应的数据表(以及其关系)的schema就会被导入到 ObjectContext的DataSet中,同时Customer所对应的数据表记录会被fill,如果你调用了一个关系,比方说

for(Order order in customers.Orders){

}

Order对象所对应的数据表也会被fill

你创建一个对象,删除一个对象,这些对象对应的DataRow也会被相应的创建和删除

6. 说说ObjectContext的使用范围,在实际使用中,你应该将ObjectContext当成类似一个DataSet.一般来说,一个表单维护一个ObjectContet,比方说报价表单,维护一个quotationObjectContext,这样quotationObjectContext.DataSet将会仅包括报价有关的信息,如主表,明细表 ,相关的lookup记录等

7. 上面了说,在操作对象时neo 会同objectContext.DataSet保持同步,但反过来,操作objectContext.DataSet时,不会.但是你总可以将一个DataRow,DataSet合并到objectContext中. 这点对分布式操作非常重要.

8. 基于上一条,在windows form 中,你可以直接绑定objectContext.DataSet,但是,最好不要直接新增DataRow,创建一个entity,然后定位entity.Row (DataRow)是一个方法

9.neo 的机制非常适合分布式环境, 这这种情况下,你将ObjectContext区分为server端的和 client的

server端的ObjectContext ,总是获得一个具体的IDataStore实例,构造一个服务层,在两者之间传递dataset,clinet段的ObjectContext,则合并从服务层中获得的dataset ,然后提交更改的DataSet

10. neo 在内部维护着一个叫ObjectTable类的实例,这个实例跟踪所有的对象(包括删除的对象),在这里,ObjectId被作为key,ObjectId 是由dataTable 的表名和其primaryKey数组的值组成的.

这里有个主键的问题,主键最好是业务无关的,自动生成的,有些业务相关的并且依赖其它字段的主键是个棘手的问题.这同neo无关,不过如果你没有设置主键,这样,连续的两次新增就会有异常. (objectable 会找到上一条新增的记录,并且,同该记录的datarow会相同,结果引发类似下面的错误)

protected virtual IEntityObject GetObjectForRow(DataRow aRow)

 if(eo.Row.Equals(aRow) == false)
     throw new InvalidOperationException("Internal inconsistency; object exists but references different row.");
   

通常的做法是阻止连续新增,每条记录检查完整性,我把它称为 每记录检查,批量更新.意思是每新增或编辑完一条记录(注意,这里只是保存到dattable或是对象列表中)检查完整性, 用户可以新增多条,编辑多条,然后一次性提交修改.在 界面上,主要在移动记录时,如上一条,下一条,新建,删除,和保存时检查当前记录的完整性. 由于是批量更新,在用户离开时要有提示

 

使用neo 时并不能真正忘掉dataset,比方说 ,你注册一个ColumnChanging事件,这里你需要知道datatable name和datacolumn name(neo通过ColumnChangeBroker 对象对单个列的ColumnChanging事件支持), 如果你不想在这个事件中处理数据行,你可以将获得该datarow的对象表示,如下面的例子customerBindingSource 绑定的是由ObjectContext提供的DataSet,GetObjectId是个Helper函数

public ObjectId GetObjectId(DataRow row)
        {
            object[] keys = new object[row.Table.PrimaryKey.Length];
            foreach (DataColumn column in row.Table.PrimaryKey)
            {
                keys[0] = row[column];

            }
            return new ObjectId(row.Table.TableName, keys);
        }
        private bool ValidateCurrent()
        {
            if (customerBindingSource.Current != null)
            {
                DataRowView drv = (DataRowView)customerBindingSource.Current;
                DataRow row=drv.Row;
                Shyechang.Data.Customer customer=(Shyechang.Data.Customer)clientObjectContext.GetObject(GetObjectId(row));
                return customer.Validate();
            }
            return true;

        }

 默认生成时datatable name和datacolumn 的name是数据库的表名和列名. 你可以说这隔离的不可彻底. 看起来如果编码为entity 的名称和属性名 会好些,看起来这个可以通过修改一些代码来达成.

[2007-07-20 07:01:31 | Author:jiangjianxiao ] [] 3 comments

笔记: neo 支持中文生成

neo 使用nvelocity 来生成类,默认不支持中文,你可以通过以下方式来达到

Neo.Generator.Core.VelocityGenerator

修改构造过程
public VelocityGenerator()
  {
   Velocity.SetProperty(RuntimeConstants_Fields.RUNTIME_LOG_LOGSYSTEM_CLASS, "NVelocity.Runtime.Log.NullLogSystem");
            Velocity.SetProperty(RuntimeConstants_Fields.INPUT_ENCODING, "utf-8");
            Velocity.SetProperty(RuntimeConstants_Fields.OUTPUT_ENCODING, "utf-8");
            Velocity.SetProperty(RuntimeConstants_Fields.ENCODING_DEFAULT, "utf-8");

  }

Neo.VsTool.CodeGenAdaptor

第54行

 output = Encoding.UTF8.GetBytes(writer.ToString()); 

 

对于第一个,你也可以在vstool或其引用的任何一个项目中增加一个nvelocity.properties文件,并设置为嵌入的资源,里面加上

input.encoding=utf-8
output.encoding=utf-8

nvelocity.properties 会从当前appdomain的assembly中查找配置文件

 

这里指的neo 是指neo 2005 preview

http://weblogs.asp.net/pgielens/archive/2006/05/24/Download-Neo2005-Preview-_2800_Entity-Object-Framework_2900_.aspx

[2007-07-19 09:47:24 | Author:jiangjianxiao ] [] 1 comments

ibatis.net QueryForDataTable

完整的为ibatis.net 引入datatable支持要改动很多地方,所以描述的是最小化的改动.不过我们可以大概了解一下比较完整的集成要做那些事情.

ibatis.net 的基本运行原理就是获得一个reader后,然后进行循环,对每条记录使用ResultStrategy中的对应实现进行处理,然后返回到结果集.因此,首先,需要实现一个DataTableStrategy 用来为每条记录产生一个新DataRow. 大家可以看到,下面的实现已经绕开了ibatis.net的处理逻辑.

你可以在网上google到一些ibatis返回dataset的代码可 ,在最新的ibatis.net 这些代码都无法工作,这是因为RequestScope.IDbCommand现在返回的是一个DbCommandDecorator对象实例(一个实现IDbCommand接口并代理一个具体的IDbCommand实现的对象),而DataAdapter的实现,需要对应的idbcommand实现,如 SqlDataAdapter需要SqlCommand.因此,如下代码会导致cast错误

Mapper.LocalSession.CreateDataAdapter(scope.IDbCommand).Fill(dataTable);

这里有两种解法,一是使用datatable.Load方法来装载IDbCommand.ExecuteReader的返回结果,这是可行的

其次是利用反射,实际的idbcommand在DbCommandDecorator中被保存为_innerDbCommand field ,下面是两种实现. 大约的感觉,如果你在意性能的话,第一种会快些

   public DataTable QueryForDataTable(string statementName, object parameterObject)
        {
        
            bool isSessionLocal = false;
            ISqlMapSession session = _sessionStore.LocalSession;
            DataTable dataTable = null;

            if (session == null)
            {
                session = CreateSqlMapSession();
                isSessionLocal = true;
            }

            try
            {
                IMappedStatement statement = GetMappedStatement(statementName);
                dataTable = new DataTable(statementName);
                RequestScope request = statement.Statement.Sql.GetRequestScope(statement, parameterObject, session);
                statement.PreparedCommand.Create(request, session, statement.Statement, parameterObject);

                using (request.IDbCommand)
                {
                    dataTable.Load(request.IDbCommand.ExecuteReader());
            
                }

         
            }
            catch
            {
                throw;
            }
            finally
            {
                if (isSessionLocal)
                {
                    session.CloseConnection();
                }
            }

            return dataTable;

        }

修改一下以上代码,就 可以用来返回DataSet

    public DataSet QueryForDataSet(string statementName, object parameterObject)
        {

            bool isSessionLocal = false;
            ISqlMapSession session = _sessionStore.LocalSession;
            DataSet ds = new DataSet(statementName);

            if (session == null)
            {
                session = CreateSqlMapSession();
                isSessionLocal = true;
            }

            try
            {
                IMappedStatement statement = GetMappedStatement(statementName);
           
                RequestScope request = statement.Statement.Sql.GetRequestScope(statement, parameterObject, session);
                statement.PreparedCommand.Create(request, session, statement.Statement, parameterObject);

                using (request.IDbCommand)
                {
                    IDataReader reader = request.IDbCommand.ExecuteReader();
                    DataTable dt = new DataTable();
                    while(! reader.IsClosed){
                        dt.Load(reader);
                    }
                    ds.Tables.Add(dt);

                }


            }
            catch
            {
                throw;
            }
            finally
            {
                if (isSessionLocal)
                {
                    session.CloseConnection();
                }
            }

            return ds;

        }

下面的例子是利用反射,使用dataadapter来返回数据集

   public DataSet QueryForDataSet2(string statementName, object parameterObject)
        {
            bool isSessionLocal = false;
            ISqlMapSession session = _sessionStore.LocalSession;
            DataSet ds = new DataSet(statementName);

            if (session == null)
            {
                session = CreateSqlMapSession();
                isSessionLocal = true;
            }

            try
            {
                IMappedStatement statement = GetMappedStatement(statementName);

                RequestScope request = statement.Statement.Sql.GetRequestScope(statement, parameterObject, session);
                statement.PreparedCommand.Create(request, session, statement.Statement, parameterObject);
                FieldInfo info = request.IDbCommand.GetType().GetField("_innerDbCommand", BindingFlags.NonPublic | BindingFlags.Instance);

                using (IDbCommand cmd = (IDbCommand)info.GetValue(request.IDbCommand))
                {
                    session.CreateDataAdapter(cmd).Fill(ds);
                }

 

            }
            catch
            {
                throw;
            }
            finally
            {
                if (isSessionLocal)
                {
                    session.CloseConnection();
                }
            }

            return ds;
        }

[2007-07-17 04:37:29 | Author:jiangjianxiao ] [] 4 comments

ironpython 不支持_getframe

cpython的_getframe 简单的说就是能获得运行时的stack,比方说 下面的代码

import sys
def MyTestMethod():
    l= sys._getframe(1).f_locals
    print l['x']
    l['x']=9
   
class MyTest:
    x=0
    MyTestMethod()
  
if __name__=="__main__":
    t=MyTest()
    print t.x

 

你认为x的值是多少呢? 是9,_getframe获得了class MyTest的运行时stack,动态修改了成员的值,因此说 ,_getframe是实现dsl 最 简单的武器. elixir 就是如此完成的 http://elixir.ematia.de/ 

class Person(Entity):
    has_field('name', Unicode(255))

    has_many('addresses', of_kind='Address')


class Address(Entity):
    has_field('email', String(128))

    belongs_to('person', of_kind='Person')

[2007-07-15 08:44:03 | Author:jiangjianxiao ] [] 1 comments

google 侵袭

我不是google的fans ,但是, 得确,用gmail 作为我主要的邮件工具已经很久了.  除了易于存取外,gmail 最吸引我的特点是快. 我不了解为什么hotmail 会如此的慢? 程序应该不是主要的. 服务器的连接速度应该是主要因素.

我想,不管你的应用有如何的cool ,如果是慢吞吞的,那没有人会去尝试.特别是基于internet 的应用.

所以说,我一直不太看好google file ,理由同上.

最近,我这些看法可能又有所改变. 我正在使用google file 来代替word ,理由是它的发布特性,另外,我的本机文档也过于混乱,导致每次装机都会丢失许多.(写到这里,我在想,如果google 出现一个故障,丢失了用户数据,那结局会怎样? 上个月,我使用mytodo.cn 来保存hshg的todo 列表,结果服务停了几天不说,恢复后,列表已经被还原 到早先的一个备份.这使我心有余悸,导致现在everydo.com 虽然不错,但却不敢再次使用).

目前我正在使用并且非常频繁的google 应用

  • gmail
  • 书签
  • google file
  • google reader
  • 照片



在使用,但不频繁的包括

  • gtalk
  • 日历

说起来,我想todo比日历有用的多,特别是对于我们做soho的,时间的分配并不像上班族那样有规律,可惜到现在我还没有发现一个我喜欢的此类应用.

写这个blog的意图是, google应用的侵入有点像水煮青蛙,有点渐进式的.在几年前,我的google在应用上对ms的威胁不以为然,但现在,每次在使用这些东西都会有感觉. 所以说 ,尝试是非常有必要的,它能改变你的固有看法. 执著有时是可怕的.

[2007-07-14 06:08:21 | Author:jiangjianxiao ] [] 1 comments

Total 91 Display 21 of 25
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
Powered by Google App Engine