[原创]Type.InvokeMember执行指定参数名方法问题

mikel阅读(1246)

Type.InvokeMember执行方法时候需要按参数名对应参数值时,指定了参数名和参数值,结果传递给方法的参数值和参数名不对应,这个问题很奇怪,调用时调换了参数名和值的顺序就可以,一直不知道为什么?

调用代码:

static void Main(string[] args)
{
Comp comp = new Comp();
Dictionary<String, object> param = new Dictionary<string, object>();
param.Add(“compclass”, “compclass”);
param.Add(“compmodule”, “compmodule”);

param.Add(“compid”, “compid”);

ReflectionHelper.InvokeMethod(comp,”saveinfo”,param.Values.ToArray<Object>(),param.Keys.ToArray<String>());
Console.Read();
}

方法代码:

public ViewData SaveInfo(string compid,string compclass,string compmodule)
{
try{ //传入的compid=”compmodule” compclass=”compid” compmodule=”compclass”

return ExecuteAction(compid, “AlterInfo”);
}catch(Exception e)
{
throw e;
}
}

调换了参数顺序后,则正常运行,调换后的代码:

static void Main(string[] args)
{
Comp comp = new Comp();
Dictionary<String, object> param = new Dictionary<string, object>();
param.Add(“compclass”, “compclass”);

param.Add(“compid”, “compid”);

param.Add(“compmodule”, “compmodule”);

//param.Add(“compcontent”, “compcontent”);

ReflectionHelper.InvokeMethod(comp,”saveinfo”,param.Values.ToArray<Object>(),param.Keys.ToArray<String>());
Console.Read();
}

最终无奈了,解决办法就是,读取Method的参数数组,然后按顺序取值存入Object[]数组中,反射执行方法,代码如下:

String methodName = GetMethodName(context).ToLower();//获得方法名
MethodInfo[] methods = ReflectionHelper.GetMethods(comp);

MethodInfo method = methods.Where<MethodInfo>(m => m.Name.ToLower().Equals(methodName)).ToList<MethodInfo>()[0];
//按参数顺序取参数值
Object[] values = new Object[] { };
Array.Resize<Object>(ref values, method.GetParameters().Length);
foreach (ParameterInfo info in method.GetParameters())
{
values[info.Position]=namedParameters[info.Name];
}

ViewData viewData = method.Invoke(comp, values) as ViewData;

[转载]flex 联机游戏开发 - 斗地主游戏:(一)核心逻辑

mikel阅读(1109)

[转载]flex 联机游戏开发 – 斗地主游戏:(一)核心逻辑 – 博弈居 – 开放的第三方应用游戏开发测试平台 – 博客园.

这是我希望开发的第一个纸牌类游戏,斗地主本身来说是一个有限用户的回合制游戏,核心逻辑与棋类游戏并无多大的不同,但是因为并无多大的行棋算法, 所以位置显得不那么的重要。所以我决定还是用开发四国军棋形成的flexchessapi来开发这个游戏,如果flexchessapi不能胜任这个工 作,我会考虑形成一个通用的flexcardapi来开发这个游戏。这样,我就能利用flexcardapi开发更多的游戏,比如诈金花等。

核心逻辑开发完成后效果如图所示

好了,现在我们开始设计一些纸牌类的一般

[转载]flex 联机游戏开发 - 斗地主游戏:(一)核心逻辑

mikel阅读(1211)

[转载]flex 联机游戏开发 – 斗地主游戏:(一)核心逻辑 – 博弈居 – 开放的第三方应用游戏开发测试平台 – 博客园.

这是我希望开发的第一个纸牌类游戏,斗地主本身来说是一个有限用户的回合制游戏,核心逻辑与棋类游戏并无多大的不同,但是因为并无多大的行棋算法, 所以位置显得不那么的重要。所以我决定还是用开发四国军棋形成的flexchessapi来开发这个游戏,如果flexchessapi不能胜任这个工 作,我会考虑形成一个通用的flexcardapi来开发这个游戏。这样,我就能利用flexcardapi开发更多的游戏,比如诈金花等。

核心逻辑开发完成后效果如图所示

好了,现在我们开始设计一些纸牌类的一般逻辑思考,放在一个静态类如CardHelp.as中。。

1) 设计一幅牌

我们对一幅牌的每一张牌应该给定一个值,每个值包括花色与本身的大小,这样,我们就以一个3位数的值来代表一张牌,比如303代表红花3,414 代表黑桃A,这样,我们就能够设计出一幅牌的完整数组,不同的牌需要的完整数不同,比如扎金花是不要王,斗地主是要王的,现在,我们设计一幅牌。

/**
*斗地主带王的一幅完整的牌
* 在斗地主中,2是比A还大的牌,我们不能叫102,就叫115吧。
*/
public static const CARDS_WITH_JOKER:Array=[115,103,104,105,106,107,108,109,110,111,112,113,114,215,203,204,205,206,207,208,209,210,211,212,213,214,315,303,304,305,306,307,308,309,310,311,312,313,314,415,403,404,405,406,407,408,409,410,411,412,413,414,518,519];

/**
*扎金花不带王的一幅完整的牌
*/
public static const CARDS_NO_JOKER:Array=[102,103,104,105,106,107,108,109,110,111,112,113,114,202,203,204,205,206,207,208,209,210,211,212,213,214,302,303,304,305,306,307,308,309,310,311,312,313,314,402,403,404,405,406,407,408,409,410,411,412,413,414];

设计两个帮助方法

/**
*获得特定处于的花色
* @param card
* @return
*
*/
public static function getHuaShe(value:int):int{
return Math.floor(value/100);
}
/**
* 获得特定牌的原始值
* @param card
* @return
*
*/
public static function getPureValue(value:int):int{
return value%100;
}

2)牌有了,我们开始设计一个发牌的函数

2.1)发一张牌,发牌函数相对比较简单,直接从一幅牌的数组中取一张即可,注意的是,取完后应该将该牌删除,以免下次再发一样的牌。

