2023年2月

在上篇《
Winform下的地图开发控件(GMap.NET)使用心得
》中简单介绍了GMap.NET的控件基本情况,本篇开始介绍一下相关的代码操作。

其实目前GMap.NET提供的功能还不是很多,因此其演示的例子基本上都涉及到了,我在此基础上做了一些改进和运用,总结下功能代码吧。

首先使用控件前,需要初始化一些变量和事件,初始化代码如下所示:



代码


private

void
InitMapControl()
{

this
.gMapControl1.Manager.Mode
=
AccessMode.ServerAndCache;

this
.gMapControl1.MapType
=
MapType.GoogleMapChina;

this
.gMapControl1.MaxZoom
=

18
;

this
.gMapControl1.MinZoom
=

8
;

this
.gMapControl1.Zoom
=

13
;


this
.gMapControl1.MouseMove
+=

new
MouseEventHandler(gMapControl1_MouseMove);

this
.gMapControl1.DoubleClick
+=

new
EventHandler(gMapControl1_DoubleClick);

this
.gMapControl1.MouseDown
+=

new
MouseEventHandler(
this
.MainMap_MouseDown);

this
.gMapControl1.MouseUp
+=

new
MouseEventHandler(
this
.MainMap_MouseUp);


this
.gMapControl1.OnCurrentPositionChanged
+=

new
CurrentPositionChanged(
this
.MainMap_OnCurrentPositionChanged);

this
.gMapControl1.OnTileLoadStart
+=

new
TileLoadStart(
this
.MainMap_OnTileLoadStart);

this
.gMapControl1.OnTileLoadComplete
+=

new
TileLoadComplete(
this
.MainMap_OnTileLoadComplete);


this
.gMapControl1.OnMarkerClick
+=

new
MarkerClick(
this
.MainMap_OnMarkerClick);

this
.gMapControl1.OnMapZoomChanged
+=

new
MapZoomChanged(
this
.MainMap_OnMapZoomChanged);

this
.gMapControl1.OnMapTypeChanged
+=

new
MapTypeChanged(
this
.MainMap_OnMapTypeChanged);


this
.routes
=

new
GMapOverlay(
this
.gMapControl1,
"
routes
"
);

this
.gMapControl1.Overlays.Add(
this
.routes);

this
.objects
=

new
GMapOverlay(
this
.gMapControl1,
"
objects
"
);

this
.gMapControl1.Overlays.Add(
this
.objects);

this
.top
=

new
GMapOverlay(
this
.gMapControl1,
"
top
"
);

this
.gMapControl1.Overlays.Add(
this
.top);

this
.currentMarker
=

new
GMapMarkerGoogleRed(
this
.gMapControl1.CurrentPosition);

this
.top.Markers.Add(
this
.currentMarker);

this
.center
=

new
GMapMarkerCross(
this
.gMapControl1.CurrentPosition);

this
.top.Markers.Add(
this
.center);


this
.myShop
=

new
GMapOverlay(
this
.gMapControl1,
"
myShop
"
);

this
.gMapControl1.Overlays.Add(
this
.myShop);
DisplayMyShop();

SetZoomCenter();


this
.gMapControl1.DragButton
=
MouseButtons.Left;
}

其中的OnMarkerClick好像虽然有相关的事件,但是并不能捕获单击图标的时间操作,估计是没有完成该功能吧。

GMap.NET提供了各种鼠标的操作事件,我们重载即可实现特殊的控制处理了:



鼠标事件操作


void
gMapControl1_DoubleClick(
object
sender, EventArgs e)
{

this
.gMapControl1.Zoom
+=

1
;

this
.gMapControl1.CurrentPosition
=
lastPosition;
}


void
gMapControl1_MouseMove(
object
sender, MouseEventArgs e)
{
PointLatLng latLng

=

this
.gMapControl1.FromLocalToLatLng(e.X, e.Y);

this
.tsslPosition.Text
=

string
.Format(
"
经度:{0}, 纬度:{1}
"
, latLng.Lng, latLng.Lat);
}


private

void
MainMap_MouseDown(
object
sender, MouseEventArgs e)
{

if
(e.Button
==
MouseButtons.Left)
{

this
.isMouseDown
=

true
;
lastPosition

=

this
.gMapControl1.FromLocalToLatLng(e.X, e.Y);
}
}


private

void
MainMap_MouseUp(
object
sender, MouseEventArgs e)
{

if
(e.Button
==
MouseButtons.Left)
{

this
.isMouseDown
=

false
;
}
}

保存截图的操作如下所示



代码


private

