之前整理了一下XPO在Session管理和缓存方面的一些资料(XPO:Session管理与缓存--机制篇),但原文的例程还是有些含糊的地方,这两天抽空做了一下测试。若有不当或者不对的地方敬请不吝赐教。

 

XPO初始化的代码就不重复贴了,这里只贴上主要的代码。

测试中构建了2个简单的类,XpoUser和XpoOrder,一对多的关系。

XpoUser
using System;
using DevExpress.Xpo;

namespace Model
{
    
public class XpoUser : XPObject
    {
        
public XpoUser()
            : 
base()
        {
            
// This constructor is used when an object is loaded from a persistent storage.
            
// Do not place any code here.
        }

        
public XpoUser(Session session)
            : 
base(session)
        {
            
// This constructor is used when an object is loaded from a persistent storage.
            
// Do not place any code here.
        }

        
public override void AfterConstruction()
        {
            
base.AfterConstruction();
            
// Place here your initialization code.
        }

        
private string _Name;
        
public string Name
        {
            
get
            {
                
return _Name;
            }
            
set
            {
                SetPropertyValue(
"Name"ref _Name, value);
            }
        }

        [Association(
"XpoUser-Orders")]
        
public XPCollection<XpoOrder> Orders
        {
            
get
            {
                
return GetCollection<XpoOrder>("Orders");
            }
        }
    }
}

 

 

 

XpoOrder
using System;
using DevExpress.Xpo;

namespace Model
{
    
public class XpoOrder : XPObject
    {
        
public XpoOrder()
            : 
base()
        {
            
// This constructor is used when an object is loaded from a persistent storage.
            
// Do not place any code here.
        }

        
public XpoOrder(Session session)
            : 
base(session)
        {
            
// This constructor is used when an object is loaded from a persistent storage.
            
// Do not place any code here.
        }

        
public override void AfterConstruction()
        {
            
base.AfterConstruction();
            
// Place here your initialization code.
        }

        
private string _OrderID;
        
public string OrderID
        {
            
get
            {
                
return _OrderID;
            }
            
set
            {
                SetPropertyValue(
"OrderID"ref _OrderID, value);
            }
        }

        
private XpoUser _User;
        [Association(
"XpoUser-Orders")]
        
public XpoUser User
        {
            
get
            {
                
return _User;
            }
            
set
            {
                SetPropertyValue(
"User"ref _User, value);
            }
        }
    }
}

 

 

 

做了两个测试,测试一的代码:

Test1
            ClearDB();
            StringBuilder sb 
= new StringBuilder();
            Session s1 
= new Session();
            Session s2 
= new Session();
            XpoUser user1 
= new XpoUser(s1) { Name = "UserName" };
            user1.Save();
            XpoUser user2 
= s2.FindObject<XpoUser>(new BinaryOperator("Name""UserName"));
            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));

            sb.AppendLine(Environment.NewLine 
+ "Change user's name in Session 1");
            user1.Name 
= "New UserName";
            user1.Save();
            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));

            sb.AppendLine(Environment.NewLine 
+ "Create / Read something else(XpoRole) in Session 2");
            XpoRole role 
= new XpoRole(s2) { Name = "RoleName" };
            role.Save();
            role 
= s2.FindObject<XpoRole>(new BinaryOperator("Name""RoleName"));
            sb.AppendLine(
string.Format("Role name : {0}", role.Name));
            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));

            sb.AppendLine(Environment.NewLine 
+ "Create / Read another XpoUser in Session 2");
            XpoUser anotherUser 
= new XpoUser(s2) { Name = "Another User" };
            anotherUser.Save();
            anotherUser 
= s2.FindObject<XpoUser>(new BinaryOperator("Name""Another User"));
            sb.AppendLine(
string.Format("Another user's name : {0}", anotherUser.Name));
            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));

            sb.AppendLine(Environment.NewLine 
+ "Try to Save the user in Session 2, which is modified in Session 1");
            