/**
* 获得牌组中一张随机的牌值
* @param cards
* @return
*
*/
public static function getRadomCard(cards:ArrayCollection):int{
if (cards.length==0) return -1;
var key:int=Math.round(Math.random()*(cards.length-1));
return cards.removeItemAt(key) as int;

}

2.2)发一手牌,对斗地主来说,一手牌就是17张,最后的三张就是底牌,在发牌的时候,我们考虑的是给牌排序,这样,发完牌后就自动从大到小排列。

/**
* 获得一个随机的一手牌。
* @param cards 全部牌
* @param num 牌的数量
* @param sort 是否排序
* @return
*
*/
public static function getRadomCards(cards:ArrayCollection,num:int,sort:Boolean=true):ArrayCollection{
var temp:ArrayCollection=new ArrayCollection();
do{
var cardvalue:int=getRadomCard(cards);
trace(“生成牌:”+getDisplayName(cardvalue));
if (!sort)
temp.addItem(cardvalue);
else
{
var added:Boolean=false;
for (var i:int=0;i<temp.length;i++)
{

var value:int=temp.getItemAt(i) as int;
if (cardvalue%100>value%100)
{
temp.addItemAt(cardvalue,i);
added=true;
break;
}

}
if (!added)
{
temp.addItemAt(cardvalue,i);
}

}

num–;
}while(num>0);
trace(“排序后:”+temp.toString());
return temp;
}

3)进入斗地主的逻辑,我们现在设计一个通用类叫 DoudizhuHelp.as。

斗地主游戏中的牌型很多,算法也各有不同,但我总觉得网上一些通用的算法有点铺天盖地,所以,我决定自己来重新想一下算法,总的来说,无论你出什么 处于,三带二,炸,4带一等,你总是有个主牌的,从牌只认张数,大小是无所谓的,所以,我将主牌清点出来应该就只有 对子,3条,炸弹,双王,顺子这五类,所以,在发牌或者拿到底牌后,我们直接将所有的主牌类型清理出来即可。

现在,写一个清理主牌类型的函数。

/**
*获得当前牌的所有牌型
* @return
*
*/
public function initType():void{

var temp:ArrayCollection=new ArrayCollection();

for (var i:int=0;i<_cardAC.length-1;i++)
{

var card1:ICard=_cardAC.getItemAt(i) as ICard;
var card2:ICard=_cardAC.getItemAt(i+1) as ICard;

//添加至双王
if (card1.pureValue==18||card1.pureValue==19)
{
if (!shuangwangAC.contains(card1.pureValue))
shuangwangAC.addItem(card1.pureValue);
}
//相同部分,我们只需要判断一次
if (card1.pureValue==card2.pureValue)
{

//添加至炸弹
if (this.santiaoAC.contains(card1.pureValue))
{
if (!this.zhadanAC.contains(card1.pureValue))
this.zhadanAC.addItem(card1.pureValue);
}

//添加至三条
else if (this.duizhiAC.contains(card1.pureValue))
{
this.santiaoAC.addItem(card1.pureValue);
}
//添加至对子
else
{
this.duizhiAC.addItem(card1.pureValue);
}

}
//添加至顺子
else if (card1.pureValue==card2.pureValue+1&&card2.pureValue!=3)
{
//2不能加入顺子中
if (card1.pureValue==15) continue;

if (!temp.contains(card1.pureValue))
temp.addItem(card1.pureValue);
if (!temp.contains(card2.pureValue))
temp.addItem(card2.pureValue);
}
//位数不等或者已经到达最后
else
{
if (card2.pureValue==3) temp.addItem(card2.pureValue);
if (temp.length>=5)
{
this.sunzhiAC.addAll(temp);
}
temp=new ArrayCollection();

}

}
//如果没有双王,则清空双王集合
if (shuangwangAC.length<2)
{
shuangwangAC.removeAll();

}

trace(“双王..”+this.shuangwangAC.toString());
trace(“对子..”+this.duizhiAC.toString());
trace(“三条…”+this.santiaoAC.toString());
trace(“顺子…”+this.sunzhiAC.toString());
trace(“炸弹….”+this.zhadanAC.toString());

}

分析每一句的意义。函数应该比较好懂,暂时还没发现什么bug,不过我总觉得不放心:),高手们可以帮我找找bug,谢谢了。。。

现在,我试着分析了一手牌。

双王..
对子..15,9,6,4,3
三条…15,9
顺子…10,9,8,7,6,5,4,3
炸弹….

在这一个月里,我已经开了太多的头,四国军棋 写到了3,五子棋 写到了2,中国象棋 写到了 1,所以我决定在再完成一个扎金花的游戏逻辑分析后,下一步就开始埋头做联机游戏服务器网站 www.boyiju.com 的开发与联机游戏客户端api的开发,然后对所有已经完成逻辑的游戏进行统一更新,在第三方应用发展迅猛的今天,我的目标是建成一个所有程序员可以轻松进 行第三方游戏应用开发测试的公用开源平台,平台将一个完整的sns网站测试环境与以下的各类api,感兴趣的朋友可以随时关注项目开发进展情况,如果你极 端感兴趣,或者您仍旧怀着工作部分为了理想活着的大志。你也可以加我QQ 2 1 0 7 3 4 5 2 了解这个项目或者参与这个项目的开发,您不必担心我,你面对的只是一个除了有点忙,没什么坏习惯的开发者。

[转载]Windows下获取Android系统源码

mikel阅读(1267)

[转载]Windows下获取Android系统源码 – 杨平 – youhei – 博客园.

接手Android也有一年的时间了,由于刚开始学习时资料比较少,又大多都是断章取义的,所以对于Android我一直处于似懂非懂的状态下。今天终于感觉到该留点学习的经验吧,那就从这篇开始我的android系列文章了。

Android使用Git管理源代码的,在Linux下我们可以使用ubuntu来获取系统源码,下面我就逐步介绍在Windows些如何获取Android系统源码了。

一。安装msysGit

在google的http://code.google.com/p/msysgit/中这样介绍到:

