博客 (71)

代码一:

#if DEBUG
    // Debug 模式
#else
    // Release 模式
#endif

作用:判断 调试模式 / 发布模式

条件:须在项目属性的生成页中勾选“定义 DEBUG 常量”

适用:记录 EF 生成的 SQL 语句、控制定时器只在生产环境执行等

注意:调试和发布时应选择正确的模式


代码二:

if(Request.IsLocal) { }

作用:判断 本地 / 远程

条件:在会话中

适用:显示错误详情、记录 EF 生成的 SQL 语句

注意:不能用于定时器、Application_Start、异步操作等非会话中


代码三:

if (System.Net.IPAddress.IsLoopback(HttpContext.Connection.RemoteIpAddress)) { }

说明:ASP.NET Core 中的写法,用于检测 IP 地址是否为本地回环地址(IPv4 为 127.0.0.1,IPv6 为 ::1)

若在视图中使用,HttpContext 改为 ViewContext.HttpContext 或 Context

作用/条件/适用/注意:参“代码二”


代码四:

if(HttpRuntime.AppDomainAppPath == "X:\xxx\") { }

作用:判断当前网站根目录路径

条件:开发环境和生产环境的根目录路径不同

适用:判断根目录路径作相应的处理,适用于以上各种情况

注意:路径有变须要修改相应程序代码

xoyozo 7 年前
6,552

ASP.NET MVC 如果从控制器输出的是匿名类的集合,那么绑定到视图时会报错:

“object”未包含“xxx”的定义

简单的解决方法是 JSON 序列化再反序列化,例如:

ViewBag.list = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(q.ToList()));

上面是 ViewBag 的情形,如果是直接 return View(匿名类集合);

那么在视图页面:

@model IEnumerable<dynamic>


xoyozo 7 年前
4,025

“/”应用程序中的服务器错误。

无法为具有固定名称“MySql.Data.MySqlClient”的 ADO.NET 提供程序加载在应用程序配置文件中注册的实体框架提供程序类型“MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6, Version=6.10.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d”。请确保使用限定程序集的名称且该程序集对运行的应用程序可用。有关详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=260882。

打开 nuget,卸载并重新安装 MySql.Data 和 MySql.Data.Entity

如果没有效果,打开 web.config,检查是否多次定义了 MySql.Data.MySqlClient

xoyozo 7 年前
8,491

“/”应用程序中的服务器错误。

未能加载文件或程序集“MySql.Data.Entity.EF6, Version=6.10.7.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040)


6.10.7 版本的 MySql.Data.Entity.EF6.dll 文件发布后会自动变成 6.10.6,文件大小一样,只要用 FTP 上传覆盖一下即可。

xoyozo 7 年前
5,547

阅读官方文档了解基本概念和逻辑:http://doc-bot.tmall.com/docs/doc.htm?treeId=393&articleId=106714&docType=1

文档中的示例是以自己创建的实体来示范的,如果我们需要创建一个“找工作”的意图,那么不可能把所有的工种一一添加到实体中,必须在语料中使用 sys:any 这个系统实体来匹配工种。例如:

添加语料:我想找会计的工作。把会计标注为 sys:any,参数名称可以取为 job,必选,追问语句:请说出您要找的工作类型。这样,Web Hook 就能接收到参数 job 了,而且值可以为用户说的任意工种。


同样的,我们可以再添加一个“租房子”的意图。


有了两个或多个意图后,必须选择其中一个为默认意图。如果我们的意图功能有同等的重要性,那么可以再添加一个入口意图,引导用户自己选择需要使用的意图,实现类似 10086 的层级选项。


添加一个“想干嘛”的意图并设为默认,不管是否添加语料或参数,意图完成时最终还是会调用 Web Hook,那么我们就在 Web Hook 中返回引导语句,如:您要找工作还是租房子呢?


然后在“找工作”意图中添加语料:我要找工作。在“租房子”意图中添加语料:我要租房子。顺利引导用户进入相应意图。