try
            {
                user2.Save();
            }
            
catch (Exception ex)
            {
                sb.AppendLine(ex.Message);
            }
            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));

            sb.AppendLine(Environment.NewLine 
+ "Session.Reload(User) in Session 2");
            s2.Reload(user2);
            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));

            txtResult.Text 
= sb.ToString();

 

 

输出的结果:

[Session 1] User's Name : UserName

[Session 2] User's Name : UserName


Change user's name in Session 1

[Session 1] User's Name : New UserName

[Session 2] User's Name : UserName


Create / Read something else(XpoRole) in Session 2

Role name : RoleName

[Session 1] User's Name : New UserName

[Session 2] User's Name : UserName


Create / Read another XpoUser in Session 2

Another user's name : Another User

[Session 1] User's Name : New UserName

[Session 2] User's Name : UserName


Try to Save the user in Session 2, which is modified in Session 1

Cannot persist the object. It was modified or deleted (purged) by another application.

[Session 1] User's Name : New UserName

[Session 2] User's Name : UserName


Session.Reload(User) in Session 2

[Session 1] User's Name : New UserName

[Session 2] User's Name : New UserName

 

可以看到:

2个不同Session里Load同一个object,确实是同一个object被传递给了2个Session,而并非该Object的任何副本。

在一个Session中对该Object做了修改后,其他Session除非Reload该对象,否则将永远无法获得其他Session对其做的改动。

同时,试图保存这个已经被其他Session所更改过的对象时会抛错。

 

测试2:

Test2
            ClearDB();
            StringBuilder sb 
= new StringBuilder();
            Session s1 
= new Session();
            Session s2 
= new Session();
            XpoUser user1 
= new XpoUser(s1) { Name = "UserName" };
            XpoOrder order 
= new XpoOrder(s1) { OrderID = "#1", User = user1 };
            user1.Orders.Add(order);
            user1.Save();
            XpoUser user2 
= s2.FindObject<XpoUser>(new BinaryOperator("Name""UserName"));

            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 1] User's Order : {0}", GetOrders(user1)));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Order : {0}", GetOrders(user2)));


            sb.AppendLine(Environment.NewLine 
+ "Create a new order , change user's name and change Order1's ID to #3 in Session 1");
            order 
= s1.FindObject<XpoOrder>(new BinaryOperator("OrderID""#1"));
            order.OrderID 
= "#3";
            order.Save();
            order 
= new XpoOrder(s1) { OrderID = "#2", User = user1 };
            order.Save();
            user1.Name 
= "New UserName";
            user1.Orders.Add(order);
            user1.Save();
            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 1] User's Order : {0}", GetOrders(user1)));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Order : {0}", GetOrders(user2)));

            sb.AppendLine(Environment.NewLine 
+ "Session.Reload(User) in Session 2");
            s2.Reload(user2);
            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 1] User's Order : {0}", GetOrders(user1)));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Order : {0}", GetOrders(user2)));

            sb.AppendLine(Environment.NewLine 
+ "Session.Reload(User, forceAggregatesReload) in Session 2");
            s2.Reload(user2, 
true);
            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 1] User's Order : {0}", GetOrders(user1)));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Order : {0}", GetOrders(user2)));

            sb.AppendLine(Environment.NewLine 
+ "Load Order again in Session 2");
            order 
= s2.FindObject<XpoOrder>(new BinaryOperator("OrderID""#3"));
            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 1] User's Order : {0}", GetOrders(user1)));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Order : {0}", GetOrders(user2)));

            sb.AppendLine(Environment.NewLine 
+ "Session.Reload(Order, forceAggregatesReload) in Session 2");
            s2.Reload(order, 
true);
            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 1] User's Order : {0}", GetOrders(user1)));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Order : {0}", GetOrders(user2)));

            sb.AppendLine(Environment.NewLine 
+ "Load Order2 in Session 2");
            order 