void
tsbSavePicture_Click(
object
sender, EventArgs e)
{

try

{

using
(SaveFileDialog dialog
=

new
SaveFileDialog())
{
dialog.Filter

=

"
PNG (*.png)|*.png
"
;
dialog.FileName

=

"
GMap.NET image
"
;
Image image

=

this
.gMapControl1.ToImage();

if
(image
!=

null
)
{

using
(image)
{

if
(dialog.ShowDialog()
==
DialogResult.OK)
{

string
fileName
=
dialog.FileName;

if
(
!
fileName.EndsWith(
"
.png
"
, StringComparison.OrdinalIgnoreCase))
{
fileName

+=

"
.png
"
;
}
image.Save(fileName);
MessageBox.Show(

"
图片已保存:
"

+
dialog.FileName,
"
GMap.NET
"
, MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
}
}
}
}

catch
(Exception exception)
{
MessageBox.Show(

"
图片保存失败:
"

+
exception.Message,
"
GMap.NET
"
, MessageBoxButtons.OK, MessageBoxIcon.Hand);
}

地址查询并绘制图标的代码如下:



代码

private

void
btnSearch_Click(
object
sender, EventArgs e)
{

if
(
this
.txtAddress.Text.Length
==

0
)
{

this
.txtAddress.Focus();
MessageBox.Show(

"
请输入查询的地址
"
);
}


string
search
=

string
.Format(
"
{0},{1}
"
,
this
.txtCity.Text,
this
.txtAddress.Text);
GeoCoderStatusCode code

=

this
.gMapControl1.SetCurrentPositionByKeywords(search);

if
(code
!=
GeoCoderStatusCode.G_GEO_SUCCESS)
{
MessageBox.Show(

"
地址没有找到:'
"

+

this
.txtAddress.Text
+

"
', 原因:
"

+
code.ToString(),
"
GMap.NET
"
, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}


this
.objects.Markers.Clear();
AddLocation(

this
.txtAddress.Text);
}


private

void
AddLocation(
string
place)
{
GeoCoderStatusCode unknow

=
GeoCoderStatusCode.Unknow;
PointLatLng

?
latLngFromGeocoder
=
Singleton
<
GMaps
>
.Instance.GetLatLngFromGeocoder(place,
out
unknow);

if
(latLngFromGeocoder.HasValue
&&
(unknow
==
GeoCoderStatusCode.G_GEO_SUCCESS))
{
GMapMarker item

=

new
GMapMarkerGoogleGreen(latLngFromGeocoder.Value);
GMapMarkerRect rect

=

new
GMapMarkerRect(latLngFromGeocoder.Value);
rect.Size

=

new
System.Drawing.Size(
100
,
100
);
rect.ToolTipText

=
place;
rect.TooltipMode

=
MarkerTooltipMode.Always;

this
.objects.Markers.Add(item);

this
.objects.Markers.Add(rect);
}
}

绘制两地之间的线路图命令如下所示:



代码


private

void
ctxMenu_GetRout_Click(
object
sender, EventArgs e)
{

this
.objects.Markers.Clear();

this
.routes.Routes.Clear();
//
清楚路线




this
.start
=
defaultLocation;

this
.end
=

this
.gMapControl1.FromLocalToLatLng(
this
.contextMenuStrip1.Bounds.X,
this
.contextMenuStrip1.Bounds.Y);

MapRoute route

=
Singleton
<
GMaps
>
.Instance.GetRouteBetweenPoints(
this
.start,
this
.end,
false
, (
int
)
this
.gMapControl1.Zoom);

if
(route
!=

null
)
{
GMapRoute item

=

new
GMapRoute(route.Points, route.Name);
item.Color

=
Color.Blue;

this
.routes.Routes.Add(item);

GMapMarker marker

=

new
GMapMarkerGoogleRed(
this
.start);

//
marker.ToolTipText = "Start: " + route.Name;


marker.TooltipMode
=
MarkerTooltipMode.Always;


//
Placemark place = this.gMapControl1.Manager.GetPlacemarkFromGeocoder(this.end);
//
地标不准确,不用


MapRoute mapRoute
=

this
.gMapControl1.Manager.GetRouteBetweenPoints(
this
.start,
this
.end,
true
, (
int
)
this
.gMapControl1.Zoom);
GMapMarker marker2

=

new
GMapMarkerGoogleGreen(
this
.end);
marker2.ToolTipText

=

string
.Format(
"
目的地距离:{0}公里
"
, Math.Round(mapRoute.Distance,
2
));
marker2.TooltipMode

=
MarkerTooltipMode.Always;


this
.objects.Markers.Add(marker);

this
.objects.Markers.Add(marker2);

this
.gMapControl1.ZoomAndCenterRoute(item);
}
}

放大、缩小、重新加载地图的操作如下:



代码


private

void
ctxMenu_ZoomOut_Click(
object
sender, EventArgs e)
{

this
.gMapControl1.Zoom
+=

1
;
}


private

void
ctxMenu_ZoomIn_Click(
object
sender, EventArgs e)
{

this
.gMapControl1.Zoom
-=

1
;
}


private

void
tsbReload_Click(
object
sender, EventArgs e)
{

this
.gMapControl1.ReloadMap();
}

程序截图如下所示:

在做淘宝API的学习过程中,发现一个API的封装类库Top4NET对操作封装的比较好,试用了一下,效果非常不错,另外由于淘宝的API对象参数很多,多数是英文的,另一方面Top4NET的实体类对象备注信息很详细,因此可以考虑吧实体类的属性描述信息作为列表的表头说明使用。

我们看到他的Domain里面的实体类信息描述很详细,代码如下所示:



代码


///

<summary>


///
商品结构

///

</summary>


[Serializable]
[JsonObject]
[XmlRoot(

"
item
"
)]

public

class
Item : BaseObject
{

///

<summary>


///
商品编号

///

</summary>


[JsonProperty(
"
iid
"
)]
[XmlElement(

"
iid
"
)]

public

string
Iid {
get
;
set
; }


///

<summary>


///
商品地址

///

</summary>


[JsonProperty(
"
detail_url
"
)]
[XmlElement(

"
detail_url
"
)]

public

string
DetailUrl {
get
;
set
; }


///

<summary>


///
商品数字编号

///

</summary>


[JsonProperty(
"
num_iid
"
)]
[XmlElement(

"
num_iid
"
)]

public

string
NumIid {
get
;
set
; }


///

<summary>


///
商品标题

///

</summary>


[JsonProperty(
"
title
"
)]
[XmlElement(

"
title
"
)]

public

string
Title {
get
;
set
; }


///

<summary>


///
卖家昵称

///

</summary>


[JsonProperty(
"
nick
"
)]
[XmlElement(

"
nick
"
)]

public

string
Nick {
get
;
set
; }

我们看看Top4NET类库生成的字段描述信息,它是一个叫做Top4Net.xml的文件(VS自动生成出来的),结构如下所示:

这些是很详细的类库备注信息,如果能在代码中读取对应的属性备注信息出来,那么各项内容的显示,就不需要太多的手工字段的转义工作了,虽然在性能上有点点损失,但是开发效率上会提高不少。

现在的问题就是该如何才能正确读取到相关的信息,这个操作可以通过XML文档的检索实现的,我在测试的时候,发现可以通过3种方式来读取到正确节点的信息的。其中上面的Member中的Name第一个字符有T、P、M等字样,其中的T是类,P是属性,M是方法的意思。



代码


///

<summary>


///
根据类属性名称,获取对应的备注信息(如果键名不存在,返回空)

///

</summary>


///

<param name="classPropertyName">
类全局名称(带命名空间)
</param>


///

<returns></returns>



public

static

string
GetPropertySummary(
string
classPropertyName)
{

string
filePath
=
Path.Combine(Application.StartupPath,
"
Top4Net.xml
"
);

string
keyName
=

string
.Format(
"
//doc/members/member[@name='P:{0}']/summary
"
, classPropertyName);

XPathDocument doc

=

new
XPathDocument(filePath);
XPathNavigator nav

=
doc.CreateNavigator();
XPathNodeIterator iterator

=
nav.Select(keyName);


string
result
=

""
;

try

{

if
(iterator.MoveNext())
{
XPathNavigator nav2

=
iterator.Current.Clone();
result

+=
nav2.Value;
}
}

catch
(Exception ex)
{
;
}


return
result.Trim();
}


///

<summary>


///
根据类属性名称,获取对应的备注信息(如果键名不存在,返回空)

///

</summary>


///

<param name="classPropertyName">
类全局名称(带命名空间)
</param>


///

<returns></returns>



public

static

string
GetPropertySummary2(
string
classPropertyName)
{

string
filePath
=
Path.Combine(Application.StartupPath,
"
Top4Net.xml
"
);

string
keyName
=

string
.Format(
"
//doc/members/member[@name='P:{0}']/summary
"
, classPropertyName);

XmlDocument document

=

new
XmlDocument();
document.Load(filePath);


string
result
=

""
;
XmlNode node

=
document.SelectSingleNode(keyName);

if
(node
!=

null
)
{
result

=
node.FirstChild.Value;
}


return
result.Trim();
}


///

<summary>


///
根据类属性名称,获取对应的备注信息(如果键名不存在,返回空)

///

</summary>


///

<param name="classPropertyName">
类全局名称(带命名空间)
</param>


///

<returns></returns>



public

static

string
GetPropertySummary3(
string
classPropertyName)
{

string
strReturn
=

string
.Empty;

try

{

string
keyName
=

string
.Format(
"
P:{0}
"
, classPropertyName);

string
filePath
=
Path.Combine(Application.StartupPath,
"
Top4Net.xml
"
);
XmlDocument document

=

new
XmlDocument();
document.Load(filePath);

XmlNodeList nodes

=
document.GetElementsByTagName(
"
member
"
);

for
(
int
i
=

0
; i
<
nodes.Count; i
++
)
{

//
获得将当前元素的key属性


XmlAttribute attribute
=
nodes[i].Attributes[
"
name
"
];

//
根据元素的第一个属性来判断当前的元素是不是目标元素



if
(attribute
!=

null

&&
(attribute.Value
==
keyName))
{
XmlNode node

=
nodes[i].ChildNodes[
0
];

if
(node
!=

null
)
{
strReturn

=
node.FirstChild.Value;

break
;
}
}
}
}

catch

{
;
}


return
strReturn.Trim();
}

由于上述的方法参数classPropertyName是类的全称名字,因此我们需要通过反射方式获取对应的属性名称信息,由于属性名称的简称对我们也有用,因此编写一个API函数,返回Dictionary<string,string>类型即可。



代码


///

<summary>


///
获取类属性的名称和全称字典列表

///

</summary>


///

<param name="obj"></param>


///

<returns></returns>



public

static
Dictionary
<
string
,
string
>
GetPropertyFullNameDict(
object
obj)
{
Dictionary

<
string
,
string
>
dict
=

new
Dictionary
<
string
,
string
>
();
Type t

=
obj.GetType();

PropertyInfo[] pi

=
t.GetProperties();

foreach
(PropertyInfo info
in
pi)
{

if
(
!
dict.ContainsKey(info.Name))
{
dict.Add(info.Name,

string
.Format(
"
{0}.{1}
"
, t.FullName, info.Name));
}
}


return
dict;
}

最后我们在进一步封装,把实体类对象的备注信息取出来放到字典对象中,如下所示:



代码


///

<summary>


///
获取类属性名称和描述信息的对照字典

///

</summary>


///

<param name="obj">
实体类对象
</param>


///

<returns></returns>



public

static
Dictionary
<
string
,
string
>
GetPropertyAliasDict(
object
obj)
{
Dictionary

<
string
,
string
>
dict
=

new
Dictionary
<
string
,
string
>
();
Dictionary

<
string
,
string
>
propertyNameDict
=
XmlHelper.GetPropertyFullNameDict(obj);
StringBuilder sb

=

new
StringBuilder();

foreach
(
string
simpleName
in
propertyNameDict.Keys)
{

string
summary
=
XmlHelper.GetPropertySummary(propertyNameDict[simpleName]);

if
(
string
.IsNullOrEmpty(summary))
{
summary

=
simpleName;
//
如果找不到对象的名称,那么取其属性名称作为Summary信息


}


if
(
!
dict.ContainsKey(simpleName))
{
dict.Add(simpleName, summary);
}
}

return
dict;
}

有了这个API函数,我们就可以随意的在界面中,通过极少的代码,实现表头中文化的自动转义了。下面我给我参考代码:



代码


//
查询商品信息(不含类别)


ItemsGetRequest itemReq
=

new
ItemsGetRequest();
itemReq.Fields

=

"
iid,title,nick,pic_url,cid,price,type,delist_time,post_fee,score,volume,location.city,location.state
"
;
itemReq.Query

=

"
笔记本
"
;

//
itemReq.Cid = "14";


itemReq.OrderBy
=

"
volume:desc
"
;
itemReq.PageNo

=

1
;
itemReq.PageSize

=

40
;


#region
给显示控件添加别名解析



//
特殊字段采用手工修改



this
.winGridView1.AddColumnAlias(
"
Created
"
,
"
创建时间
"
);

this
.winGridView1.AddColumnAlias(
"
Modified
"
,
"
修改时间
"
);


//
其他字段代码自动转义


Dictionary
<
string
,
string
>
dictAlias
=
XmlHelper.GetPropertyAliasDict(
new
Item());

foreach
(
string
simpleName
in
dictAlias.Keys)
{

this
.winGridView1.AddColumnAlias(simpleName, dictAlias[simpleName]);
}

#endregion



//
显示列表信息


ResponseList
<
Item
>
itemRsp
=
client.Execute(itemReq,
new
ItemListJsonParser());

this
.winGridView1.DataSource
=
itemRsp.Content;

this
.winGridView1.PrintTitle
=

"
查询商品信息(不含类别)
"
;

最后给出一个测试程序的界面截图,供参考:

取实体类备注信息作为界面展示用途,还可以应用在很多其他地方,淘宝Top4NET只是一个恰当的应用场景而已,对于大量备注完整的实体类,使用这些资源应该还是比较不错的选择。

前阵子有空发现淘宝的API挺有意思的,就留意学习了一下,淘宝从2008.6月开始公测以来,API渐趋稳定,文档的相关说明也比较细致,不过基于淘宝API做应用的软件目前应该还不是很多,不过由于马云的影响力,淘宝API终会发光发热,给我们做开发的程序员多一份选择,多一个摆弄的东西。

在学习中,发现有一些比较好玩,比较有趣的地方,如果有空继续的话,准备开一个系列,总结介绍一下相关的知识,希望能够给博客多一份人气,多一份积累。

淘宝开放平台(Taobao Open Plateform,又叫TOP),地址是
http://open.taobao.com/
,我们看看其官方对TOP的描述:

淘宝开放平台(即TOP,无特别说明下文中TOP即指淘宝开放平台)对各类合作伙伴所提供的产品可以概括为两个平台,三条支撑主线。两个平台指的是
Open.taobao.com

App.taobao.com
;三条支撑主线是开放数据和业务流程、开放淘宝自有插件平台、开放对外接入标准,关于这两个产品和三条业务支撑线。


和众多平台一样,如Google,淘宝一样需要开发者注册一个账号,淘宝每个应用需要注册一个应用程序键(App Key),淘宝就是基于应用程序键来控制用户的访问频率和流量的,另外可以通过应用程序键,让使用者登陆确认,获取到相关的授权码,然后获取SessionKey,作为访问使用者淘宝资源(如买入卖出等私人记录的信息)。



淘宝的授权码是在用户确认后产生,并且只能使用一次,用来生成SessionKey,一般来说,SessionKey间隔的调用API事件不能超过10分钟,否则就会失效,需要重新获取用户的授权码,然后再次生成SessionKey。这样的做法虽然比较麻烦,但是对保证使用者的淘宝资源确是比较不错的做法。大概的界面如下所示:

淘宝的论坛有各种语言开发的介绍,相对来说还是比较详细,开发者有什么问题,在这里都可以问问,论坛地址是:
http://open.taobao.com/bbs/forum.php

淘宝的API是以REST服务提供了,通过HTTP访问,返回相关的信息,格式可以是Json格式的,也可以是XML格式的,各取所需吧。

有一位能人把淘宝的API做了进一步的封装,就是TOP4NET的类库,封装了各种API资源的访问,并且把信息封装为实体类对象进行传递,使用起来还是不错的,本篇后面会进一步介绍一下基于TOP4NET的基础上做淘宝的应用开发。

TOP4NET 是作为开源组件提供的,其SVN地址是
http://code.google.com/p/top4net/source/browse/#svn/trunk/Top4Net
, 大家可以下载下来研究研究,做一定的修改补充,代码风格写得不错,赞一个。

在上篇《
淘宝API开发系列--开篇概述
》介绍了下淘宝API平台的一些基本知识,由于一直有事情忙,就没有及时跟进随笔的更新,本篇继续讨论淘宝API的开发知识,主要介绍商家的绑定操作。上篇我们说过,
淘宝
就是基于应用程序键来控制用户的访问频率和流量的,另外可以通过应用程序键,让使用者登陆确认,获取到相关的授权码,然后获取SessionKey,作为访问使用者淘宝资源(如买入卖出等私人记录的信息)

我们再看看SessionKey是如何获取的(下面是淘宝关于正式环境下SessionKey的说明):

正式环境下获取SessionKey


注意:web插件平台应用和web其它应用在正式环境下是同样的获取方法

1、WEB应用

例如回调URL为:http://localhost

访问 http://container.open.taobao.com/container?appkey={appkey},页面会跳转到回调URL,地址类似如下:

http://localhost/?top_appkey={appkey} &top_parameters=xxx&top_session=xxx&top_sign=xxx

回调url上的top_session参数即为SessionKey
2、客户端应用

访问 http://auth.open.taobao.com/?appkey={appkey},即可获得授权码

通过http方式访问 http://container.open.taobao.com/container?authcode={授权码},会得到类似如下的字符串

top_appkey=1142&top_parameters=xxx&top_session=xxx&top_sign=xxx

字符串里面的top_session值即为SessionKey。

由于本篇文章主要是介绍C/S客户的应用,因此客户端的应用就不能通过回调Url方式获得用户的验证,我们可以通过在Winform中的WebBrowser控件,显示一个登陆验证及访问确认的操作界面给客户,当客户确认的时候并返回Session Key的内容界面的时候,我们取出Session Key保存并关闭浏览器窗口即可,今后把该SessionKey作为参数来访问相关需要Session Key的API即可。

另外,由于SessionKey的间隔时间比较短,如果API调用间隔时间比较长,那么SessionKey有可能失效的,但是我们注意到,如果API调用的时候,SesionKey过期 那么会抛出TopException(其中ErrorCode为26或者27是SessionKey过期),里面有关于与TopException的部分说明如下:

26 Missing Session 缺少SessionKey参数
27 Invalid Session 无效的SessionKey参数

我们先看看具体实现的界面,然后分析其中的实现逻辑吧。

1、首次需要登录的时候,使用一个Winform嵌套一个WebBrowser控件,实现网页登录。

2、商家用户输入账号密码后,确认是否授权程序访问相关资源。

3、确认后生成SessionKey,这个Key正是我们的程序需要的关键内容,因此需要自动获取出来。

4、程序拿到该Session Key后,把它作为参数来访问淘宝API获取相关的信息,这里获取交易API的购买信息,需要SessionKey的。

以上就是使用SessionKey的API工作流程界面,我们下面介绍一下相关的实现代码。

1) 主窗体主要的操作代码:



代码


public

partial

class
Form1 : Form
{

private
TopJsonRestClient jsonClient;

private
TopContext context;


private

void
Form1_Load(
object
sender, EventArgs e)
{

this
.winGridView1.ProgressBar
=

this
.toolStripProgressBar1.ProgressBar;

this
.winGridView1.AppendedMenu
=

this
.contextMenuStrip1;

jsonClient

=

new
TopJsonRestClient(
"
http://gw.api.taobao.com/router/rest
"
,
"
12033411
"
,
"
你的密钥
"
);

client

=
GetProductTopClient(
"
json
"
);
xmlClient

=

new
TopXmlRestClient(
"
http://gw.api.taobao.com/router/rest
"
,
"
12033411
"
,
"
你的密钥
""
);


}


///

<summary>


///
判断是否顺利获取SessionKey

///

</summary>


///

<returns></returns>



private

bool
GetAuthorizeCode()
{

string
authorizeCode
=

""
;
FrmAuthorized dlg

=

new
FrmAuthorized();

if
(dlg.ShowDialog()
==
DialogResult.OK)
{
authorizeCode

=
dlg.AuthrizeCode;
}

if
(
string
.IsNullOrEmpty(authorizeCode))
return

false
;

context

=
SysUtils.GetTopContext(authorizeCode);

if
(context
==

null
)
return

false
;


return

true
;
}


private

void
BindData()
{

if
(context
==

null
)
{

bool
flag
=
GetAuthorizeCode();

if
(
!
flag)
return
;
}


string
sessionKey
=
context.SessionKey;


///
/获取用户信息



//
UserGetRequest request = new UserGetRequest();

//
request.Fields = "user_id,nick,sex,created,location,alipay_account,birthday";

//
request.Nick = "wuhuacong";

//
User user = client.Execute(request, new UserJsonParser());

//
MessageBox.Show(ReflectionUtil.GetProperties(user));




try

{

//
买入交易


TradesBoughtGetRequest req
=

new
TradesBoughtGetRequest();
req.Fields

=

"
tid,title,price,type,iid,seller_nick,buyer_nick,status,orders
"
;
req.PageNo

=

1
;
req.PageSize

=

10
;
ResponseList

<
Trade
>
rsp
=
jsonClient.GetBoughtTrades(req, sessionKey);

this
.winGridView1.DataSource
=
rsp.Content;
MessageBox.Show(rsp.Content.Count.ToString());


//
卖出交易


TradesSoldGetRequest soldReq
=

new
TradesSoldGetRequest();
soldReq.Fields

=

"
tid,title,price,type,iid,seller_nick,buyer_nick,status,orders
"
;
soldReq.PageNo

=

1
;
soldReq.PageSize

=

10
;
ResponseList

<
Trade
>
soldRsp
=
jsonClient.GetSoldTrades(soldReq, sessionKey);

this
.winGridView1.DataSource
=
soldRsp.Content;
MessageBox.Show(soldRsp.Content.Count.ToString());
}

catch
(TopException ex)
{

if
(ex.ErrorCode
==

26

||
ex.ErrorCode
==

27
)
{

if
(MessageUtil.ShowYesNoAndError(
"
SessionKey过期,您是否需要重新认证
"
)
==
DialogResult.Yes)
{

bool
flag
=
GetAuthorizeCode();

if
(
!
flag)
return
;

BindData();

//
重新刷新


}

else

{

return
;
}
}
}
}


private

void
btnTest_Click(
object
sender, EventArgs e)
{
BindData();
}

2、用户登陆的窗体,就是一个form窗体加上一个WebBrowser控件,窗体代码如下:



代码


public

partial

class
FrmAuthorized : Form
{

///

<summary>


///
授权码

///

</summary>



public

string
AuthrizeCode
=

""
;

private

string
url
=

"
http://open.taobao.com/authorize/?appkey=12033411
"
;


public
FrmAuthorized()
{
InitializeComponent();
}


///

<summary>


///
获取HTML页面内制定Key的Value内容

///

</summary>


///

<param name="html"></param>


///

<param name="key"></param>


///

<returns></returns>



public

string
GetHiddenKeyValue(
string
html,
string
key)
{

string
str
=
html.Substring(html.IndexOf(key));
str

=
str.Substring(str.IndexOf(
"
value
"
)
+

7
);

int
eindex1
=
str.IndexOf(
"
'
"
);

int
eindex2
=
str.IndexOf(
"
\
""
);



int
eindex
=
eindex2;

if
(eindex1
>=

0

&&
eindex1
<
eindex2)
{
eindex

=
eindex1;
}

return
str.Substring(
0
, eindex);
}


private

void
webBrowser1_DocumentCompleted(
object
sender, WebBrowserDocumentCompletedEventArgs e)
{

if
(e.Url.AbsoluteUri
==
url)
{
AuthrizeCode

=
GetHiddenKeyValue(
this
.webBrowser1.DocumentText,
"
autoInput
"
);

if
(
!
string
.IsNullOrEmpty(AuthrizeCode)
&&
AuthrizeCode.IndexOf(
"
TOP-
"
)
>=

0
)
{

this
.DialogResult
=
DialogResult.OK;

this
.Close();
}
}
}


private

void
FrmAuthorized_Load(
object
sender, EventArgs e)
{
webBrowser1.Navigate(url);
}
}

这样我们就可以在首次使用API或者SessionKey失效的时候,让商家用户输入账号密码并确认即可,其他使用即可顺利无阻。

是不是有点意思呢,赶快试试吧,说不定带来一些意想不到的收获及创意哦。

由于工作需要,最近在弄数据库相关的项目,对于很多地方不甚了解,特别是一些概念性的东西,知其然而不知其所以然,这里列出一些基本知识,做个印记,也和读者共享。

数据仓库

数据仓库(
Data Warehouse
)是一个面向主题的(
Subject Oriented
)、集成的(
Integrate
)、相对稳定的(
Non-Volatile
)、反映历史变化(
Time Variant
)的数据集合,用于支持管理决策。

对于数据仓库的概念我们可以从两个层次予以理解,首先,数据仓库用于支持决策,面向分析型数据处理,它不同于企业现有的操作型数据库;其次,数据仓库是对多个异构的数据源有效集成,集成后按照主题进行了重组,并包含历史数据,而且存放在数据仓库中的数据一般不再修改。

根据数据仓库概念的含义,数据仓库拥有以下四个特点:

1

、面向主题。

数据仓库中的数据是按照一定的主题域进行组织。

主题是一个抽象的概念,是指用户使用数据仓库进行决策时所关心的重点方面,一个主题通常与多个操作型信息系统相关。

而操作型数据库的数据组织面向事务处理任务,各个业务系统之间各自分离。

2
、集成的。

数据仓库中的数据是在对原有分散的数据库数据抽取、清理的基础上经过系统加工、汇总和整理得到的,必须消除源数据中的不一致性,以保证数据仓库内的信息是关于整个企业的一致的全局信息。

面向事务处理的操作型数据库通常与某些特定的应用相关,数据库之间相互独立,并且往往是异构的。

3
、相对稳定的。

数据仓库的数据主要供企业决策分析之用,所涉及的数据操作主要是数据查询,一旦某个数据进入数据仓库以后,一般情况下将被长期保留,也就是数据仓库中一般有大量的查询操作,但修改和删除操作很少,通常只需要定期的加载、刷新。


操作型数据库中的数据通常实时更新,数据根据需要及时发生变化。

4
、反映历史变化。

数据仓库中的数据通常包含历史信息,系统记录了企业从过去某一时点
(
如开始应用数据仓库的时点
)
到目前的各个阶段的信息,通过这些信息,可以对企业的发展历程和未来趋势做出定量分析和预测。


而操作型数据库主要关心当前某一个时间段内的数据。

数据仓库的存储模式

数据仓库存储的两个基本的元素是维度表和事实表。(维是看问题的角度,比如时间,部门,维度表放的就是这些东西的定义,事实表里放着要查询的数据,同时有维的
ID

事实表:是反映业务核心的表,表中存储了与该业务相关的关键数据,我们称其为“度量值”,是今后用来计算及统计的主要字段。除此之外,事实表中还存储着与该业务相关的所有表的关联信息,也就是数据库中外键。能够与其他表建立起联系,从而获得所需要的所有业务信息。而被关联的其他表就是维度表。

维度表:存储与业务相关的非核心信息的表。

事实表与维度表之间通过主外键关联,结构有两种:星型结构和雪花型结构。

星型结构:如图,是指一个事实表连接一个或多个维度表构成的结构,维度表不再关联其他维度表。

雪花型结构:是指一个事实表关联一个或多个维度表,并且维度表还关联了其他的维度表,构成多层级的结构,称为雪花型。

联机分析处理OLAP
:

OLAP(联机分析处理On-Line Analytical Processing)也叫多维DBMS。

OLAP是数据仓库系统的主要应用,支持复杂的分析操作,侧重决策支持,并且提供直观易懂的查询结果。

OLAP的目标是满足决策支持或者满足在多维环境下特定的查询和报表需求,它的技术核心是"维"这个概念。

“维”是人们观察客观世界的角度,是一种高层次的类型划分。“维”一般包含着层次关系,这种层次关系有时会相当复杂。通过把一个实体的多项重要的属性定义为多个维(dimension),使用户能对不同维上的数据进行比较。因此OLAP也可以说是多维数据分析工具的集合。也叫做多维数据集。一般一个多维数据集可以用一个立方体的方式进行描述。

多维数据集是联机分析处理 (OLAP) 中的主要对象,是一项可对数据仓库中的数据进行快速访问的技术。多维数据集是一个数据集合,通常从数据仓库的子集构造,并组织和汇总成一个由一组维度和度量值定义的多维结构。

每个多维数据集都有一个架构,架构是数据仓库中已联接的各表的集合,多维数据集从数据仓库提取其源数据。架构中的核心表是事实数据表,事实数据表是多维数据集度量值的源。


OLAP
的基本多维分析操作有钻取(
roll up

drill down
)、切片(
slice
)和切块(
dice
)、以及旋转(
pivot
)、
drill
across


drill through
等。

·
钻取是改变维的层次,变换分析的粒度。它包括向上钻取(
roll up
)和向下钻取(
drill down
)。
roll up
是在某一维上将低层次的细节数据概括到高层次的汇总数据,或者减少维数;而
drill
down

则相反,它从汇总数据深入到细节数据进行观察或增加新维。

·
切片和切块是在一部分维上选定值后,关心度量数据在剩余维上的分布。如果剩余的维只有两个,则是切片;如果有三个,则是切块。

·
旋转是变换维的方向,即在表格中重新安排维的放置(例如行列互换)。

ETL介绍:

ETL(Extract-Transform-Load的缩写,即数据抽取、转换、装载的过程)作为BI/DW(Business Intelligence)的核心和灵魂,能够按照统一的规则集成并提高数据的价值,是负责完成数据从数据源向目标数据仓库转化的过程,是实施数据仓库的一重要组成部分 。

企业使用ETL工具后,利用起了已存在的数据资源,避免大量的联机事务处理。ETL工具的典型代表有:OWB、微软DTS, Informatica,Datastage 等

ETL的质量问题具体表现为:正确性、完整性、一致性、完备性、有效性、时效性和可获取性。

不同时期系统业务过程有变化,遗留系统和新业务,遗留系统模块在运营、人事、财务、办公系统等相关信息的不一致,ETL转换的过程主要包含空值处理、规范化数据格式、拆分数据、验证数据正确性及数据替换。.


DTS介绍:

DTS (Data Transformation Services的缩写)能处理数据导入、分析操作过程中与数据转换有关的步骤 .进行校验,清理等.可以自动或交互的从多个异构数据源向数据仓库或数据集市装入数据的技术。

大多数机构都有数据的多种存储格式和多个存储位置。为了支持决策制定、改善系统性能或更新现有系统,数据经常必须从一个数据存储位置移动到另一个存储位置,都可由DTS来做。DTS还允许用户定期导入或变换数据,以实现数据转换的自动化。

可以将 DTS 解决方案创建为一个或多个包。每个包都可能包含一组用来定义要执行工作的经过组织的任务、对数据和对象的转换、用来定义任务执行的工作流约束以及与数据源和目标的连接。DTS 包还提供了一些服务,例如记录包执行详细信息、控制事务和处理全局变量。DTS提供一组工具,可以从不同的源将数据抽取、转换和合并到一个或多个目标位置。借助于DTS工具,您可以创建适合于您的组织特定需要的自定义移动解决方案。

数据仓库与ETL

如下图所示,ETL服务于数据仓库,将数据迁移至仓库中。

ETL功能包括:数据抽取、数据传输、数据转换、数据装载、配置维护。如果说数据仓库的模型设计是一座大厦的设计蓝图,数据是砖

瓦的话,那么ETL就是建设大厦的过程。


E:  Extract ,连接异构数据源平台,提取数据


T:  Transform,过程一般都是批量操作,也是ETL的核心


L:  Load ,由普通关系库导进数据仓库

ETL正式运行特点:


一是数据同步,按照固定周期运行


二是数据量一般都是巨大的,所以会拆分成E,T,L几个过程

ETL正式运行要求:


增量与自动定时运行 ,相应的加载策略、更新策略(周期)、汇总策略(替换变化的数据记录,或新增汇总记录)、维护策略。

ETL与DTS关系

为数据仓库提供导入,清洗,装载的系统解决方案的工具叫ETL,DTS是微软提供的实现ETL工具的一个解决方案,通过DTS组件的提供的功能,可定制自己的ETL运行方案。

导入:配置好数据源连接由数据转换任务从数据源提取数据(DTS的数据源Connections 11个,任务(task)19个,工作流(workflow)3个)

清洗:定制好转换规则,在字段级上处理数据

装载:清洗后处理的数据结果,批量存入数据库中