If you just want to use Git to do your version control in Windows, you will need to download Git for Windows, run the installer, and you are ready to start. Note: Git for Windows is a project run by volunteers, so if you want it to improve, volunteer!

If you want to download and install the complete source code of Git for Windows (including C compiler) to start hacking, you will need to install the full development system which is called msysGit.

注释:如果你需要使用Git工具在Windows下进行版本控制,你可以使用基于Windows的Git,这里提供了msysGit的安装下载。

我们选择full安装版本,http://code.google.com/p/msysgit/downloads/detail?name=msysGit-fullinstall-1.7.3.1-preview20101002.exe

下载msysGit安装包后安装,在安装过程中msysGit会自动的安装或编译基于Windows环境的Git。编译Git需要点时间,安装完后截图如下:

12

这就是Git的shell窗口,你可以在其中通过命名行操作下载得到Android的系统源码,下面就以Lunacher——桌面——为例下载源码。

二。下载Luancher源码

Android系统源码都在http://android.git.kernel.org/网址下,其中那个系统app都在platform/packages/apps结构下。

在刚才的Git的shell命名行窗口中输入下面的命名:

git clone git://android.git.kernel.org/platform/packages/apps/Luancher2.git

你就可以在你的msysGit安装目录的git下(~\msysgit\msysgit\git)看到Luancher工程文件夹了。

3456

然后打开~\msysgit\msysgit\git\Luancher2文件夹,就可以看到Android的Luancher系统源码工程了。

78

对于其他的app系统源码的获取方法和上述的Luancher相同,只是命名行后的参数不同。

git clone git://android.git.kernel.org/需下载源码的app所在的位置。

三。启动msysGit

有些朋友在关闭了msysGit命名行窗口后就找不到重新打开的链接了,不用着急,你只需要在你安装的msysGit路径下双击执行msys.bat文件即可。

所有的都大功告成了。

[转载]Android根据Button状态(normal,focused,pressed)显示不同背景图片

mikel阅读(1107)

[转载]Android根据Button状态(normal,focused,pressed)显示不同背景图片 – Android平台开发技术 – 博客园.

Android中Button 有focused, selected, pressed 等不同状态,通过配置一个XML格式的 drawable “selector” 即可实现”在不同状态下显示不同背景图片“的功能。

1. 在res/drawable目录下添加一个xml文件,用来描述Button在不同状态下对应的不同图片。我这里给该xml文件命名为btn_background.xml



<?xml version="1.0" encoding="UTF-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@drawable/btn_pressed" /> <!-- pressed --> <item android:state_focused="true" android:drawable="@drawable/btn_normal" /> <!-- focused --> <item android:drawable="@drawable/btn_normal" /> <!-- default --> </selector>

2. 在res/layout目录下,对应的layout xml文件中,将Button的Android:background属性设置为btn_background即可。

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/btn_background" /> </LinearLayout>

3.运行结果

默认状态(unselected)

点击状态(pressed)


参考资料:

Google Android开发文档:ImageButton

http://developer.android.com/reference/android/widget/ImageButton.html

我的Twitter @mainroadlee

[转载]Android编程获取手机型号,本机电话号码,sdk版本及firmware版本号(即系统版本号)

mikel阅读(1063)

[转载]Android编程获取手机型号,本机电话号码,sdk版本及firmware版本号(即系统版本号) – Android平台开发技术 – 博客园.

Android开发平台中,可通过TelephonyManager 获取本机号码。

TelephonyManager phoneMgr=(TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE); txtPhoneNumber.setText(phoneMgr.getLine1Number()); //txtPhoneNumber是一个EditText 用于显示手机号

注:


Android的安全机制,在使用TelephonyManager时,必须在AndroidManifest.xml中添加<uses-
permission android:name=”READ_PHONE_STATE” /> 否则无法获得系统的许可。


手机型号 Build.MODEL

String MODEL The end-user-visible name for the end product.

sdk版本 Build.VERSION.SDK

String SDK This constant is deprecated. Use SDK_INT to easily get this as an integer.

及frimware版本号(系统版本号) Build.VERSION.RELEASE

String RELEASE The user-visible version string.