为了避免对话中断,需要在“想干嘛”意图的 Web Hook 中将 resultType 设为 ASK_INF


AliGenie 的语料匹配过程并非是:

先匹配语料填充参数,再通过追问语句引导匹配其它语料填充剩余必选参数,最后调用 Web Hook。

我觉得更应该是:

先匹配语料填充参数,再通过追问语句引导单个填充剩余必选参数,最后调用 Web Hook。


也就是说追问语句应尽量引导用户回答单个实体值(如在查天气示例中:“明天”、“北京”,加一些助词之类的也是没问题的,毕竟是人工智能嘛),而不是引导用户去匹配其它语料。


当我们成功完成一次找工作的检索后,如果用户想继续询问其它岗位,需要再重新匹配一次语料。添加连续对话语料可以实现更简洁的对话场景:

连续对话语料:厨师

其中厨师标注为 job

可以实现如下对话:

我要找工作

请说您要找的工作类型

驾驶员

没有找到关于 驾驶员 的招聘信息。

司机呢

为您找到 司机 的招聘信息。 xxxxxxxx

如果没有这个连续对话语料,那么最后一句的回答是:对不起,我不明白你的意思


为了提高用户体验,当成功返回用户所要找的工作信息时追问“重听还是下一条?”,这时我在连续对话语料或普通语料中添加“重听”,会丢失 job 参数,直接返回了 job 参数的追问语句,无法调用 Web Hook,期待大家指点迷津。


更多需要注意的地方:

  • 修改配置(意图、实体等信息)后,如果在线测试未即时生效,可能有缓存或 bug,第二天再继续测试,当然也不排除配置有误,应仔细排查。

  • 文档中心底部可以直接向提交问题,官方会给予回复(不保证),及时性令人捉急。

  • 用户的语句是按:连续对话语料 -> 普通语料 -> 意图 -> 技能 -> 平台 的顺序来匹配的。也就是说,如果上一步是在处理意图 A 的逻辑,那么下一步会优先匹配意图 A 的连续对话语料和普通语料,如果未匹配,但匹配了意图 B 的语料,那么照样会执行意图 B 的逻辑。如果是真机测试,那么甚至是跳出技能来匹配官方技能(如唱一首歌)。

  • AliGenie 会在匹配语料之前对用户的话进行建模,如果添加了语料“我找工作”,实际测试中,有时候能匹配语句“我找工作”,有时候却不能,有时候甚至连“我找工作”都不能匹配,具体的匹配逻辑令人无法捉摸,可能这样更像是“人工智能”吧。

  • 真机测试时最好在手机上开启“天猫精灵”App,时刻观察天猫精灵识别到的内容,以减少我们对 AliGenie 处理逻辑的误判。

  • sys:time 可以匹配:今天、明天、后天、大后天等,但不能匹配“昨天”,神马情况?

  • AliGenie 目前还不完善,特别是添加修改语料时经常出错莫名错误和乱码,期待阿里能尽快完善,能让更多的开发者参与到其中来,共同推动 AI 发展。


xoyozo 7 年前
13,315

jquery.barrager.js 是一款优雅的网页弹幕插件,支持显示图片,文字以及超链接。支持速度、高度、颜色、数量等自定义。

使用方法参官方网站:http://yaseng.org/jquery.barrager.js/

jquery.barrager.min.js 会报错,改用 jquery.barrager.js(版本 1.1)

实战示例:https://xoyozo.net/Demo/DanMu

该示例重写了部分 CSS 样式。

注:info 属性支持 HTML,因此要自己过滤危险标签,建议仅保留 <img /> 标签

如果要显示在指定区域,可以使用 bottom 属性,例:

bottom: (Math.random() * (0.95 - 0.5) + 0.5) * $(window).height()

建议加 loading 锁,以适应网络不通畅的环境(ajax 时间超过定时间隔会导致请求到重复的数据)

跨域环境可以使用 JSONP 来实现,服务器端需设置 Access-Control-Allow-Origin 为 *。

xoyozo 8 年前
9,725

经常会看到这样似错非错的提示:

