[转载]使用Source Safe for SQL Server解决数据库版本管理问题 - CareySon - 博客园

mikel阅读(1137)

[转载]使用Source Safe for SQL Server解决数据库版本管理问题 – CareySon – 博客园.

简介

在软件开发过程中,版本控制是一个广为人知的概念。因为一个项目可能会需要不同角色人员的参与,通过使用版本控制软件,可以使得项目中不同角色的人并行参 与到项目当中。源代码控制使得代码可以存在多个版本,而不会将代码库变得混乱,典型的场景包括Bug修复、添加新功能、版本整合等。

虽然在开发层面的版本控制软件已经非常成熟,但目前国内还没有专门针对数据库层面的版本控制软件来帮助不同角色的人员在数据库层面进行团队协作、变更代码管理以及对数据库的变更进行查看和比对。在数据库层面版本控制工具的缺乏可能会出现如下场景:

  • 无法在数据库层面进行团队协作:开发人员A对存储过程的修改导致开发人员B创建的存储过程被覆盖,从而无法比对和追踪
  • 开发人员-开发DBA-测试人员难以协作:SVN等版本工具是基于文件的,很难在数据库层面进行版本控制
  • 数据库发生的变更难以追踪:现有的技术无法追踪由谁、在什么时间、对数据库修改了什么,当发生由数据库引起的报错或性能下降时,难以排查
  • 无法记录对数据库变更的过程资产:数据库变更的历史记录只有数据库运维人员了解,当该相关人员离职或调岗,这些过程资产难以继承
  • 难以审计数据库:现有的数据库审计功能往往依赖于日志,对性能造成很大影响。
  • SQL脚本无法有效管理:现有的做法往往是将SQL以文件形式保存,无法有效管理和共享
  • 无法查看被加密的数据库对象:当需要对加密的数据库对象进行修改时,如果无法找到对象定义的原始记录,则必须重写该对象
  • SVN建立和使用复杂:SVN使用流程对于数据库人员过于繁琐,为数据库人员增加了额外的工作负担

 

由于数据库是整个业务应用的核心,上述问题无论是在开发环境还是在生产环境如果得不到有效的解决,会造成生产力低下、过程资产无法得到管理、数据库审查无法进行、难以排查由数据库变更导致的问题等情况。

 

下面来介绍一下Source Safe for SQL Server如何解决该类问题。

 

软件的安装

软件的官网下载完Source Safe的安装包后一路下一步,安装完成后打开Management Studio,在需要加入到源代码控制器的某个数据库服务器上右键,在弹出菜单中选择“添加数据库到版本控制”,如图1所示。

1

图1.将数据库添加到版本控制

 

然后设置相关的选项,如图2所示。

2

图2.添加数据库到版本控制相关设置

 

现在再来看,整个数据库都已经在版本控制之下了,如图3所示。

3

图3.查看受版本控制的数据库

 

至此,Source Safefor SQL Server就安装配置完成了。

 

典型应用场景

Source Safe可以解决下述问题:

 

开发团队进行版本控制

在一个开发团队中,对于数据库对象的每一次变更都会自动覆盖上一个版本,导致上一个版本对象定义的丢失。比如说开发人员A创建了存储过程“ProcA”, 然后开发人员B修改了存储过程“ProcA”,开发人员A创建的存储过程将会被开发人员B所做的修改覆盖,造成之前定义存储过程的丢失和无法回滚。使用 Source Safe可以轻松解决该类问题。

通过图4,我们看到存储过程“ProcA”由开发人员“Jack”创建后,开发人员“CareySon”对其进行了修改,Source Safe可以完整的记录由谁,在什么时间,做了哪些修改,如图所示。

1

图4.查看存储过程“ProcA”被修改的历史记录

 

2

图5.查看存储过程 “ProcA”两个版本的差异部分

 

开发里程碑标记

在开发过程中,往往需要对开发里程碑进行迭代,每一个开发里程碑导致的数据库对象变更都可以完整的被记录和文档化。

3

图6.里程碑版本以及涉及到的对象变更

 

生产环境变更管理

在生产环境中,程序的升级、程序或人为对数据库对象的更改有可能导致数据库出现问题,例如应用程序报错或数据库性能下降。通过SourceSafe可以快 速比较出数据库之前版本和当前数据库定义中存在差异的部分,并根据具体情况回滚导致数据库出现问题的对象,从而快速排除错误并保证数据库持续稳定运行。如 图7所示。

4

图7.选择需要版本比较的对象

 

常用脚本管理

无论是开发人员还是数据库的运维人员,都会有常用脚本需要保存。过去的做法往往是将SQL代码以文件的形式保存,这样既不方便使用,也不方便分类管理。利 用Source Safe的代码管理功能,可以方便的将SQL代码的管理无缝集成到SQL Server Management Studio中。如图8所示。

5

图8、使用Source Safe的脚本管理功能对SQL进行管理

 

此外,Source Safe特别设定了默认文件夹“工具栏快捷方式”,用户可以将频繁使用的SQL代码置于此处,在该分类下的脚本会自动出现在Management Studio的工具栏中,如图9所示。

6

图9、将常用脚本置于“工具栏快捷方式”中

 

对选定的对象进行版本归类

虽然Source Safe每次同步之后都会生成一个基于变更的版本号,但在某些特殊情况下,比如需要对库中某些变更进行管理、对库中的版本进行归类的情况下会需要额外的标签。如图10所示。

7

图10、利用标签标出某个业务版本涉及到的数据库对象

 

 

与SVN的无缝集成

SourceSafe的版本内容和历史记录可以直接导出到SVN、TFS、VSS中,从而打通数据库版本控制和现有的SVN系统。在数据库中我们对存储过程dbo.TestProc做了三次变更,如图11所示。

010

图11.对存储过程的3次变更

 

接下来将历史记录导出到SVN,如图12所示。

011

图12.将SourceSafe记录导出到SVN

 

导出完成后,通过SVN客户端软件可以看到对应SQL文件的在数据库中对应图10的三次变更,如图13所示。

012

图13.SVN对应SourceSafe中的3次变更

 

软件的下载