private void loadPhoneStatus() { TelephonyManager phoneMgr=(TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE); txtPhoneModel.setText(Build.MODEL); //手机型号 txtPhoneNumber.setText(phoneMgr.getLine1Number());//本机电话号码 txtSdkVersion.setText(Build.VERSION.SDK);//SDK版本号 txtOsVersion.setText(Build.VERSION.RELEASE);//Firmware/OS 版本号 }

事实上,Build能向我们提供包括 硬件厂商,硬件编号,序列号等很多信息 调用方法也都同上,很简单。

String BOARD The name of the underlying board, like “goldfish”.
String BOOTLOADER The system bootloader version number.
String BRAND The brand (e.g., carrier) the software is customized for, if any.
String CPU_ABI The name of the instruction set (CPU type + ABI convention) of native code.
String CPU_ABI2 The name of the second instruction set (CPU type + ABI convention) of native code.
String DEVICE The name of the industrial design.
String DISPLAY A build ID string meant for displaying to the user
String FINGERPRINT A string that uniquely identifies this build.
String HARDWARE The name of the hardware (from the kernel command line or /proc).
String HOST
String ID Either a changelist number, or a label like “M4-rc20”.
String MANUFACTURER The manufacturer of the product/hardware.
String MODEL The end-user-visible name for the end product.
String PRODUCT The name of the overall product.
String RADIO The radio firmware version number.
String SERIAL A hardware serial number, if available.
String TAGS Comma-separated tags describing the build, like “unsigned,Debug”.
long TIME
String TYPE The type of build, like “user” or “eng”.
String UNKNOWN Value used for when a build property is unknown.
String USER

最近在做韩国一家公司的Android平台软件开发,我的手机号是韩国的啦。所以看到010打头的号码,别太惊讶..

我的twitter: @mainroadlee

手头做的项目涉及到Android平台中网络设置,数据库,http协议下的数据传输,支付系统,以及SNS API(twitter, facebook, me2day, etc), QR码扫描等模块。

如果大家感兴趣的话,我会在开发闲暇时间陆续将常用的一些模块的开发调用,写成独立,简洁的教程发上来。

[转载]android开发我的新浪微博客户端-用户授权页面功能篇(3.2) – 遇见未知的自己 – 博客园

mikel阅读(1043)

[转载]android开发我的新浪微博客户端-用户授权页面功能篇(3.2) – 遇见未知的自己 – 博客园.

==》

上一篇实现了用户授权页面的UI,如上图,接下来要做的就是在这个基础上完成功能部分真正实现用户的授权认证,这一篇是android开发我的新浪微博客户端-OAuth篇(2.1)的具体应用篇原理就不多解释了不懂的看OAuth篇即可。认证过程从点击开始按钮然后跳转到新浪的授权页面,接着用户在新浪的页面里输入自己的账户和密码确定后返回用户授权页面。首先给开始按钮添加点击事件代码,代码中主要是调用我们前面android开发我的新浪微博客户端-OAuth篇(2.1)完成的OAuth类的RequestAccessToken方法用来获取oauth_verifier,具体代码如下:

代码

ImageButton stratBtn=(ImageButton)diaView.findViewById(R.id.btn_start);
stratBtn.setOnClickListener(
new OnClickListener(){

@Override
public void onClick(View arg0) {
auth
=new OAuth();
auth.RequestAccessToken(AuthorizeActivity.
this, CallBackUrl);
}

});

上面的代码中重点来说明一下 RequestAccessToken方法的第二参数CallBackUrl,这个参数是用户在新浪的页面中输入账户密码 后完成认证后返回的地址,我这里是这样设置的CallBackUrl = “myapp://AuthorizeActivity”,在AndroidManifest.xml中配置给AuthorizeActivity添加如 下配置把myapp://AuthorizeActivity指向到AuthorizeActivity,这样当页面返回到 AuthorizeActivity中就可以获取到传过来的oauth_verifier参数。

代码

<intent-filter>
<action Android:name=”Android.intent.action.VIEW” />
<category android:name=”android.intent.category.DEFAULT” />
<category android:name=”android.intent.category.BROWSABLE” />
<data android:scheme=”myapp” android:host=”AuthorizeActivity” />
</intent-filter>

再AuthorizeActivity如果来接收返回的oauth_verifier参数呢?接下来在AuthorizeActivity添加如下方法:

@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//在这里处理获取返回的oauth_verifier参数
}

关于onNewIntent的说明是这样的,onCreate是用来创建一个Activity也就是创建一个窗体,但一个Activty处于任务栈的顶 端,若再次调用startActivity去创建它,则不会再次创建。若你想利用已有的Acivity去处理别的Intent时,你就可以利用 onNewIntent来处理。在onNewIntent里面就会获得新的Intent,在这里AuthorizeActivity是属于已有的 Acivity,所以需要onNewIntent来处理接收返回的参数,获取oauth_verifier参数后OAuth还没有结束从android开发我的新浪微博客户端-OAuth篇(2.1)描述来看还需要进行根据这个参数继续向新浪微博请求获取User_id、Access Token和Access Secret,在这里我把这些操作全部写在了GetAccessToken方法中。在onNewIntent添加如下代码:

代码

UserInfo user= auth.GetAccessToken(intent);
if(user!=null){
DataHelper helper
=new DataHelper(this);
String uid
=user.getUserId();
if(helper.HaveUserInfo(uid))
{
helper.UpdateUserInfo(user);
Log.e(
UserInfo, update);
}
else
{
helper.SaveUserInfo(user);
Log.e(
UserInfo, add);
}
}

通过上面的代码完成了User_id、Access Token和Access Secret 获取并且保存到了SQLite库中,这样就完成了用户的OAuth认证,当需要调用新浪的api时只需要去SQLite库中找该用户的User_id、Access Token和Access Secret 即可。到这里本篇就结束了,请关注下一篇。

[转载]使用VS2010编译QOAuth支持微博通用认证OAuth实现SINA微博登陆 - 风雷云雪电 - 博客园

mikel阅读(1235)

[转载]微博是个大金矿,使用VS2010编译QOAuth支持微博通用认证OAuth实现SINA微博登陆 – 风雷云雪电 – 博客园.

随着Twitter的兴起和国内Sina和QQ等公司的追随,微博现在是如日中天,将传统的SNS给完全比拼下去,微博对于大家来说完全是个尚未完 全开采的大金矿,对于一直站在潮流最前端的程序员来说怎么能将这么好的机会错失呢。在这里我抛砖引玉,先介绍下如何在Qt平台上编译QAuth来支持现在 微博的通用认证OAuth

根据Twitter的API Wiki,基本的OAuth验证workflow如下:
1. 程序利用http://api.twitter.com/oauth/request_token来从twitter.com那里获取一个request token。
2. 然后程序引导用户到http://api.twitter.com/oauth/authorize页面。
3. 用户如果同意授权,twitter.com则会显示一个7位数字的PIN码。
4. 用户需要将PIN码复制,然后回到程序那里。
5. 之后程序要提示用户输入得到的PIN码。
6. 然后程序将PIN码作为参数oauth_verifier的值,接着调用http://api.twitter.com/oauth/access_token去核实PIN码,从而将request_token 换成access_token。
7. Twitter之后会返回一个access_token,程序就此token来生成之后的OAuth签名。

OAuth本身不是很复杂,不过如果有个库可以作为验证的helper的话,就可以省事很多了,由于最近一直在用Qt做开发,在上篇文章中我介绍了Qt并介绍了如何在VS2010中编译Qt环境,如果对Qt不熟悉的朋友可以点这里查看。顺理成章的是我希望在Qt框架下找到一个能支持OAuth的类库来简化开发,通过放狗进行搜索,搜到有个QOAuth的东西,是在Qt下的OAuth库,不过仔细一看,原来在使用QOauth之前还需要OpenssL,QCA和QCA的OpenssL插件,晕啊!