= s2.FindObject<XpoOrder>(new BinaryOperator("OrderID""#2"));
            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 1] User's Order : {0}", GetOrders(user1)));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Order : {0}", GetOrders(user2)));

            sb.AppendLine(Environment.NewLine 
+ "Session.Reload(Order2, forceAggregatesReload) in Session 2");
            s2.Reload(order, 
true);
            sb.AppendLine(
string.Format("[Session 1] User's Name : {0}", user1.Name));
            sb.AppendLine(
string.Format("[Session 1] User's Order : {0}", GetOrders(user1)));
            sb.AppendLine(
string.Format("[Session 2] User's Name : {0}", user2.Name));
            sb.AppendLine(
string.Format("[Session 2] User's Order : {0}", GetOrders(user2)));

            txtResult.Text 
= sb.ToString();

 

 

上面代码其实测试了几种不同的情况,分别注释掉后部几个代码块单独跑可以看得更清楚,这里只贴全部跑的结果:

[Session 1] User's Name : UserName

[Session 1] User's Order : #1 

[Session 2] User's Name : UserName

[Session 2] User's Order : #1 


Create a new order , change user's name and change Order1's ID to #3 in Session 1

[Session 1] User's Name : New UserName

[Session 1] User's Order : #3 #2 

[Session 2] User's Name : UserName

[Session 2] User's Order : #1 


Session.Reload(User) in Session 2

[Session 1] User's Name : New UserName

[Session 1] User's Order : #3 #2 

[Session 2] User's Name : New UserName

[Session 2] User's Order : #1 


Session.Reload(User, forceAggregatesReload) in Session 2

[Session 1] User's Name : New UserName

[Session 1] User's Order : #3 #2 

[Session 2] User's Name : New UserName

[Session 2] User's Order : #1 


Load Order again in Session 2

[Session 1] User's Name : New UserName

[Session 1] User's Order : #3 #2 

[Session 2] User's Name : New UserName

[Session 2] User's Order : #3 


Session.Reload(Order, forceAggregatesReload) in Session 2

[Session 1] User's Name : New UserName

[Session 1] User's Order : #3 #2 

[Session 2] User's Name : New UserName

[Session 2] User's Order : #3 


Load Order2 in Session 2

[Session 1] User's Name : New UserName

[Session 1] User's Order : #3 #2 

[Session 2] User's Name : New UserName

[Session 2] User's Order : #3 #2 


Session.Reload(Order2, forceAggregatesReload) in Session 2

[Session 1] User's Name : New UserName

[Session 1] User's Order : #3 #2 

[Session 2] User's Name : New UserName

[Session 2] User's Order : #3 #2 

 

 

在这个例子里,刷新User没有获得Order的变更,刷新Order也没有获得User的变更,更没有获得同一个User下其他Order的变更。

对于有一对多关系的结构来说,无论是刷新哪个对象,用不用forceAggregatesReload,都只会刷新到自己,不会对关联对象做出更新。

是bug还是对forceAggregatesReload的理解有误?


 

另一个问题是,在Session下并没有找到DropCache()方法,只有一个DropIdentityMap()方法,而调用这个方法以后,再Reload其他对象会抛错(对象已被Dispose)。在DevExpress的官网上大概搜了一下,也没找到什么说法。

 


从上面的测试结果来看,我很同意DevExpress建议的,对每一组互相关联的少量数据都使用一个单独的Session,“大方”的使用。在保持数据之间的逻辑关系不会被破坏的情况下,颗粒度越小越好。 单一Session里包含的数据越少,则这些数据的时间跨度就越小,而且一般说来也越快会被更新。如果想彻底抛弃一个Session的缓存重新加载的话,最好就直接new一个Session来做,而不要去Drop什么的了。

 

 

 

 

posted on 2010-02-11 17:00  Elvin Chen  阅读(2373)  评论(2编辑  收藏  举报