当前上下文中不存在名称"__o"

The name '__o' does not exist in the current context

image.png

实际上,我没有定义任何名为 __o 的变量。

发生这种情况的原因可能是使用了类似如下的代码:

<% if(true) { %>
<%= 1 %>
<% } %>
<%= 2 %>

为了在设计界面的 <%= %> 代码块中提供智能感知,ASP.NET(VB 或 C#)会自动生成一个名为“__o”的临时变量,这在页面编译器看到第一个 <%= %> 块时就完成了。但是在这里,<%= %> 块在 if 中出现,所以当关闭 if 后再使用 <%= %> 时,变量超出了定义的范围。

if (true)
{
    object @__o;
    @__o = 1;
}
@__o = 2;

解决方法:在页面的早期添加一个虚表达式。例如:<%= "" %>。这将不会呈现任何内容,并且它将确保在任何潜在的 if(或其他范围界定)语句之前,在 Render 方法中将 __o 声明为顶级。


当然还有一种治标不治本的方法就是隐藏这些错误提示(这并不影响程序正常运行):

点击错误列表面板左上角的过滤器按钮,CS0103,其中包含错误代码:当前上下文中不存在名称"__o",这些错误将不再显示,您仍然可以有其他 IntelliSense 错误和警告。

I
转自 IT屋 8 年前
6,087
  1. 下载 Senparc 官方 Sample


  2. 打开:\WeiXinMPSDK-master\src\Senparc.Weixin.MP.Sample\Senparc.Weixin.MP.Sample.sln


  3. 工具 - NuGet 包管理器 - 管理解决方案的 NuGet 程序包 - 更新

    选择所有的包(Microsoft.Net.Http 无法更新,暂不勾选),更新

    Microsoft.Net.Http 是个老顽固,无法更新也无法卸载,原因是它的语言包 Microsoft.Net.Http.zh-Hans 无法安装。我们切换到“已安装”选项卡,搜索“Microsoft.Net.Http”,选中 Microsoft.Net.Http.zh-Hans 并卸载它,现在我们可以更新 Microsoft.Net.Http 了。


  4. 【可能】运行报错:

    “/”应用程序中的服务器错误。

    配置错误

    说明: 在处理向该请求提供服务所需的配置文件时出错。请检查下面的特定错误详细信息并适当地修改配置文件。 

    分析器错误消息: 创建 system.web.webPages.razor/host 的配置节处理程序时出错: 未能加载文件或程序集“System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040)

    源错误: 

    行 4:    <configSections>
    行 5:      <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
    行 6:        <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    行 7:        <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
    行 8:      </sectionGroup>


    源文件: E:\WeiXinMPSDK-master\src\Senparc.Weixin.MP.Sample\Senparc.Weixin.MP.Sample\views\web.config    行: 


    展开 Senparc.Weixin.MP.Sample 引用,查看 System.Web.Razor 的属性,复制版本号。打开 \views\web.config,修改 System.Web.WebPages.Razor 的 Version(有 3 处)。


  5. 对于小型开发项目来说,我习惯将所有代码放在同一个项目(Project)中来,所以做了以下处理:(盛派官方分离这些代码是为了同时供 MVC 和 WebForms 重用)

    在 Senparc.Weixin.MP.Sample 中添加文件夹 CommonService,将 Senparc.Weixin.MP.Sample.CommonService 项目中的文件夹和文件(.config 除外)拖到 CommonService 文件夹中。

    卸载 Senparc.Weixin.MP.Sample.CommonService 项目并删除引用。


  6. 在解决方案的 Libraries 文件夹中,我们看到 Sample 项目了以下功能模块,这些就是 Senparc.Weixin 的源代码,如果不需要修改,可以卸载它们改为使用从 NuGet 获取,以便随时更新到最新版本:

    Senparc.WebSocket

    Senparc.Weixin

    Senparc.Weixin.MP

    Senparc.Weixin.MP.MvcExtension NuGet 中对应的包名为:Senparc.Weixin.MP.MVC

    Senparc.Weixin.Open

    Senparc.Weixin.Work

    Senparc.Weixin.WxOpen

    Senparc.Weixin.Cache.Memcached

    Senparc.Weixin.Cache.Redis

    卸载这些项目,并在 NuGet 中安装这些模块(搜索“Senparc.Weixin”第一页都在了,认准作者 Jeffrey Su)

    单元测试项目中的引用也按需更换,当然也可以卸载这些单元测试项目(视情况保留 Senparc.Weixin.MP.Sample.Tests) 。


  7. 【可能】运行报错


    HTTP Error 500.19 - Internal Server Error

    无法访问请求的页面,因为该页的相关配置数据无效。

    配置源:

    134:     </sessionState>

    135:   <sessionState customProvider="Memcached" mode="Custom">

    136: <providers>


    可以在 Global.asax 的 RegisterWeixinCache() 中修改 Memcached 配置。

    或者打开 Web.config,找到 Memcached 所在的 sessionState 标签,注释掉,这样程序自动使用上方的 InProc(应用进程内)。

    如果开启了 ASP.NET State Service 服务,那么建议改用 StateServer,与 Custom 一样需要序列化,方便日后改用 Memcached 或 Redis。


  8. 再次更新 NuGet,否则会提示无法加载 Enyim.Caching 的错误(报错行:MemcachedObjectCacheStrategy.RegisterServerList(memcachedConfig);)。


  9. 接下来,进入微信公众平台,在左侧 开发 - 基本配置 中设置相关参数(填写“服务器地址(URL)”如“https://weixin.xoyozo.net/weixin/”),打开 Sample 项目中的 Web.config,配置我们的公众号参数。


  10. 将项目发布到服务器上,尝试向公众号发送消息吧。


  11. 单元测试

    设置单元测试中的公众号配置项:打开 Senparc.Weixin.MP.Test/CommonAPIs/CommonApiTest.cs,在 AppConfig 方法中可以看到支持两种配置方式,在 Senparc.Weixin.MP.Test/Config/test.config 配置文件中配置,或直接在 CommonApiTest.cs 文件中配置,前者格式如下,后者建议只在个人测试项目中使用。

    <Config>
      <AppId>YourAppId</AppId>
      <Secret>YourSecret</Secret>
      <MchId>YourMchId</MchId>
      <TenPayKey>YourTenPayKey</TenPayKey>
      <TenPayCertPath>YourTenPayCertPath</TenPayCertPath>
      <!-- 小程序 -->
      <WxOpenAppId>YourOpenAppId</WxOpenAppId>
      <WxOpenSecret>YourWxOpenSecret</WxOpenSecret>
    </Config>

    另外,将 _testOpenId 值改为自己的微信号在本公众号上的 openid。


  12. 微信网页授权(OAuth2)

    打开 OAuth2Controller 控制器,将“Index”Action 中的盛派的网址(有 2 处)改成:

    Request.Url.Scheme + "://" + Request.Url.Authority + "/Oauth2/……


  13. 未完待续……

xoyozo 8 年前
7,062

安装过程就不介绍了,主要记一下被动模式和端口开放设置。


FileZilla Server 默认以 21 端口安装,阿里云的安全组配置“入方向”规则:

协议类型:自定义 TCP,端口范围:21/21,授权对象 0.0.0.0/0


如果要配置“被动模式”,那么在 FileZilla Server 菜单中选择“Edit”- Settings

切换到“Passive mode settings”,打勾“Use custom port range”,并填写端口范围,譬如 30000-40000,确定。


同样在阿里云安全组中配置“入方向”规则:

协议类型:自定义 TCP,端口范围:30000/40000,授权对象 0.0.0.0/0


这样就可以使用被动模式连接了。

如果无法连接,可尝试打开 Windows Server 2016 的“服务器管理器”,选择右上角“工具”-“高级安全 Windows 防火墙”-“入站规则”-“新建规则”,将上述端口设为允许连接。

xoyozo 8 年前
6,965

UTC(世界协调时间)和 GMT(格林尼治标准时间)都与英国伦敦的本地时间相同。北京是东八区,即 UTC+8 或 GMT+0800

时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。

翻译成程序员语言就是指当前的本地时间与 1970-1-1 0:00:00 UTC 时间换算的本地时间相差的秒数
或者当前的 UTC 时间与 1970-1-1 0:00:00 UTC 时间相差的秒数

以我在北京时间 2000/1/1 8:00:00 站在东八区为例:

在 JS 中:

// 获取当前本地时间:
new Date()
// 返回:Jan 1 2000 8:00:00 GMT+0800(中国标准时间)

// 获取当前 UTC 时间字符串:
(Local Time).toUTCString()
// 返回:Jan 1 2000 0:00:00 GMT

// 初始化一个 UTC 时间 2000-1-1 0:00:00
new Date(Date.UTC(2000, 1 - 1, 1, 0, 0, 0))

// 获取 UTC 时间的本地时间字符串:
(UTC Time).toLocaleString()


// 本地时间 1970/1/1 8:00:00 的时间戳
new Date(1970, 1 - 1, 1, 8, 0, 0).getTime() / 1000
// 返回:0

// 本地时间 2000/1/1 8:00:00 的时间戳
new Date(2000, 1 - 1, 1, 8, 0, 0).getTime() / 1000
// 返回:946684800

// 本地时间 当前 的时间戳
new Date().getTime() / 1000

// UTC 时间 2000/1/1 0:00:00 的时间戳
new Date(Date.UTC(2000, 1 - 1, 1, 0, 0, 0)).getTime() / 1000
// 或
Date.UTC(2000, 1 - 1, 1, 0, 0, 0) / 1000
// 返回:946684800

在 C# 中:

DateTime 默认的 Kind 是 Local,使用 DateTime.SpecifyKind() 方法可以定义一个 UTC 时间
DateTime.Now 返回当前本地时间
DateTime.UtcNow 返回当前 UTC 时间

// 定义一个本地时间 2000/1/1 8:00:00
new DateTime(2000, 1, 1, 8, 0, 0)

// 定义一个 UTC 时间 2000/1/1 0:00:00
DateTime.SpecifyKind(new DateTime(2000, 1, 1), DateTimeKind.Utc)

// 将 UTC 时间转化为本地时间
(UTC Time).ToLocalTime()

// 将本地时间转化为 UTC 时间
(Local Time).ToUniversalTime()

再次说明,本地时间和 UTC 时间都是 DateTime 对象,关键看定义的时候是 Local 还是 Utc

// 本地时间 1970/1/1 8:00:00 的时间戳
(new DateTime(1970, 1, 1, 8, 0, 0) - DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc).ToLocalTime()).TotalSeconds
// 返回:0

// 本地时间 2000/1/1 8:00:00 的时间戳
(new DateTime(2000, 1, 1, 8, 0, 0) - DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc).ToLocalTime()).TotalSeconds
// 返回:946684800

// 本地时间 当前 的时间戳
(DateTime.Now - DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc).ToLocalTime()).TotalSeconds


// 将时间戳 946684800 转换为本地时间
DateTime.SpecifyKind(new DateTime(1970, 1, 1), DateTimeKind.Utc).ToLocalTime().AddSeconds(946684800)

实战:

如果我们要将时间戳精确到毫秒,那么 JS 直接 .getTime() 即可,不需要 / 1000,C# 将它转换为本地时间时用 AddMilliseconds 代替 AddSeconds。

中国跨越了多个时区却统一使用北京时间,所以国内网站只要记录本地时间即可;如果做国际站或者有不同国家的访客,除非全部由服务器端获取时间,否则客户端 JS 的本地时间(非时间戳)都需要转换成 UTC 时间来跟服务端的时间进行运算和保存,推荐使用时间戳在客户端和服务端之间传递,因为时间戳与时区无关,它是两个相同性质的时间(同为本地时间或同为 UTC 时间)的差值。

xoyozo 8 年前
13,933