闲话就不说了,咱们进入正题

  • openssL

首先得到OpenssL的官网上下载OpenssL,http://www.openssl.org/,我下载的是openssl-1.0.0c.tar.gz,也是最新的,如果大家不想编译也可以到网上搜索别人编译好的库。

下载好后解压缩到硬盘中,我的路径是 H:\openssl-1.0.0c,由于OpenssL使用Perl生成makefile,所以需要先下载Perl,我下载的ActivePerl,下载好后进行安装。

Perl安装好后,运行Visual Studio Command Prompt (2010),不要运行普通的控制台。

键入命令 perl Configure VC-WIN32

image

如果输入错误会有明显提示,需要注意的是VC-W32必须是大写

等完成后键入命令 ms\do_ms

image

等完成后输入 nmake -f ms\ntdll.mak

image

编译成功后生成的lib,dll等库文件在out32dll中,头文件在include中,这些目录在以后的编译中需要加入到项目头文件目录和lib包含目录中。

  • 编译支持OpenssL的Qt

由于默认情况下Qt编译出来时不支持OpenssL的,需要重新编译下Qt,囧

基本步骤和我这篇文章 Qt简介以及如何配置Qt使用VS2010进行开发 中所描述的基本一致,唯一不同的在于configure的时候需要加上-openssl -I openssL头文件目录 -L openssLlib文件目录

如我的环境下是这样子滴

configure -platform win32-msvc2010 -openssl -I H:\openssl-1.0.0c\include -L H:\openssl-1.0.0c\out32dll

  • QCA

QCA需要到这个网站http://delta.affinix.com/qca/下载,同样的这个网站提供的QCA的OpenssL插件下载

QCA我下载的 qca-2.0.3.tar.bz2

QCA的OpenssL插件我下载的是 qca-ossl-2.0.0-beta3.tar.bz2

下载好后解压缩到硬盘,我的路径是 H:\qca-2.0.3,打开系统的环境变量,将QTDIR加入系统环境变量,如图所示

image

打开VS2010,Qt菜单(如果没有这个菜单,请查看我的上一篇文章),Open Qt Project File(.pro)…,选择QCA的安装目录下的qca.pro,稍等一会儿,自动将PRO项目转换成VS2010项目并加载了QCA下的所有项目

image

工程下有很多项目,我们只要编译qca项目即可,直接编译会出错,需要进行下设置

展开qca项目,在Generated Files下的Debug和release目录下找到qpipe.moc文件,右键属性,显示下图界面

image

在Command Line的最后面加上-DQ_OS_WIN,确定后,再编译,OK一切都安静了

  • QCA的Openssl插件

下载好后解压缩到硬盘,在Visual Studio Command Prompt (2010)中进入压缩目录,我的是在H:\qca-ossl-2.0.0-beta3

执行下面命令: configwin rd

image

新 建一个文本文件,输入WINLOCAL_PREFIX = C:/local,C:/Local可以是任何你想指定的目录,这个目录是作为零时文件的存放路径,将其保存到你的Qt目录下的 mkspecs\features目录中,文件名为winlocal.prf,注意不要保存成文本文件,我的保存路径是H:\Qt\2010.05\qt \mkspecs\features,供大家参考。

image

打开VS2010,Qt菜单(如果没有这个菜单,请查看我的上一篇文章),Open Qt Project File(.pro)…,选择QCA的OpenSSL插件的的安装目录下的qca-ossl.pro,稍等一会儿生成了QCA的OpenSSL插件的VS项目

将OpenssL的头文件路径和lib文件路径添加到项目中

在项目中搜索所有的EVP_md2,一共有4个,加上宏定义#ifndef OPENSSL_NO_MD2…#endif,如图示

image

编译之,在lib文件夹中获取劳动成果dll和lib文件两颗,拷贝进Qt的Plugins的crypto中,如果没有这个目录,手动建立目录。在以后编译的项目中需要将这个目录拷贝到exe文件的目录中即可。

  • QOAuth

从QOAuth的官方网站https://github.com/ayoy/qoauth下载QOAuth

在硬盘解压缩,我的目录是H:\ayoy-qoauth-18dbc19

通用的打开VS2010,Qt菜单(如果没有这个菜单,请查看我的上一篇文章),Open Qt Project File(.pro)…,选择QOAuth目录下的qoauth.pro,稍等片刻生成项目文件

在qoauth项目中的interface.h文件中点击右键,属性,如下图所示

image

将.\GeneratedFiles\$(ConfigurationName)\加入到include目录中

image

编译qoauth项目,成功后在lib文件中找到dll和lib

编译好之后得安装。具体做法是:
将oauth.prf复制到QTDIR\mkspecs\features里面,
将include和src复制到QTDIR\include里面,src里面只需有头文件即可
将生成的dll复制到QTDIR\lib,如果编译时候提示找不到这两个dll,可能是因为这是的dll名字包含版本号,去掉就ok了。。

用了QOAuth之后,编译出来的程序需要
+OpenSSL的链接库:(如果不带上OpenSSL的话,程序仍然可以启动,QCA会显示插件不支持的,然后会中止程序,算是个小陷阱)
-libeay32.dll,libssl32.dll,ssleay32.dll
+QCA的链接库:
-qcad2.dll
+QOAuth的链接库
-qoauth1.dll/qoauthd1.dll

[转载]优化OEA中的聚合SQL

mikel阅读(1095)

[转载]优化OEA中的聚合SQL – 哲学驱动设计 – 博客园.

之前写过几篇关于聚合对象SQL的文章,讲的是如果设计框架,使用一句SQL语句来加载整个聚合对象树中的所有数据。相关内容,参见:《性能优化总结(二):聚合SQL》、《性能优化总结(三):聚合SQL在GIX4中的应用》。 由于没有使用其它的ORM框架,当时项目组决定做聚合SQL,主要是为了减少SQL查询的次数,来提升部分模块的性能。现在看来,当时虽然达到了这个目 标,但是聚合SQL的API却不简单,使用极为不便。至今,项目组中的其它人也不会使用。所以,这次我们决定把聚合SQL的API使用再次进行封装,以达 到使用起来更简便的效果。