软件的下载可以在软件的官网下载,下载地址请猛击这里(http://www.grqsh.com/products.htm?tab=sourcesafe-for-sql-server)

[转载]简单爬虫-抓取博客园文章列表 - 成天 - 博客园

mikel阅读(1332)

[转载]简单爬虫-抓取博客园文章列表 – 成天 – 博客园.

    如果使用对方网站数据,而又没有响应的接口,或者使用接口不够灵活的情况下,使用爬虫在合适不过了。爬虫有几种,对方网站展示形式有几种都是用分析,每个网站展示有相似的地方,有不同的地方。

    大部分使用httpRequst就能完成,不管是否添加了口令、随即码、请求参数、提交方式get或者post、地址来源、多次响应等等。但是有些网站使 用ajax如果是返回json或固定格式的也好处理,如果是很复杂的,可以使用webbrower控件进行抓取,最后正则解析,获取所需要的数据即可。

     那我们来抓取去首页网站列表 文章标题、文章摘要、文章发布时间、文章作者、文章评论次数、文章浏览次数。看下结构图。

  get请求返回静态html附代码如下

public class HttpCnblogs
{
public static List HttpGetHtml()
{

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.cnblogs.com/");
request.Method = "GET";
request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
request.UserAgent = " Mozilla/5.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0";
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream stream = response.GetResponseStream();
StreamReader sr = new StreamReader(stream);
string articleContent = sr.ReadToEnd();

List list = new List();

#region 正则表达式
//div post_item_body列表
Regex regBody = new Regex(@"<div\sclass=""post_item_body"">([\s\S].*?)

", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
//a标签 文章标题 作者名字 评论 阅读
Regex regA = new Regex("<a[^>]*?>(.*?)", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
//p标签 文章内容
Regex regP = new Regex(@"<p\sclass=""post_item_summary"">(.*?)

", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
//提取评论 阅读次数如:评论(10)-》10
Regex regNumbernew = new Regex(@"\d+", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
//提取时间
Regex regTime = new Regex(@"\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}", RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
#endregion
MatchCollection mList = regBody.Matches(articleContent);
CnblogsModel model = null;
String strBody = String.Empty;
for (int i = 0; i < mList.Count; i++)
{
model = new CnblogsModel();
strBody = mList[i].Groups[1].ToString();
MatchCollection aList = regA.Matches(strBody);
int aCount = aList.Count;
model.ArticleTitle = aList[0].Groups[1].ToString();
model.ArticleAutor = aCount == 5 ? aList[2].Groups[1].ToString() : aList[1].Groups[1].ToString();
model.ArticleComment = Convert.ToInt32(regNumbernew.Match(aList[aCount-2].Groups[1].ToString()).Value);
model.ArticleTime = regTime.Match(strBody).Value;
model.ArticleView = Convert.ToInt32(regNumbernew.Match(aList[aCount-1].Groups[1].ToString()).Value);
model.ArticleContent = regP.Matches(strBody)[0].Groups[1].ToString();
list.Add(model);
}
return list;
}
}

public class CnblogsModel
{
///
/// 文章标题
///

public String ArticleTitle { get; set; }
///
/// 文章内容摘要
///

public String ArticleContent { get; set; }
///
/// 文章作者
///

public String ArticleAutor { get; set; }
///
/// 文章发布时间
///

public String ArticleTime { get; set; }
///
/// 文章评论量
///

public Int32 ArticleComment { get; set; }
///
/// 文章浏览量
///

public Int32 ArticleView { get; set; }
}

最后看看获取的文章model

写的不好,还请见谅,准备下面试去。。

[转载]编写高质量代码改善C#程序的157个建议[C#闭包的陷阱、委托、事件、事件模型] - aehyok - 博客园

mikel阅读(1210)

[转载]编写高质量代码改善C#程序的157个建议[C#闭包的陷阱、委托、事件、事件模型] – aehyok – 博客园.

前言

本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要学习记录以下内容:

建议38、小心闭包中的陷阱

建议39、了解委托的实质

建议40、使用event关键字对委托施加保护

建议41、实现标准的事件模型

建议38、小心闭包中的陷阱

  首先我们先来看一段代码:

class Program
{
static void Main(string[] args)
{
List list = new List();
for (int i = 0; i < 5; i++) { Action t = () =>Console.WriteLine(i.ToString());
list.Add(t);
}
foreach (Action t in list)
{
t();
}
Console.ReadLine();
}
}

你设想的结果或许是0,1,2,3,4

但没想到执行后结果如下

通过IL可以查看代码,组合后大致代码如下:

public class TempClass
{
public int i;
public void TempFunc()
{
Console.WriteLine(i.ToString());
}

}
class Program
{
static void Main(string[] args)
{
List list = new List();
TempClass tempClass = new TempClass();
for (tempClass.i = 0; tempClass.i < 5; tempClass.i++)
{
Action t = tempClass.TempFunc;
list.Add(t);
}
foreach (Action t in list)
{
t();
}
Console.ReadLine();
}
}

当然运行后结果还是5,5,5,5,5

其实这段代码所演示的就是一个闭包对象。所谓的闭包对象,指的是上面这种情形中的TempClass对象,如果匿名方法(Lambda表达式)引用了某个局部变量,编译器就会自动将该引用提升到该闭包对象中,即将for循环中的变量i修改成了引用闭包对象的公共变量i。这样一来,即使代码执行后离开了原局部变量i的作用域(如for循环),包含该闭包对象的作用域也还存在。

下面简单修改一下之前的代码

class Program
{

static void Main(string[] args)
{
List list = new List();
for (int i = 0; i < 5; i++) { int temp = i; Action t = () => Console.WriteLine(temp.ToString());
list.Add(t);
}
foreach (Action t in list)
{
t();
}
Console.ReadLine();
}
}

执行结果如下:

建议39、了解委托的实质

 http://www.cnblogs.com/aehyok/archive/2013/03/22/2976356.html这里有我之前对委托的简单的学习过程,虽然在工作中很少用,几乎就没用。不过还是拿来学习学习。

理解委托需要把握两个点:

1、委托是方法指针。

2、委托就是一个类。当对其进行实例化的时候,要将引用方法作为它构造函数的参数。

建议40、使用event关键字对委托施加保护

 http://www.cnblogs.com/aehyok/archive/2013/02/22/2922586.html 这也是对于事件的简单理解学习。

建议41、实现标准的事件模型

我们应该知道微软为事件模型设定的几个规范:

1、委托类型的名称以EventHandler结束。

2、委托原型返回值为void。

3、委托原型具有两个参数:sender表示事件触发者,e表示事件参数。

4、事件参数的名称以EventArgs结束。

public class FileUploadedEventArgs : EventArgs
    {
        public int FileProgress { get; set; }
    }

    public class FileUploader
    {
        public event EventHandler<FileUploadedEventArgs> FileUploaded;

        public void Upload()
        {
            FileUploadedEventArgs e = new FileUploadedEventArgs() { FileProgress=100 };
            while (e.FileProgress > 0)
            {
                ///传输代码,省略
                e.FileProgress--;
                if (FileUploaded != null)
                {
                    FileUploaded(this, e);
                }
            }
        }
    }

最终进行调用的代码如下:

class Program
    {
        static void Main(string[] args)
        {
            FileUploader fileUploader = new FileUploader();
            fileUploader.FileUploaded += Progress;
            fileUploader.Upload();
            Console.ReadLine();
        }

        static void Progress(object sender,FileUploadedEventArgs e)
        {
            Console.WriteLine(e.FileProgress);
        }
    }

[转载]Restful.Data v1.0 - 轻量级数据持久层组件, 正式开源发布了 - linli8 - 博客园

mikel阅读(1100)

[转载]Restful.Data v1.0 – 轻量级数据持久层组件, 正式开源发布了 – linli8 – 博客园.

经过几个星期的优化调整,今天 Restful.Data 正式开源发布。

源码地址:https://github.com/linli8/Restful

今天不写那么多废话了,还是重新介绍一下 Restful.Data 吧。


 

1、什么是Restful.Data?

Restful.Data是一套通用的轻量级数据持久层组件,除封装了ADO.NET基本的数据库操作以外,也提供了一些orm相关的API,用户可以方便的定义实体类,并使用这些API对数据进行增删改查等操作。

Restful.Data借鉴了业界如nhibernate、entity framework等知名的数据持久层组件,但从一开始设计的初衷就是为了让用户能快速的学习和使用,并写出更加简洁优雅的代码,所以摒弃了一些复杂的设 计和功能,用户可以使用变通的方式或方法使用Restful.Data组件实现其目的。

Restful.Data充分考虑了实体框架的执行效率问题,进行了反复的推敲和论证,尽可能的采用高效的设计方案来提高性能。


 

2、谁需要Restful.Data?

敢于冒险、追求完美、勇于挑战并极具责任感的程序设计人员。


 

3、Restful.Data提供哪些功能?

基本的ADO.NET操作:BeginTransaction、ExecuteScalar、ExecuteDataReader、 ExecuteDataTable、ExecuteDataSet、ExecutePageQuery、ExecuteStoredProcedure

ORM相关操作:Insert、Updete、Delete、Find


4、如何使用Restful.Data?

使用前请先下载Restful.dll、Restful.Data、Restful.Data.MySQL、Remotion.Linq.dll、MySQL.Data.dll,或者直接下载源代码进行编译并获取这5个dll,并在项目中引用这些dll。

在 Web.config 或 App.config 中配置连接字符串,如下:






注册提供程序工厂:

SessionFactories.Register();

提供程序工厂在一个Application中仅需注册一次。

如何进行基本的数据库操作:

using( ISession session = SessionFactory.CreateDefaultSession() )
{
string sql = "select * from Person";

DataTable dt = session.ExecuteDataTable( sql );
}

CreateDefaultSession默认情况下根据配置文件中连接字符串节点的第一项创建数据库连接,你可以调用CreateSession进行指定,或者你也可以使用 SessionFactory.Default = “MySQL2″指定默认连接。

为防止 SQL 注入,你也使用带参数方法:

using( ISession session = SessionFactory.CreateDefaultSession() )
{
string sql = "select * from Person where Id = @Id;";

IDictionary&lt;string, object&gt; parameters = new Dictionary&lt;string, object&gt;();

parameters.Add( "@Id", 5 );

DataTable dt = session.ExecuteDataTable( sql, parameters );
}

与此类似的还有ExecuteScalar、ExecuteDataReader、ExecuteDataTable、ExecuteDataSet等方法。

如何进行分页查询:

using( ISession session = SessionFactory.CreateDefaultSession() )
{
string sql = "select * from User where CreateTime &lt; @CreateTime";

IDictionary&lt;string, object&gt; parameters = new Dictionary&lt;string, object&gt;();

parameters.Add( "@CreateTime", DateTime.Now );

// 查询第2页,每页10条,并根据 CreateTime 字段降序排列
PageQueryResult result = session.ExecutePageQuery( sql01, 2, 10, "CreateTime DESC", parameters );
}

如何进行数据新增:

using( ISession session = SessionFactory.CreateDefaultSession() )
{
var person = new Person();

// person.Id = 1; 若Id字段为自增类型,无需指定。
person.Name = "test01";
person.CreateTime = DateTime.Now;
person.IsActive = true;

int i = session.Insert( person );
}

如何进行数据更新:

using( ISession session = SessionFactory.CreateDefaultSession() )
{
var person = new Person();

person.Id = 1;
person.Name = "test01";
person.CreateTime = DateTime.Now;
person.IsActive = true;

// 在调用此方法时,务必指定实例的主键值。
int i = session.Update( person );
}

或者你也可以批量更新:

using( ISession session = SessionFactory.CreateDefaultSession() )
{
var person = new Person();

// person.Id = 1;
person.Name = "test01";
person.CreateTime = DateTime.Now;
person.IsActive = true;

// 在调用此方法时,不需要指定主键值,且不会更新主键字段
session.Update().Set( person ).Where( s =&gt; s.IsActive == false ).Execute();
}

如何进行数据删除:

using( ISession session = SessionFactory.CreateDefaultSession() )
{
var person = new Person() { Id = 1 };

// 在调用此方法时,需要指定主键值
session.Delete( person );
}

或者你也可以批量删除:

using( ISession session = SessionFactory.CreateDefaultSession() )
{
// 在调用此方法时,不需要指定主键值
session.Delete().Where( s =&gt; s.IsActive == false ).Execute();
}

如何进行单表查询:

using( ISession session = SessionFactory.CreateDefaultSession() )
{
var queryable = session.Find()
.Where( s =&gt; s.Name.Contains("a") )
.Where( s =&gt; s.CreateTime &lt; DateTime.Now ) .OrderBy( s =&gt; s.CreateTime )
.Skip(5)
.Take(10);

var list = queryable.ToList();
var count = queryable.Count();
var first = queryable.FirstOrDefault();

var queryable1 = from s in session.Find()
where s =&gt; s.Name.Contains("a")
orderby s.CreateTime descending
select new { Id = s.Id, Name = s.Name };

// ...
}

目前只支持对单表的LINQ查询,且为了降低复杂度,后期也不打算支持多表查询,对函数的支持也有限,仅支持string类型的StartsWith、EndsWith、Contains、Equals、IsNullOrEmpty等方法,对于其他方法后期将会继续完善。

如果你需要实现一个复杂的查询并将其转换成对象,你也可以这样:

using( ISession session = SessionFactory.CreateDefaultSession() )
{
string sql = "...";

T @object = session.Find( sql );
}

如何支持事务处理:

using( ISession session = SessionFactory.CreateDefaultSession() )
{
using( DbTransaction transaction = session.BeginTransaction() )
{
// ...
// ...

transaction.Commit();
}
}

SessionHelper的使用:

SessionHelper对session对象的方法进行了静态封装,如果你只是需要执行单条语句,并马上关闭连接,你可以使用 SessionHelper 类中提供的一些辅助方法。

5、如何定义实体类

[Serializable]
public class Person : EntityObject // 需继承与 EntityObject 类
{
private int m_Id;
private string m_Name;
private int? m_Age;
private decimal? m_Money;
private DateTime m_CreateTime;
private bool m_IsActive;

[PrimaryKey, AutoIncrease] // 如果是自增字段,标记为 AutoIncrease;如果是主键标记为 PrimaryKey
public int Id
{
get { return this.m_Id; }
set { this.m_Id = value; this.OnPropertyChanged( "Id", value ); }
}

public string Name
{
get { return this.m_Name; }
set { this.m_Name = value; this.OnPropertyChanged( "Name", value ); }
}

public int? Age
{
get { return this.m_Age; }
set { this.m_Age = value; this.OnPropertyChanged( "Age", value ); }
}

public decimal? Money
{
get { return this.m_Money; }
set { this.m_Money = value; this.OnPropertyChanged( "Money", value ); }
}

public DateTime CreateTime
{
get { return this.m_CreateTime; }
set { this.m_CreateTime = value; this.OnPropertyChanged( "CreateTime", value ); }
}

public bool IsActive
{
get { return this.m_IsActive; }
set { this.m_IsActive = value; this.OnPropertyChanged( "IsActive", value ); }
}
}

6、总结

因作者时间关系,组件目前并非十分完善,测试工作也只简单的进行了一部分,但您可以完全放心的应用于商业项目中,如遇到问题,作者将尽可能的解决。后期还将持续优化,感兴趣且愿意参与开源项目的小伙伴们,请加QQ群:338570336。

另如使用中发现bug,真心希望能分享出来,我们一并修正并改进。

[转载]C#开发微信门户及应用(11)--微信菜单的多种表现方式介绍 - 伍华聪 - 博客园

mikel阅读(1251)

[转载]C#开发微信门户及应用(11)–微信菜单的多种表现方式介绍 – 伍华聪 – 博客园.

在前面一系列文章中,我们可以看到微信自定义菜单的重要性,可以说微信公众号账号中,菜单是用户的第一印象,我们要规划好这些菜单的内容,布局等信 息。根据微信菜单的定义,我们可以看到,一般菜单主要分为两种,一种是普通的Url菜单(类型为View的菜单),一种是事件菜单(类型为Click的菜 单),一般情况下,微信的Url菜单,是无法获得用户的任何信息的,但微信用户信息非常重要,因此也提供了另外一种方式(类似重定向的方式)来给我们使 用,本篇主要介绍这种重新定向的方式菜单的使用,以使我们能够尽可能和用户进行交互。

1、微信自定义菜单的分类

微信对自定义菜单的要求:目前自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单。一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“…”代替。

根据菜单的分类,我们可以把它通过图形进行分类展示:

我对各种微信公众号进行了解,发现多数账号采用的都是普通的View类型的菜单链接方式,通过它们链接到自己的微网站上,但也有一些做的好的,如省 立中山图书馆,就能通过重定向的方式,提供一个绑定图书馆用户和微信OpenID的入口,绑定后,用户就可以查看借阅的书籍,然后可以通过一键续借功能实 现图书的快速续借功能。

对于这种重定向类型的Url菜单事件,微信的说明如下:

如果用户在微信中(Web微信除外)访问公众号的第三方网页,公众号开发者可以通过此接口获取当前用户基本信息(包括昵称、性别、城市、国家)。利用用户信息,可以实现体验优化、用户来源统计、帐号绑定、用户身份鉴权等功能。请 注意,“获取用户基本信息接口是在用户和公众号产生消息交互时,才能根据用户OpenID获取用户基本信息,而网页授权的方式获取用户基本信息,则无需消 息交互,只是用户进入到公众号的网页,就可弹出请求用户授权的界面,用户授权后,就可获得其基本信息(此过程甚至不需要用户已经关注公众号。)”

2、重定向类型菜单的URL

上面说了,重定向类型的菜单分为了两种,其实他们也仅仅是参数Scope类型的不同,其他部分也还是一样的。

为了展示,我们在假设用户单击菜单的时候,切换到http://www.iqidi.com/testwx.ashx这个页面,并带过来当前用户的OpenID等参数信息

对于scope=snsapi_base方式的链接如下:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx3d81fc2886d86526&redirect_uri=http%3A%2F%2Fwww.iqidi.com%2Ftestwx.ashx&response_type=code&scope=snsapi_base&state=123#wechat_redirect

而对于scope=snsapi_userinfo方式的链接如下:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx3d81fc2886d86526&redirect_uri=http%3A%2F%2Fwww.iqidi.com%2Ftestwx.ashx&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect

不过他们给手机客户端的体验是不同的,第一种可以平滑切换,但是第二种会弹出一个对话框供用户确认才能继续。

为了演示上面两种获取数据的不同,我把他们传过来的code的值,用户换取OpenID后进行用户信息的解析,他们两者的结果都是一样了。具体测试界面如下所示。

其中TestWX.ashx的页面后台代码如下所示:

///
/// TestWX 的摘要说明
///

public class TestWX : IHttpHandler
{
string appId = ""; //换成你的信息
string appSecret = ""; //换成你的信息

public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
string content = "";

if (context.Request != null &amp;&amp; context.Request.Url != null)
{
NameValueCollection list = HttpUtility.ParseQueryString(context.Request.Url.Query);
foreach (string key in list.AllKeys)
{
content += string.Format("{0}:{1} \r\n", key, list[key]);
}
}

string code = context.Request.QueryString["code"] ?? "";
if (!string.IsNullOrEmpty(code))
{
IBasicApi api = new BasicApi();
try
{
AppConfig config = new AppConfig();
appId = config.AppConfigGet("AppId");//从配置中获取微信程序ID
appSecret = config.AppConfigGet("AppSecret");//从配置中获取微信程序秘钥

AccessTokenResult result = api.GetAccessToken(appId, appSecret, code);
if (result != null)
{
content += string.Format("openid:{0}\r\n", result.openid);

string token = api.GetAccessToken(appId, appSecret);
IUserApi userApi = new UserApi();
UserJson userDetail = userApi.GetUserDetail(token, result.openid);
if (userDetail != null)
{
content += string.Format("nickname:{0} sex:{1}\r\n", userDetail.nickname, userDetail.sex);
content += string.Format("Location:{0} {1} {2} {3}\r\n", userDetail.country, userDetail.province, userDetail.city, userDetail.language);
content += string.Format("HeadUrl:{0} \r\n", userDetail.headimgurl);
content += string.Format("subscribe:{0},{1}\r\n", (userDetail.subscribe == 1) ? "已订阅" : "未订阅", userDetail.subscribe_time.GetDateTime());
}
}
}
catch { }
}

context.Response.Write(content);
}

在上面的代码中,我主要分为几步,一个是打印当前用户重定向过来的链接的参数信息,代码如下。

NameValueCollection list = HttpUtility.ParseQueryString(context.Request.Url.Query);
foreach (string key in list.AllKeys)
{
content += string.Format("{0}:{1} \r\n", key, list[key]);
}

然后获取到Code参数后,通过API接口,获取AccessTokenResult的数据,这里面有用户的OpenID

AccessTokenResult result = api.GetAccessToken(appId, appSecret, code);

当正常调用后,我们把用户标识的OpenID进一步进行解析,调用API获取用户的详细信息,具体代码如下所示。

UserJson userDetail = userApi.GetUserDetail(token, result.openid);

当我们把用户的相关信息获取到了,就可以做各种用户信息的展示了,如下代码所示。

if (userDetail != null)
{
content += string.Format("nickname:{0} sex:{1}\r\n", userDetail.nickname, userDetail.sex);
content += string.Format("Location:{0} {1} {2} {3}\r\n", userDetail.country, userDetail.province, userDetail.city, userDetail.language);
content += string.Format("HeadUrl:{0} \r\n", userDetail.headimgurl);
content += string.Format("subscribe:{0},{1}\r\n", (userDetail.subscribe == 1) ? "已订阅" : "未订阅", userDetail.subscribe_time.GetDateTime());
}

3、重定向链接菜单的用途

这种菜单就是需要指定域名,在微信后台中进行设置,重定向的链接必须属于这个域名之中,否则不会转到你希望的链接。

这个方式,让我们的微信应用程序后台可以获得用户的标识、用户详细信息等,我们就可以用来绑定和用户相关的业务信息了,如上面提到的图书馆借阅信 息,送水客户的信息,客户的积分信息,或者可以和后台账号进行关联实现更加复杂的应用等。用户的身份信息如此重要,如果结合到我们的CRM系统、业务管理 系统,就可以发挥用户信息应用的作用了。

以上就是我对这个类型菜单链接的应用了解,具体还需要进一步深化其应用,希望和大家共同探讨这方面的应用场景。

 

如果对这个《C#开发微信门户及应用》系列感兴趣,可以关注我的其他文章,系列随笔如下所示:

C#开发微信门户及应用(10)–在管理系统中同步微信用户分组信息

C#开发微信门户及应用(9)-微信门户菜单管理及提交到微信服务器

C#开发微信门户及应用(8)-微信门户应用管理系统功能介绍

C#开发微信门户及应用(7)-微信多客服功能及开发集成

C#开发微信门户及应用(6)–微信门户菜单的管理操作

C#开发微信门户及应用(5)–用户分组信息管理

C#开发微信门户及应用(4)–关注用户列表及详细信息管理

C#开发微信门户及应用(3)–文本消息和图文消息的应答

C#开发微信门户及应用(2)–微信消息的处理和应答

C#开发微信门户及应用(1)–开始使用微信接口

主要研究技术:代码生成工具、Visio二次开发、客户关系管理软件、送水管理软件等共享软件开发
专注于Winform开发框架Web开发框架、WCF开发框架的研究及应用。
转载请注明出处:
撰写人:伍华聪  http://www.iqidi.com

[转载]页面UI注意事项,你在乎吗? - 我和小菜 - 博客园

mikel阅读(833)

[转载]页面UI注意事项,你在乎吗? – 我和小菜 – 博客园.

早上打开微信,看到一篇文章,下面就和大家分享一下,该文章属于前端文章系列,希望做后台开发系统的程序员也可以学习一下,只会写代码把功能实现是第一,接下来也要把界面做做好。

现在的界面风格对于手机而言,一直跟随苹果的设计风格,而安卓的很多app在今年也跟随苹果的扁平化风格开始改变UI设计思路

对于后台界面,我想淘宝的后台界面也不可能那么绚丽,也可能很平常普通的界面,但是我想凡事要不断的往前推进,让自己的产品要更好,更能够吸引客户 的眼光,首先UI还是第一个,一个十年前的界面放到现在,可能使用者会已经习惯了,不用再改动了吧,如果当你对UI进行了调整,客户也会赞美很多,下面就 看些注意事项

Good UI是一家研究用户体验的设计机构。我们知道成功的页面设计不仅有很高的转化率更便于用户使用,既能满足商业目标更能为用户带来良好的体验。今天为大家分享一些Good UI 在一些项目中获取的设计以及运营策略等方面的经验。

1. 用通栏布局代替多栏布局

2. 给用户些好处,别急着做生意

3. 整合相似的功能,去掉零碎的UI元素

4.利用社会认同效应,别总是自吹自擂

5.主要功能需要多次强化显示

6.区分选中和可点击的状态,不要使用户困惑

7.布局有层次有重点,而非简单罗列

8. 允许用户撤销操作而不是使用弹窗需要用户确认

9. 明确的告知用户适用人群而不是简单的面向所用用户

10.简洁明了,直接了当

11.页面上多使用对比的方法

12.直接标出产地,别总单纯的讲历史

13.使用简洁的表单

14.把选项列出来而不是藏起来

 

15.使用连续性的提示符,别让用户误以为页面到了终点

16.功能专一而不是使用太多的链接

17.提示体统状态

18. 在动作按钮上增加些吸引人的诱惑

19.用直接操作来代替无数个菜单

20.直接显示输入框可以省略一个页面

21. 用一些动效过度而不用立即显示变化

22. 循序渐进的引导用户而非生硬的要求用户注册

23.试着减少线框,减少不必要的注意

24.向用户展示功能上的便捷之处而不是泛泛的展示特性

25. 用户没有使用记录的时候要善于引导

26. 给出默认的选项而不需要用户选择

27.保持一致性降低用户的学习成本

28. 自动补全一些数据,降低用户的操作负担

29. 尊重用户的使用习惯而不是创造新的规则

30. 提示用户如何规避风险,而不是总想着如何获利

31. 善于引导用户的视觉浏览线,而非单一布局

32. 将相关的条目分组,不要杂乱无章的排列

33.采用及时校验而不是到最后才提示错误

34.需要用户输入的格式宽松严格限定格式

35.让用户有一些紧急的意识,别让用户拖得太久

36. 适当尝试饥饿营销

37. 帮助用户识别

38. 使用更大的点击区域

39. 增加加载的速度,别让用户等太久

40. 可以给用户提供一些快捷操作

41. 使用一些对比

42. 初始化的时候给用户一些激励

43.循序渐进地引导用户,不要简单粗暴地直接呈现给用户

这些注意点,设计师和工程师们,喜欢吗?

[转载]PHP生成二维码【谷歌API+qrcode+圆角Logo】 - 小编络络 - 博客园

mikel阅读(1350)

[转载]PHP生成二维码【谷歌API+qrcode+圆角Logo】 – 小编络络 – 博客园.

这几天在开发公司的企业版块,想在每个企业的展示页面上添加一个公司的二维码,通 过扫描二维码便可以将公司的信息导入手机通讯录中,想法产生了,于是开始搜索PHP生成二维码的方法,比较流行的方法主要有两种:1谷歌二维码API;2 PHP QR CODE;本文将重点介绍这两种方法。

 方法一:谷歌二维码API

接口地址:https://chart.googleapis.com/chart

官方文档:https://developers.google.com/chart/infographics/docs/qr_codes

参数说明:

cht=qr:必要参数,固定值<qr>,表明要生成二维码

chs=<width>x<height>必要参数,生成二维码尺寸,单位是像素,目前生成的二维码都是正方形的,所以两个宽高值都设置为一样的值

chl=<data>必要参数,生成二维码包含的信息,可以是数字、字符、二进制信息、汉字。不能混合数据类型,数据必须经过UTF-8 URL-encoded.如果需要传递的信息超过2K个字节,需使用POST方式

choe=<output_encoding>可选参数,编码格式。包括UTF-8/Shift_JIS/ISO-8859-1三个选项,默认为UTF-8。

chld=<error_correction_level>|<margin>可选参数,

  error_correction_level:容错级别(共分四个等级,L-默认:可以识别已损失的7%的数据;M-可以识别已损失15%的数据;Q-可以识别已损失25%的数据;H-可以识别已损失30%的数据)

  margin:生成的二维码离图片边框的距离

 

例一:扫描二维码打开网址(API)

<!--?php <br ?--> //二维码信息,用urlencode编码
$data = urlencode('http://www.baidu.com');
//生成二维码尺寸
$size = '300x300';
//完整的API地址
$qrurl = "http://chart.googleapis.com/chart?chs=$size&amp;cht=qr&amp;chl=$data&amp;chld=L|1&amp;choe=UTF-8";
//获取二维码
$qrcode = file_get_contents($qrurl);
//输出图片
header('Content-type: image/png');
echo $qrcode;

在打开一个网站之后,我们要来实现归初的目的,扫描二维码,将企业信息导入通讯录,即在二维码中包含一个名片信息,其实二维码说白了就是将一堆信息转换为图片的形式表达,而这堆信息到底是什么样子的它并不关心,真正关心这些信息的是扫描二维码的工具,以上面的例子来说,当你用微信扫描二维码后,微信读取到二维码中储存的数据,然后尝试开始辨别它,当它发现内容是百度的网址时便使用浏览器打开它,同样的当它发现内容是一堆简单的文本数据时便以文本的形式来显示,同样的微信还可以辨别出二维码的内容是不是一个 vcard 格式的电子名片,我们的手机通讯录导入导出的时候大多是这种格式,所以我们可以直接在二维码中包含一个标准的 vcard 电子名片,这样就可以让扫描工具将它识别为通讯录。

vcard 格式参考如下:

BEGIN:VCARD
VERSION:3.0
FN:用户名
TEL;CELL;VOICE:18858140621
TEL;WORK;VOICE:0358-2157466
TEL;WORK;FAX:0358-2157466
EMAIL;PREF;INTERNET:lzw#lzw.me
URL:http://lzw.me
orG:志文工作室
ROLE:产品部
TITLE:CTO
ADR;WORK;POSTAL:北京市朝阳区北四环中路35号;100101
REV:2012-12-27T08:30:02Z
END:VCARD

如果你想更详细的定制所需要的格式,请百度 vcard 的格式标准。

例二:生成一个名片二维码(API)

<!--?php <br ?--> //构建一个 vcard 格式数据
$vcard =
"BEGIN:VCARD".
"\nVERSION:3.0".
"\nFN:小编络络".
"\nTEL;CELL;VOICE:18858140621".
"\nTEL;WORK;VOICE:0358-2157466".
"\nEMAIL:luoluo@qq.com".
"\nURL:http://www.baidu.com".
"\nADR:浙江省杭州市滨江区".
"\nEND:VCARD";

//二维码信息,用urlencode编码
$data = urlencode($vcard);
//生成二维码尺寸
$size = '300x300';
//完整的API地址
$qrurl = "http://chart.googleapis.com/chart?chs=$size&amp;cht=qr&amp;chl=$data&amp;chld=L|1&amp;choe=UTF-8";
//获取二维码
$qrcode = file_get_contents($qrurl);
//输出图片
header('Content-type: image/png');
echo $qrcode;

 到此为止我们归初的目的已经基本实现,但实际使用中,由于直接将上述拼装好的 url 加到了 img 标签的 src 属性,结果出了点小问题,由于我们的名片信息比较多,导致了二维码生成错误,在本文开头已经用红色字体标出,get 方式最多只能传递 2K 大的数据,大于 2K 数据时我们需要使用 POST 请求,所以在实际使用中,可以先在程序中通过 POST 方式获取二维码并保存为本地图片,之后直接调用本地图片即可。

 

方法二:使用 PHP QR CODE 生成二维码

官方主页:http://phpqrcode.sourceforge.net

下载地址:http://sourceforge.net/projects/phpqrcode/

使用方法:QRcode::png($data, $filename , $errorCorrectionLevel, $matrixPointSize, $margin );

    * $data 数据  
    * $filename 生成二维码保存路径,如果为 false 直接输出到浏览器 
    *$errorCorrectionLevel 错误处理级别  L、M、Q、H;同谷歌 API 中纠错级别
    * $matrixPointSize 每个黑点的像素  
    * $margin 图片外围的白色边框像素

 

例三:扫描二维码打开网址(phpqrcode)

<!--?php </p-->

// 包含 qrlib.php 或 phpqrcode.php
// 包含 qrlib.php 需要同其它文件放到一起; phpqrcode.php 是合并后版本,只需要包含这个文件,但生成的图片速度慢而且不太准确
include('./phpqrcode/qrlib.php');

// 二维码数据
$data = 'http://www.111cn.net';

// 纠错级别:L、M、Q、H
$errorCorrectionLevel = 'L';

// 点的大小:1到10
$matrixPointSize = 5;

QRcode::png($data, false, $errorCorrectionLevel, $matrixPointSize, 2);

例二:生成一个名片二维码(phpqrcode)

<!--?php <br ?--> // 包含 qrlib.php 或 phpqrcode.php
include('./phpqrcode/qrlib.php');
// 构建 vcard 数据
$vcard =
"BEGIN:VCARD".
"\nVERSION:3.0".
"\nFN:王歌".
"\nTEL;CELL;VOICE:18858140621".
"\nTEL;WORK;VOICE:0358-2157466".
"\nEMAIL:wang@qq.com".
"\nURL:http://www.mailuow.com".
"\nADR:浙江省杭州市滨江区江虹路1750号".
"\nEND:VCARD";

// 纠错级别:L、M、Q、H
$errorCorrectionLevel = 'L';

// 点的大小:1到10
$matrixPointSize = 4;

QRcode::png($vcard, false, $errorCorrectionLevel, $matrixPointSize, 2);

到此为止我们已经可以用两种方法来实现最初的想法了,但二维码是挂上去了,功能也 实现了,不过总觉得不够华丽,如果再能把企业的 logo 嵌到二维码当中就好了,嘿嘿,当然这部分工作已经不在二维码生成的范筹了,接下来就要靠我们 PHP 中的 GD 库来完成了,但是有一点需要了解,我们在前面提到过二维码的容错级别,即二维码在损毁一部分之后仍有可能解析出完整信息,而给二维码中间添加 logo 后仍能解读出完整信息靠的正是它的高容错性。

网上有找到一些 PHP 生成带 Logo 二维码的方法,但不够细致,生成的二维码很难看,笔者试过好几种方法,现将效果最好的一种拿出来与大家分享。

整体思路如下图:

例五:生成带 Logo 二维码

<!--?php <br ?--> // 包含 qrlib.php 或 phpqrcode.php
include('./phpqrcode/qrlib.php');
// 构建 vcard 数据
$vcard =
"BEGIN:VCARD".
"\nVERSION:3.0".
"\nFN:王歌".
"\nTEL;CELL;VOICE:18858140621".
"\nTEL;WORK;VOICE:0358-2157466".
"\nEMAIL:wang@qq.com".
"\nURL:http://www.mailuow.com".
"\nADR:浙江省杭州市滨江区江虹路1750号".
"\nEND:VCARD";

// 纠错级别:L、M、Q、H
$errorCorrectionLevel = 'M';

// 点的大小:1到10
$matrixPointSize = 4;

// 保存文件名
$filename = 'test.png';

//生成二维码
QRcode::png($vcard, $filename, $errorCorrectionLevel, $matrixPointSize, 2);

//获取二维码
$qrcode = file_get_contents($filename);
$qrcode = imagecreatefromstring($qrcode);
$qrcode_width = imagesx($qrcode);
$qrcode_height = imagesy($qrcode);

//圆角图片
$corner = file_get_contents('./images/corner.png');
$corner = imagecreatefromstring($corner);
$corner_width = imagesx($corner);
$corner_height = imagesy($corner);

//计算圆角图片的宽高及相对于二维码的摆放位置,将圆角图片拷贝到二维码中央
$corner_qr_height = $corner_qr_width = $qrcode_width/5;
$from_width = ($qrcode_width-$corner_qr_width)/2;
imagecopyresampled($qrcode, $corner, $from_width, $from_width, 0, 0, $corner_qr_width, $corner_qr_height, $corner_width, $corner_height);

//logo图片
$logo = file_get_contents('./images/logo.jpg');
$logo = imagecreatefromstring($logo);
$logo_width = imagesx($logo);
$logo_height = imagesy($logo);

//计算logo图片的宽高及相对于二维码的摆放位置,将logo拷贝到二维码中央
$logo_qr_height = $logo_qr_width = $qrcode_width/5 - 6;
$from_width = ($qrcode_width-$logo_qr_width)/2;
imagecopyresampled($qrcode, $logo, $from_width, $from_width, 0, 0, $logo_qr_width, $logo_qr_height, $logo_width, $logo_height);

header('Content-type: image/png');
imagepng($qrcode);
imagedestroy($qrcode);
imagedestroy($corner);
imagedestroy($logo);

 本例中使用到的 corner 图片: 右击另存为

 本例中所有代码示例下载:http://yun.baidu.com/s/1c0ovV6w

 

[转载]C#开发微信门户及应用(7)-微信多客服功能及开发集成 - 伍华聪 - 博客园

mikel阅读(1021)

[转载]C#开发微信门户及应用(7)-微信多客服功能及开发集成 – 伍华聪 – 博客园.

最近一直在弄微信的集成功能开发,发现微信给认证账户开通了一个多客服的功能,对于客户的咨询,可以切换至客服处理的方式,而且可以添加多个客服进 行处理,这个在客户咨询比较多的时候,是一个不错的营销功能。微信多客服的功能,能够在很大程度上利用客服员工资源,及时迅速对客户咨询信息进行处理,为 企业带来更多的机会和市场。

默认这个多客服的功能,需要在微信公众平台中的服务中心进行主动开通,默认是不开通的,为了体验这个功能,我这里把多客服功能进行开通。

1、多客服准备工作

微信的多客服功能,对于客服的响应操作,既可以在电脑的客户端上进行操作,也可以在微信多客服助手进行信息处理,两者都能对客户的信息进行回应、结束会话等操作。

开通微信多客服功能后,就需要添加一些处理客户信息的客服工号了。

多客服账号采用“工号@微信号”的形式进行登录,请您在登录窗口依照下图形式输入帐号信息。

2、使用多客服客户端或助手操作

在电脑客户端上使用

在手机客户端上进行多客服的使用,就是关注一个账号,信息通过转发到这里进行处理。关注公众号”多客服助手“就搞定了

通过上面两种途径,能够很好处理客户的相关信息,其实也就是类似电话坐席的方式,让不同的客服员工,对来访的客户进行处理。

3、微信多客服的开发使用

在微信的多客服开发介绍中,内容介绍的比较少,如下所示。

在新的微信协议中,开发模式也可以接入客服系统。 开发者如果需要使用客服系统,需要在接收到用户发送的消息时,返回一个MsgType为transfer_customer_service的消息,微信 服务器在收到这条消息时,会把用户这次发送的和以后一段时间内发送的消息转发客服系统。返回的消息举例如下。

<![CDATA[touser]]>
<![CDATA[fromuser]]>
1399197672
<![CDATA[transfer_customer_service]]>

而在开发的时候,我们一般把它封装为一个实体类信息,如下所示。主要就是指定消息类型,和翻转传入传出对象就可以了。

///
/// 客服消息
///

[System.Xml.Serialization.XmlRoot(ElementName = "xml")]
public class ResponseCustomer : BaseMessage
{
public ResponseCustomer()
{
this.MsgType = ResponseMsgType.transfer_customer_service.ToString().ToLower();
}

public ResponseCustomer(BaseMessage info) : this()
{
this.FromUserName = info.ToUserName;
this.ToUserName = info.FromUserName;
}
}

然后调用处理的时候,代码如下所示。

ResponseCustomer customInfo = new ResponseCustomer(info);
xml = customInfo.ToXml();

如我在客户应答处理里面,客户回应0,我就切换进入客服模式,这样客户后续所有的输入内容,均不会触发微信门户里面的解析,而转发到客服模式,让客服的工号可以和客户进行交谈了。

//处理 0 指令, 人工客服
if (string.IsNullOrEmpty(xml) &amp;&amp; eventKey.Trim() == "0")
{
xml = base.DealEvent(eventInfo, "event_customservice");
}

而在DealEvent里面,根据这个条件进行处理就可以了。

//人工客服
if (eventKey == "event_customservice")
{
ResponseCustomer customInfo = new ResponseCustomer(info);
xml = customInfo.ToXml();
}

通过使用多客服的客户端,这样处理消息交互起来非常方便,能获得客户的对话信息了,在电脑客户端上,看到的界面如下所示。

 

 

手机上的谈话截图如下所示。

 

                 

 

这样就能够通过多途径,及时响应客户的信息了。

 

如果感兴趣或者体验相关的客服应答功能,可以关注我的微信了解下。具体效果可以关注我的微信门户:广州爱奇迪,也可以扫描下面二维码进行关注了解。

 

 

 

 

如果对这个系列感兴趣,可以关注我的其他文章,系列随笔如下所示:

 

C#开发微信门户及应用(6)–微信门户菜单的管理操作

 

C#开发微信门户及应用(5)–用户分组信息管理

 

C#开发微信门户及应用(4)–关注用户列表及详细信息管理

 

C#开发微信门户及应用(3)–文本消息和图文消息的应答

 

C#开发微信门户及应用(2)–微信消息的处理和应答

 

C#开发微信门户及应用(1)–开始使用微信接口

 

 

主要研究技术:代码生成工具、Visio二次开发、客户关系管理软件、送水管理软件等共享软件开发
专注于Winform开发框架Web开发框架、WCF开发框架的研究及应用。
转载请注明出处:
撰写人:伍华聪  http://www.iqidi.com

[转载]Andorid开发学习---ubuntu 12.04下搭建超好用的安卓模拟器genymotion - Hi_Amos - 博客园

mikel阅读(1009)

[转载]Andorid开发学习—ubuntu 12.04下搭建超好用的安卓模拟器genymotion – Hi_Amos – 博客园.

什么是Genymotion?

Genymotion是一套完整的工具,它提供了Android虚拟环境。它简直就是开发者、测试人员、推销者甚至是游戏玩家的福音。

Genymotion支持Windows、Linux和Mac OS等操作系统,容易安装和使用:按简单的安装过程,选择一款Android虚拟设备,开启后就体验Genymotion带来的快感吧。—–引自百度百科

 

一.下载并安装genymotion

1.下载genymotion

http://www.genymotion.cn/#theme=download_list 下载列表,必须要先注册才能使用genymotion.

下载Windows快速安装包

这个版本包含Oracle VirtualBox 4.2.12支持,因此您不用再手动下载安装VirtualBox。

其他平台版本

以下版本需要已安装Oracle VirtualBox(下载页面),才能启动虚拟设备。

注意:对于Debian系统的用户,为了获得最佳渲染,我们建议您安装libpngX(X取决于您的Debian版本)
下载IntelliJ IDEA插件
注意:如果要使用此插件,您的系统必须已经安装Genymotion
下载Eclipse插件
此插件也可以通过Eclipse直接安装:Help->Install New Software,然后添加下面的地址:http://plugins.genymotion.com/eclipse
注意:如果要使用此插件,您的系统必须已经安装Genymotion
目前最新版的是2.2.0版本的,建议同时下载其IDE插件,这里我下载了Eclipse插件,这里将jar包拷到eclipse/plugins目录下重启eclipse即可.

2.安装genymotion

下载完后.执行下面三个命令,

amosli@amosli-pc:~/workspace/genymotion$ ./genymotion-2.2.0_x64.bin 
amosli@amosli-pc:~/workspace/genymotion$ cd genymotion/
amosli@amosli-pc:~/workspace/genymotion/genymotion$ ./genymotion

如果没有安装vitualbox,那么会提示要安装vitualbox,可以接着安装vitualbox;已安装的可以跳过下面的内容.

二.下载.安装vitualbox

 1.下载vitualbox

https://www.virtualbox.org/wiki/Linux_Downloads

VirtualBox 4.3.10 for Linux

Note: The package architecture has to match the Linux kernel architecture, that is, if you are running a 64-bit kernel, install the appropriate AMD64 package (it does not matter if you have an Intel or an AMD CPU). Mixed installations (e.g. Debian/Lenny ships an AMD64 kernel with 32-bit packages) are not supported. To install VirtualBox anyway you need to setup a 64-bit chroot environment.

Please choose the appropriate package for your Linux distribution:

这里我选择的是ubuntu 12.04 amd64 位的.下载速度挺慢的,可以从sourceforge上面下载(http://sourceforge.jp/projects/sfnet_virtualbox.mirror/releases/),速度稍微快一点点,其实也差不多.

 

2.安装vitualbox

如果是deb格式的数据可以直接双击安装.

同时要加入一下其密钥:http://download.virtualbox.org/virtualbox/debian/oracle_vbox.asc

amosli@amosli-pc:~/Desktop$ sudo apt-key add oracle_vbox.asc
[sudo] password for amosli: 
OK

Ubuntu的/ Debian的用户可能要安装DKMS软件包,以确保VirtualBox主机的内核模块( vboxdrv , vboxnetflt和vboxnetadp )得到正确更新,可以执行下面的命令进行安装:

amosli@amosli-pc:~$ sudo apt-get install dkms

 

三.启动genymotion

安装完vitualbox后,再次重新启动genymotion

 

 

 

 

[转载]为 Node.js 开发者准备的8本免费在线电子书_IT新闻_博客园

mikel阅读(1057)

[转载]为 Node.js 开发者准备的8本免费在线电子书_IT新闻_博客园.  Node.js 是一套用来编写高性能网络服务器的 JavaScript 工具包,一系列的变化由此开始。比较独特的是,Node.js 会假设你是在 POSIX 环境下运行它 Linux 或 Mac OS X。如果你是在 Windows 下,那就需要安装 MinGW 以获得一个仿 POSIX 的环境。在 Node 中,Http 是首要的。Node 为创建 http 服务器作了优化,所以你在网上看到的大部分示例和库都是集中在 web 上(http 框架、模板库等)。

本文向你推荐 8 本免费的在线 Node.js 电子书。

1) Best Node.js eBook –  Node Beginner

http://static.oschina.net/uploads/img/201405/16071221_qumk.png

  2) Free Node.js eBook – Smashing Node

best free ebooks for node.js - smashingnodejs

  3) Best Free Node.js eBook – MasteringNode

best free ebooks for node.js - masteringnode

  4) Best Node.js eBook – Mixu’s Node

best free ebooks for node.js - mixunode

  5) Free Node.js eBook – Node: Up and Running

best free ebooks for node.js - nodejs

  6) Node.js Book – Node Guide

best free ebooks for node.js - nodeguide

  7) Pro Node.js for Developers

best free ebooks for node.js - pro-node

  8) Best Book –  Node.js the Right Way

node