本文中的内容与前面几篇的内容、与OEA框架中的内容相关性比较大,有兴趣的朋友可以关注CodePlex中的项目:《OpenExpressApp

结果对比


优化前的代码,在前面的文章中已经有所展示。这里主要看一下优化过后的代码:

最简单的聚合SQL生成:

1 var sqlSimple = AggregateSQL.Instance.GenerateQuerySQL<PBS>(
2 option => option.LoadChildren(pbs => pbs.PBSBQItems),
3 pbsTypeId
4 );

这样就生成了如下SQL:

SELECT
pbs0.pid as PBS_pid, pbs0.pbstypeid as PBS_pbstypeid, pbs0.code as PBS_code, pbs0.name as PBS_name, pbs0.fullname as PBS_fullname, pbs0.description as PBS_description, pbs0.pbssubjectid as PBS_pbssubjectid, pbs0.orderno as PBS_orderno, pbs0.id as PBS_id,
pbsbqi1.pbsid as PBSBQItem_pbsid, pbsbqi1.code as PBSBQItem_code, pbsbqi1.name as PBSBQItem_name, pbsbqi1.unit as PBSBQItem_unit, pbsbqi1.bqdbid as PBSBQItem_bqdbid, pbsbqi1.id as PBSBQItem_id
FROM PBS AS pbs0
LEFT OUTER JOIN PBSBQItem AS pbsbqi1 ON pbsbqi1.PBSId = pbs0.Id
WHERE pbs0.PBSTypeId = ‘084a7db5-938a-4c7b-8d6a-612146ad87f9’
ORDER BY pbs0.Id, pbsbqi1.Id

该SQL用于加载聚合根对象PBSType下的所有PBS子对象,同时每个PBS的子对象PBSBQItems也都被同时查询出来。

再进一步,我们还可以直接使用聚合关系加载出对象,而不需要SQL,如:

1 var pbsList = AggregateSQL.Instance.LoadEntities<PBS>(
2 option => option.LoadChildren(pbs => pbs.PBSBQItems),
3 pbsTypeId
4 );

这样,API内部会生成聚合SQL,并进行聚合对象的加载。相对以前的模式,易用性提高了许多。这里,再给出一个目前支持的比较完整的API示例:

1 var projectPBSs = AggregateSQL.Instance.LoadEntities<ProjectPBS>(loadOptions =>
2 loadOptions.LoadChildren(pp => pp.ProjectPBSPropertyValues)
3 .Order<ProjectPBSPropertyValue>().By(v => v.PBSProperty.OrderNo)
4 .LoadFK(v => v.PBSProperty).LoadChildren(p => p.PBSPropertyOptionalValues),
5 criteria.ProjectId
6 );

表 示:加载ProjectPBS的对象列表时:同时加载它每一个ProjectPBS的子对象列表ProjectPBSPropertyValues,并把 ProjectPBSPropertyValues按照外键PBSProperty的OrderNo属性进行排序;同时,加载 ProjectPBSPropertyValue.PBSProperty、加载 PBSProperty.PBSPropertyOptionalValues。(其中,Order方法需要使用泛型方法指明类型是因为目前的实体列表都 是非泛型的,不能进行类型推断。)

总体设计


本次设计,主要是以提高模块的易用性为目的。

在原有的设计中,主要有两个步骤,生成聚合SQL 和 从大表中加载聚合对象。这两个过程是比较独立的。它们之间耦合的地方有两个。首先,是为表生成什么样的列名,生成SQL时按照这种列名的约定进行生成,加 载对象时则在大表中找对应列的数据。其次,它们还隐含耦合一些说明性的数据,这些数据指明了需要加载哪些子属性或者外键,什么样的加载关系,对应一个什么 样的聚合SQL,也就对应加载出来的对象。

也就是说,上述两个过程需要完整的封装起来,我们需要管理好这两个部分。而列名的生成在原来的模式中已经使用了“表名+列名”的格式进行了约定,所以现在 我们只需要把“描述如何加载的描述性数据”进行管理就可以了。有了这些数据,则可以在框架内部生成聚合SQL,在框架内部按照它们进行大表到聚合对象的加 载。以下,我将这些数据称为聚合对象的“加载选项”。

同时,考虑到聚合SQL生成的复杂性及使用的2/8原则,这次的聚合SQL自动生成和加载只处理比较简单的情况:只处理简单的链式的加载。例如:A对象作为Root的子对象,它还有子对象B、C,B有子对象D、E,D有外键引用对象F、F有子对象G,那么,只处理链式的加载意味着,最多可以在加载某个Root对象的A集合的同时,带上A.B、B.C、C.D、D.F、F.G。

image

如上图所示,在加载A.B的时候,不支持加载A.C;同理,加载B.D的时候,不支持加载B.E。其实在实际运用当中,这样的局限性在使用的时候并没有太大的问题,一是较多的使用场景不需要同时加载所有的子,二是可以分两条线加载对象后,再使用对象进行数据的融合。

核心数据结构 – 加载选项


上面已经说明了加载选项是整个聚合SQL加载的描述数据,描述如何生成SQL,描述如何加载对象。它其实也就是整个过程中的核心对象,由于时间有限(预计 只有一天时间完成整个设计及代码实现),而且这个对象并不会直接暴露在外面,所以这直接使用了最简单的链表类型来表示链式的加载选项。(老实说,这个设计 的扩展性并不好。)

01 /// <summary>
02 /// 聚合加载描述器。
03 ///
04 /// 目前只包含一些聚合加载选项“AggregateSQLItem”
05 /// </summary>
06 internal class AggregateDescriptor
07 {
08 private LinkedList<LoadOptionItem> _items = new LinkedList<LoadOptionItem>();
09
10 /// <summary>
11 /// 所有的AggregateSQLItem
12 /// </summary>
13 internal LinkedList<LoadOptionItem> Items
14 {
15 get
16 {
17 return _items;
18 }
19 }
20
21 /// <summary>
22 /// 直接加载的实体类型
23 /// </summary>
24 internal Type DirectlyQueryType
25 {
26 get
27 {
28 return this._items.First.Value.OwnerType;
29 }
30 }
31
32 /// <summary>
33 /// 追加一个聚合加载选项
34 /// </summary>
35 /// <param name="item"></param>
36 internal void AddItem(LoadOptionItem item)
37 {
38 this._items.AddLast(item);
39 }
40 }

而它包含的每一个元素 LoadOptionItem 则表示一个加载项,它主要包含一个属性的元数据,用于表示要级联加载的子对象集合属性或者外键引用对象属性。

01 /// <summary>
02 /// 生成聚合SQL的加载项中的某一项
03 /// </summary>
04 [DebuggerDisplay("{OwnerType.Name}.{PropertyEntityType.Name}")]
05 internal class LoadOptionItem
06 {
07 private Action<Entity, Entity> _fkSetter;
08
09 /// <summary>
10 /// 加载这个属性。
11 /// </summary>
12 internal IPropertyInfo PropertyInfo { get; private set; }
13
14 internal Func<Entity, object> OrderBy { get; set; }
15
16 /// <summary>
17 /// 指标这个属性是一般的实体
18 /// </summary>
19 internal AggregateLoadType LoadType
20 {
21 get
22 {
23 return this._fkSetter == null ? AggregateLoadType.Children : AggregateLoadType.ReferenceEntity;
24 }
25 }
26
27 //.......
28 }
29
30 /// <summary>
31 /// 属性的加载类型
32 /// </summary>
33 internal enum AggregateLoadType
34 {
35 /// <summary>
36 /// 加载子对象集合属性
37 /// </summary>
38 Children,
39
40 /// <summary>
41 /// 加载外键引用实体。
42 /// </summary>
43 ReferenceEntity
44 }

对象加载


按照上面的加载选项的链式设计,SQL生成其实就比较简单了:列名生成还是使用原有的方法,其它部分则只需要按照元数据进行链式生成就行了。花些时间就搞定了。

框架中对象的聚合加载的实现,和手写时一样,也是基于原有的ReadFromTable方法的,也不复杂,贴下代码,不再一一描述:

001 /// <summary>
002 /// 聚合实体的加载器
003 /// </summary>
004 internal class AggregateEntityLoader
005 {
006 private AggregateDescriptor _aggregateInfo;
007
008 internal AggregateEntityLoader(AggregateDescriptor aggregate)
009 {
010 if (aggregate == null) throw new ArgumentNullException("aggregate");
011 if (aggregate.Items.Count < 1) throw new InvalidOperationException("aggregate.Items.Count < 2 must be false.");
012
013 this._aggregateInfo = aggregate;
014 }
015
016 /// <summary>
017 /// 通过聚合SQL加载整个聚合对象列表。
018 /// </summary>
019 /// <param name="sql"></param>
020 /// <returns></returns>
021 internal EntityList Query(string sql)
022 {
023 IGTable dataTable = null;
024
025 IDbFactory dbFactory = this._aggregateInfo.Items.First.Value.OwnerRepository;
026 using (var db = dbFactory.CreateDb())
027 {
028 dataTable = db.QueryTable(sql);
029 }
030
031 //使用dataTable中的数据 和 AggregateDescriptor 中的描述信息,读取整个聚合列表。
032 var list = this.ReadFromTable(dataTable, this._aggregateInfo.Items.First);
033
034 return list;
035 }
036
037 /// <summary>
038 /// 根据 optionNode 中的描述信息,读取 table 中的数据组装为对象列表并返回。
039 ///
040 /// 如果 optionNode 中指定要加载更多的子/引用对象,则会递归调用自己实现聚合加载。
041 /// </summary>
042 /// <param name="table"></param>
043 /// <param name="optionNode"></param>
044 /// <returns></returns>
045 private EntityList ReadFromTable(IGTable table, LinkedListNode<LoadOptionItem> optionNode)
046 {
047 var option = optionNode.Value;
048 var newList = option.OwnerRepository.NewList();
049 newList.ReadFromTable(table, (row, subTable) =>
050 {
051 var entity = option.OwnerRepository.Convert(row);
052
053 EntityList listResult = null;
054
055 //是否还有后继需要加载的对象?如果是,则递归调用自己进行子对象的加载。
056 var nextNode = optionNode.Next;
057 if (nextNode != null)
058 {
059 listResult = this.ReadFromTable(subTable, nextNode);
060 }
061 else
062 {
063 listResult = this.ReadFromTable(subTable, option.PropertyEntityRepository);
064 }
065
066 //是否需要排序?
067 if (listResult.Count > 1 && option.OrderBy != null)
068 {
069 listResult = option.PropertyEntityRepository.NewListOrderBy(listResult, option.OrderBy);
070 }
071
072 //当前对象是加载类型的子对象还是引用的外键
073 if (option.LoadType == AggregateLoadType.Children)
074 {
075 listResult.SetParentEntity(entity);
076 entity.LoadCSLAProperty(option.CslaPropertyInfo, listResult);
077 }
078 else
079 {
080 if (listResult.Count > 0)
081 {
082 option.SetReferenceEntity(entity, listResult[0]);
083 }
084 }
085
086 return entity;
087 });
088
089 return newList;
090 }
091
092 /// <summary>
093 /// 简单地从table中加载指定的实体列表。
094 /// </summary>
095 /// <param name="table"></param>
096 /// <param name="repository"></param>
097 /// <returns></returns>
098 private EntityList ReadFromTable(IGTable table, EntityRepository repository)
099 {
100 var newList = repository.NewList();
101
102 newList.ReadFromTable(table, (row, subTable) => repository.Convert(row));
103
104 return newList;
105 }
106 }

美化的API


基于以上的基础,我们需要一个流畅的API来定义加载选项。这一点对于一个框架设计人员来说,往往很重要,只有流畅、易用的API才能对得起你的客户:框架使用者。以下我只把给出几个为达到流畅API而特别设计的类。其中,用到了《小技巧 – 简化你的泛型API》中提到的设计原则。

001 /// <summary>
002 /// 存储了加载选项项
003 /// </summary>
004 public abstract class LoadOptionSelector
005 {
006 internal LoadOptionSelector(AggregateDescriptor descriptor)
007 {
008 _descriptor = descriptor;
009 }
010
011 private AggregateDescriptor _descriptor;
012
013 internal AggregateDescriptor InnerDescriptor
014 {
015 get
016 {
017 return _descriptor;
018 }
019 }
020 }
021
022 /// <summary>
023 /// 属性选择器
024 /// </summary>
025 /// <typeparam name="TEntity"></typeparam>
026 public class PropertySelector<TEntity> : LoadOptionSelector
027 where TEntity : Entity
028 {
029 internal PropertySelector(AggregateDescriptor descriptor) : base(descriptor) { }
030
031 /// <summary>
032 /// 需要同时加载外键
033 /// </summary>
034 /// <typeparam name="TFKEntity"></typeparam>
035 /// <param name="fkEntityExp">
036 /// 需要加载的外键实体属性表达式
037 /// </param>
038 /// <returns></returns>
039 public PropertySelector<TFKEntity> LoadFK<TFKEntity>(Expression<Func<TEntity, TFKEntity>> fkEntityExp)
040 where TFKEntity : Entity
041 {
042 var entityPropertyName = GetPropertyName(fkEntityExp);
043 var propertyName = entityPropertyName + "Id";
044
045 IEntityInfo entityInfo = ApplicationModel.GetBusinessObjectInfo(typeof(TEntity));
046 var propertyInfo = entityInfo.BOPropertyInfos.FirstOrDefault(p => p.Name == propertyName);
047
048 //构造一个临时代理方法,实现:TEntity.EntityProperty = TFKEntity
049 var pE = System.Linq.Expressions.Expression.Parameter(typeof(TEntity), "e");
050 var pEFK = System.Linq.Expressions.Expression.Parameter(typeof(TFKEntity), "efk");
051 var propertyExp = System.Linq.Expressions.Expression.Property(pE, entityPropertyName);
052 var body = System.Linq.Expressions.Expression.Assign(propertyExp, pEFK);
053 var result = System.Linq.Expressions.Expression.Lambda<Action<TEntity, TFKEntity>>(body, pE, pEFK);
054 var fkSetter = result.Compile();
055
056 var option = new LoadOptionItem(propertyInfo, (e, eFK) => fkSetter(e as TEntity, eFK as TFKEntity));
057
058 //避免循环
059 if (this.InnerDescriptor.Items.Any(i => i.OwnerType == option.PropertyEntityType))
060 {
061 throw new InvalidOperationException("有循环的实体设置。");
062 }
063
064 this.InnerDescriptor.AddItem(option);
065
066 return new PropertySelector<TFKEntity>(this.InnerDescriptor);
067 }
068
069 /// <summary>
070 /// 需要同时加载孩子
071 /// </summary>
072 /// <typeparam name="TChildren"></typeparam>
073 /// <param name="propExp">
074 /// 需要加载的孩子属性表达式
075 /// </param>
076 /// <returns></returns>
077 public ChildrenSelector LoadChildren<TChildren>(Expression<Func<TEntity, TChildren>> propExp)
078 where TChildren : EntityList
079 {
080 var propertyName = GetPropertyName(propExp);
081 IEntityInfo entityInfo = ApplicationModel.GetBusinessObjectInfo(typeof(TEntity));
082 var propertyInfo = entityInfo.BOsPropertyInfos.FirstOrDefault(p => p.Name == propertyName);
083
084 this.InnerDescriptor.AddItem(new LoadOptionItem(propertyInfo));
085
086 return new ChildrenSelector(this.InnerDescriptor);
087 }
088
089 private static string GetPropertyName<TProperty>(Expression<Func<TEntity, TProperty>> propExp)
090 {
091 var member = propExp.Body as MemberExpression;
092 var property = member.Member as PropertyInfo;
093 if (property == null) throw new ArgumentNullException("property");
094 var propertyName = property.Name;
095
096 return propertyName;
097 }
098 }
099
100 /// <summary>
101 /// 孩子选择器
102 /// </summary>
103 /// <typeparam name="TEntity"></typeparam>
104 public class ChildrenSelector : LoadOptionSelector
105 {
106 internal ChildrenSelector(AggregateDescriptor descriptor) : base(descriptor) { }
107
108 public OrderByLoadOption<TEntity> Order<TEntity>()
109 where TEntity : Entity
110 {
111 return new OrderByLoadOption<TEntity>(this.InnerDescriptor);
112 }
113
114 /// <summary>
115 /// 把孩子集合转换为实体对象,需要继续加载它的子对象
116 /// </summary>
117 /// <typeparam name="TEntity"></typeparam>
118 /// <returns></returns>
119 public PropertySelector<TEntity> Continue<TEntity>()
120 where TEntity : Entity
121 {
122 return new PropertySelector<TEntity>(this.InnerDescriptor);
123 }
124 }
125
126 public class OrderByLoadOption<TEntity> : LoadOptionSelector
127 where TEntity : Entity
128 {
129 internal OrderByLoadOption(AggregateDescriptor descriptor) : base(descriptor) { }
130
131 public PropertySelector<TEntity> By<TKey>(Func<TEntity, TKey> keySelector)
132 {
133 this.InnerDescriptor.Items.Last.Value
134 .OrderBy = e => keySelector(e as TEntity);
135
136 return new PropertySelector<TEntity>(this.InnerDescriptor);
137 }
138 }

小结


本次重构由于只处理“链式的加载选项”,所以实现并不复杂。同时,由于把Repository都临时存放在了LoadOptionItem中,使得Repository的获取不再浪费,印证了:“一个重构后良好结构的程序,性能很有可能会有所提升。”