[转载]清风乱谈 之 基于WCF的电子商务解决方案(一)

mikel阅读(989)

[转载]清风乱谈 之 基于WCF的电子商务解决方案(一) – 清风的.net研究小院 – 博客园.

首先,先用这个图片来说明我上次提到的多个组件服务器与WEB服务器之间的关系

这么做至少有3个明显的好处:

1. 将站点的压力平均化,提高网站的浏览速 度,降低服务器压力。

2. 保证各个应用组件之间逻辑的绝对分离,实 现了我们说的低耦合。

3. 应用服务器的接口可以多次复用,企业可以 通过重新组合应用服务器接口建立统一的电子商务体系。

该程序在VS2008下的结构如 下:

其中Orders对应着 订单管理组件服务系统”,Products对应着“产品目录管 理组件服务系统”,Marketing对应 着“市场营销及促销组件服务系统”,多个组件可以以IIS为宿主分别架设在多台服务器上。

这里是IIS宿 主站点的配置:

以下为WEB服务器对组件服务器 接口的调用方法:

public void UpdateQuantity(Guid productID, int quantity)

{

//此处为对产品目录管理组件服务系统中提供的Product GetProductByID(Guid productID);接口的调用

Product product = ServiceLoader.LoadService<IProduct>().GetProduct(productID);

if (product.Inventory < quantity)

{

throw new BusinessException(“Lack of inventory.”);

}

OrderDetail detail = this.Order.Details.Where(item => item.ProductID == productID).ToArray<OrderDetail>()[0];

detail.Quantity = quantity;

this.View.BindingOrderInfo(this.Order);

}

这段为接口调用在WEB.CONFIG文 件里面的相关配置代码

<client>

<endpoint address=http://localhost/PetShop/Products/productservice.svc behaviorConfiguration=petShopBehavior binding=ws2007HttpBinding contract=Artech.PetShop.Products.Service.Interface.IProductService name=productservice/>

<endpoint address=http://localhost/PetShop/Infrastructures/MembershipService.svc behaviorConfiguration=petShopBehavior binding=ws2007HttpBinding contract=Artech.PetShop.Infrastructures.Service.Interface.IMembershipService name=membershipservice/>

<endpoint address=http://localhost/PetShop/Orders/OrderService.svc behaviorConfiguration=petShopBehavior binding=ws2007HttpBinding contract=Artech.PetShop.Orders.Service.Interface.IOrderService name=orderservice/>

</client>

本次文章重点在把我昨天提出的理论找出一些实际的例子进行演示说明,这个码字和说话毕竟是有点区别 的,希望大家可以理解~

[转载].net框架读书笔记---CLR内存管理\垃圾收集

mikel阅读(1039)

[转载].net框架读书笔记—CLR内存管理\垃圾收集 – 恒星的恒心 – 博客园.

一、垃圾收集平台基本原理解析

C#中程序访问一个资源需要以下步骤:

  • 调用中间语言(IL)中的newobj指令,为表示某个特定资源的类型实例分配一定的内存空间。
  • 初始化上一步所得的内存,设置资源的初始状态,从而使其可以为程序所用。一个类型的实例构造器负责这样的初始化工作。
  • 通过访问类型成员来使用资源。
  • 销毁资源状态,执行清理工作。
  • 释放托管堆上面的内存,该步骤由垃圾收集器全权负责,值类型实例所占的内存位于当前运行线程的堆栈上,垃圾收集器并不负责这些资源的回收,当值类 型实例变量所在的方法执行结束时,他们的内存将随着堆栈空间的消亡而自动消亡,无所谓回收。对于一些表示非托管的类型,在其对象被销毁时,就必须执行一些 清理代码。

当应用程序完成初始化后,CLR将保留(reserve)一块连续的地址空间,这段空间最初并不对应任何的物理内存(backing storage)(该地址是一段虚拟地址空间,所以要真正使用它,它必须为其“提交”物理内存),该地址空间即为托管堆。托管堆上维护着一个指针,暂且称 之为NextObjPtr。该指针标识着下一个新建对象分配时在托管堆中所处的位置。刚开始的时候,NextObjPtr被设为CLR保留地址空间的基地 址。

中间语言指令newObj负责创建新的对象。在代码运行时,newobj指令将导致CLR执行以下操作:

  • 计算类型的所有实例字段(以及其基类型所有的字段)所需要的字节总数。
  • 在前面所的字节总数的基础上面再加上对象额为的附加成员所需的字节数:一个方法指针和一个SyncBlockIndex。
  • CLR检查保留区域中的空间是否满足分配新对象所需的字节数—–如果需要则提交物理内存。如果满足,对象将被分配在NextObjPtr指 针所指的地方。接着类型的实例构造器被调用(NextObjPtr会被传递给this参数),IL指令 newobj(或者说new操作符)返回其分配内存地址。就在newobj指令返回新对象的地址之前,NextObjPtr指针会越过新对象所处的内存区 域,并指示出下一个新建对象在托管堆中的地址。

下图演示了包含A,B,C三个对象的托管堆,如果再分配对象将会被放在NextObjPtr指针所演示的位置(紧跟C之后)

在C语言中堆分配内存时,首先需要遍历一个链表数据结构,一旦找到一个足够大的内存块,该内存块就会被拆开来,同时链表相应节点上的指针会得到 适当的调整。但是对于托管堆来说,分配内存仅仅意味着在指针上增加一个数值—显然要比操作链表的做法快许多,C语言都是在找到自由空间为其对象分配内 存,因此连续创建几个对象,他们将很有可能被分散在地址空间的各个角落。但是在托管堆中,连续分配的对象可以保证它们在内存中也是连续的。

就目前来看托管堆在实现的简单性和速度方面要远优于C语言的运行时中的堆。之所以这样是因为CLR做了大胆的假设—那就是应用程序的地址空 间和存储空间是无限的,显然这是不可能的。托管堆必须应用某种机制来允许这种假设。这种机制就是垃圾回收器。

当应用程序调用new创建对象时,托管堆可能没有足够的地址空间来分配该对象。托管堆通过将对象所需要的字节总数添加到NextObjPtr指 针表示的地址上来检测这种情况。如果得到的结果超出了托管堆的地址空间范围,那么托管堆将被认为已满,这时就需要垃圾收集器。,其实这种描述是过于简单 的,垃圾回收与对象的代龄有着密切的关系,还需继续学习垃圾收集。

二、垃圾收集算法

垃圾收集器通过检查托管堆中是否有应用程序不再使用的对象来回收内存。如果有这样的对象,它们的内存将被回收。那么垃圾收集器是这样知道应用程 序是否正在使用一个对象呢??还得继续学习。

每一个应用程序都有一组根(root),一个根是一个存储位置,其中包含着一个指向引用类型的内存指针。该指针或者指向一个托管堆中的对象,或 者被设置为null。例如所有的全局引用类型变量或静态引用类型都被认为是根。另外,一个线程堆栈上所有引用类型的本地变量或者参数变量也被认为是一个 根。最后,在一个方法内,指向引用类型对象的CPU寄存器也被认为是一个根。

当垃圾收集器开始执行时,它首先假设托管堆中的所有对象都是可收集的垃圾。换句话,垃圾收集器假设应用程序中没有一个根引用着托管堆中的对象。 然后垃圾收集器便利所有的根,构造出一个包含所有可达对象的图。例如,垃圾收集器可能会定位出一个引用托管对象的全局变量。下图展示了分配有几个对象的托 管堆,其中对象A,C,D,F为应用程序的根所直接引用。所有这些对象都是可达对象图的一部分。当对象D被添加到该图中时,垃圾收集器注意到它还引用着对 象H,于是对象H被添加到该图,垃圾回收器就这样子以递归的方式来遍历应用程序中所有的可达对象。

一旦该部分的可达对象完成后,垃圾回收器将检查下一个根,并遍历其引用的对象。当垃圾回收器在对象之间进行遍历时,如果发现某对象已经添加到可 达对象图中时(比如上图中的H,在检查D的时候已经将其添加到了可达对象图),它会停止沿着该对象标识的路径方向上遍历的活动。两个目的:

  • 可以避免垃圾收集器对一些对象的多次遍历,可高性能。
  • 如果两个对象之间出现了循环引用,可以避免遍历陷入无限循环(比如上图中D引用着H,而H又引用着D)。

垃圾收集器一旦检查完所有的根,其得到的可达对象将包含所有从应用程序的根可以访问的对象。任何不在该图中的对象将是应用程序 不可访问的对象,不可达的对象,因此也是可以被执行垃圾收集器的对象。垃圾收集器接着线性地遍历托管堆以寻找包含可收集垃圾对象的连续区域。

PS:CLR的垃圾收集机制对我来说有点非主流,在此之前,我一直认为是垃圾收集器直接去寻找不可达的对象,现在看来垃圾收集 器使用了逆向思维,通过找到可达对象来找到不可达的对象(这个原因还得继续思考)。

如果找到了较大的连续区域,垃圾收集器将会把内存中的一些非垃圾对象搬移到这些连续区块中以压缩堆栈,显然搬移内存中的对象将 使所有这些指向对象的指针变的无效。所以垃圾收集器必须修改应用程序的根以使它们指向这些对象更新后的位置。另外,如果任何对象包含有指向这些对象的指 针,那么垃圾收集器也会负责矫正它们。托管堆被压缩以后,NextObjPtr指针将被设为指向最后一个非垃圾对象之后。下图展示了对于上面图执行垃圾收 集器后的托管堆。

可见垃圾回收器对于应用程序的性能有不小的影响,CLR采用了代龄等措施来优化了性能(以后学习)。

因为任何不从应用程序的根中访问的对象都会在某个时刻被收集,所以应用程序将不可能发生内存泄漏,另外应用程序也不可能再访问已经被释放的对 象。因为如果对象可达,它将不可能被释放;而如果对象不可达,应用程序必将无法访问到它。

下面代码演示了垃圾收集器是这样分配管理对象的:

代码

class Program { static void Main(string[] args) { //在托管堆上ArrayList对象,a现在就是一个根 ArrayList a = new ArrayList(); //在托管堆上创建10000个对象 for (int i = 0; i < 10000; i++) { a.Add(new Object());//对象被创建在托管堆上 } //现在a是一个根(位于线程堆栈上)。所以a是一个可达对象 //,a引用的10000个对象也是可达对象 Console.WriteLine(a.Count); //在a.Count返回后,a便不再被Main中的代码所引用, //因此也就不再是一个根。如果另外一个线程在a.Count的结果被 //传递给WirteLine之前启动了垃圾收集,那么a以及它所引用的10000个对象将会被回收。 //上面for里面的变量i虽然在后面的代码中不再被引用,但由于它是一个值类型,并不存在于 //托管堆中,所以它不受垃圾收集器的管理,它在Main方法执行完毕后会随着堆栈的消失而自动 //被系统回收 Console.WriteLine("End of method"); } }

CLR之所以能够使用垃圾回收机制,有一个原因是因为托管堆总是能知道一个对象的实际类型,从而使用其元数据信息来判断一个对象的那些成员引用 着其他对象。

欢迎转载,请注明出处 贺爱平 http://www.cnblogs.com/heaiping

[转载]jQuery第六课:实现一个Ajax的TreeView

mikel阅读(1099)

[转载]jQuery第六课:实现一个Ajax的TreeView – 技术 理论 及其它 – 博客园.

TreeView是ASP.NET自带的控件,不过自带的控件在灵活性上有诸多限制。在JQuery的帮助下,自己实现一个TreeView也不困 难。本文是前几篇文章所讲内容的一个综合演练。最终实现的效果是一个目录文件查看器,如图所示:

image

其原理是,当用户单击一个目录的时候,将这个 目录的路径发送给服务器端,服务器端返回这个目录中的文件和目录信息。在服务器端,定义一个如下的类来表示要传递的文件信息:

public class FileInformation
{
    public string FullPath
    {
        get;        set;
    }
    public string Name
    {
        get;        set;
    }
    public string Info
    {
        get;        set;
    }
    public bool IsFolder
    {
        get;        set;
    }
}

其中FullPath是文件的完整路径,用于获取它的子文件夹/文件用,Name是文件的名字,用于显示,IsFolder是区分这条数据是一个文 件还是文件夹,以便用不同的图标来显示,最后一个Info是一些附加信息,在此例中没有用到。根据一个路径获得目录中的文件信息的C#代码很简单,顺便就 贴在这里:

public class FileManager
{
    public static List<FileInformation> GetFolderContent(string fullpath)
    {
        List<FileInformation> res = new List<FileInformation>();
        DirectoryInfo info = new DirectoryInfo(fullpath);
        if (info.Exists)
        {           
            foreach (DirectoryInfo d in info.GetDirectories())
            {
                res.Add(new FileInformation
                {
                    FullPath = d.FullName, Name = d.Name,IsFolder = true,
                    Info = "Any More Information goes here"
                });
            }
            foreach (FileInfo f in info.GetFiles())
            {
                res.Add(new FileInformation
                {
                    FullPath = f.FullName,Name = f.Name,IsFolder = false,
                    Info = "Any More Information goes here"
                });
            }
        }
        return res;
    }
}

此例中采用JSON数据的格式来传递这些信息。因此要将这些数据序列化。在.Net 3.5中,有现成的将实体类序列化成JSON数据的类,使用方法如下

public static string ToJson<T>(T obj)
    {
        DataContractJsonSerializer d = new DataContractJsonSerializer(typeof(T));
        System.IO.MemoryStream ms = new System.IO.MemoryStream();
        d.WriteObject(ms, obj);
        string strJSON = System.Text.Encoding.UTF8.GetString(ms.ToArray());
        ms.Close();
        return strJSON;
    }

如果是.net 2.0,则可以寻找一些第三方的组件,自己写一个也不麻烦。

至此,服务器端的主要工作已经完成了。新建一个Genric Handler文件,filelist.ashx,代码如下,简单的响应下请求,输出数据即可:

public class FileList : IHttpHandler {    
    public void ProcessRequest (HttpContext context) {
        string path = context.Request.QueryString["path"];
        string data = JsonHelper.ToJson<List<FileInformation>>(FileManager.GetFolderContent(path));
        context.Response.Write(data);
    } 
    public bool IsReusable {
        get {
            return false;
        }
    }
}

下面考虑客户端html代码的编写。最主要的就是两个事件,也就是鼠标点击发送ajax请求,处理返回的json数据生成html代码,鼠标再次点 击将html代码清空。在这里,采用ul li来显示这个treeview,在li中有一个不可见的span,里面包含了文件完整的路径,它将用作发起ajax请求的参数,但是对用户是不可见的。

HTML代码很简单,就4行:

<body>
<ul>
</ul>
</body>

首先需要初始化一个根目录,例如D:,代码如下:

$(function() {
            $('<li class="folder">D:\\<span class="fullpath">D:\\</span></li>').appendTo('ul');

            $('li').hover(function() {
                $(this).css('cursor', 'pointer');
            },
                function() { $(this).css('cursor', 'default'); });

            $('li.folder').toggle(LoadFile, CloseFolder);
        });

构造好一个li结点,添加到ul中去,然后设置下鼠标动作的样式,最后为其绑定事件处理程序,LoadFile和CloseFolder。

function LoadFile(event) {
            if (this == event.target) {
                var path = $(this).find('span').html();
                var node = $('<ul>');
                $(this).append(node);
                $.getJSON('filelist.ashx', { path: path }, function(data) {
                    $.each(data, function() {
                        if (this.IsFolder) {
                            node.append($('<li>').addClass('folder').html(this.Name).append($('<span>').addClass('fullpath').html(this.FullPath)));
                        }
                        else {
                            node.append($('<li>').addClass('file').html(this.Name).append($('<span>').addClass('fullpath').html(this.FullPath)));
                        }
                    });
                    node.find('li.folder').toggle(LoadFile, CloseFolder);
                });                 
            }
        }

首先要判断event的target和 this是否是同一个对象,以避免点击子节点事件浮升的时候造成多次触发。首先利用find和html函数获得完整的路径。构造好一个ul节点并把它添加 到当前的li中。此时ul是空的,接下来发起ajax请求,获得服务器端的数据。对每条数据生成一个li,其中对于是否是目录加以判断,生成带有不同 class的li,再加到node中。最后,不要忘记为新增的节点也绑定事件处理程序。代码还是比较简单的,至于关闭目录节点的代码就更加简单了,

function CloseFolder(event) {
    if (this == event.target)
        $(this).find('ul').remove();
}

至此此范例已经完成了。还少了几句css,不再列出。

这个例子实现的功能和样式都比较粗糙,不过在此基础上做更多的扩展和美化已经不是难事。例如可以加上一点现成的动画效果:

function CloseFolder(event) {
    if (this == event.target) {
        var node = $(this).find('ul');
        node.hide('slow', function() { $(this).find('ul').remove(); });                
    }
}

先隐藏,再删除。类似地,可以加载完毕后立刻隐藏, 再淡出。

[转载]Asp.net MVC +JQueryValidation + AjaxForm

mikel阅读(1048)

[转载]Asp.net MVC +JQueryValidation + AjaxForm – TT – 博客园.

效果图:

image

image

image

image

主要的代码:

HomeController.cs

using System.Linq;
using System.Threading;
using System.Web.Mvc;
using JqueryValidate.Models;

namespace JqueryValidate.Controllers
{
    [HandleError]
    public class HomeController : BaseController
    {
        public ActionResult Index()
        {
            var model = Database.List;
            return View("Index",model);
        }

        public ActionResult About()
        {
            return View();
        }

        public ActionResult Edit(int? id)
        {
            Thread.Sleep(1000);
            var model = Database.List.FirstOrDefault(z => z.Id == id) ??new MyModel {Id = 0};
            return PartialView("Edit", model);
        }

        [HttpPost]
        public ActionResult Edit(MyModel modelel)
        {
            if (!ModelState.IsValid)
                return JsonError(GetError(ModelState), true);
            if (modelel.Id != 0)
            {
                var find = Database.List.FirstOrDefault(z => z.Id == modelel.Id);
                if (find != null)
                {
                    find.Name = modelel.Name;
                    find.Address = modelel.Address;
                    find.Age = modelel.Age;
                    return Success("更新成功|",true);
                }
            }
            modelel.Id = Database.List.Count + 1;
            Database.List.Add(modelel);
            return Success("创建成功|",true);
        }
    }
}

MyModel.cs

using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace JqueryValidate.Models
{
    public class MyModel
    {
        [HiddenInput(DisplayValue = false)]
        public int Id{ get; set;}

        [DisplayName("姓名")]
        [Required]
        public string Name{ get; set;}

        [DisplayName("地址")]
        [Required]
        [StringLength(5,ErrorMessage = "不能大于个字符")]
        public string Address{ get; set;}

        [DisplayName("年ê龄?")]
        [Required]
        [Range(1,100,ErrorMessage = "必须填写1-100以内的整数")]
        public int Age { get; set;}
    }
}

Database.cs

using System.Collections.Generic;

namespace JqueryValidate.Models
{
    public static class Database
    {
        static Database()
        {
            List = new List<MyModel>();
        }

        public static List<MyModel> List { get; set;}
    }
}

Index.aspx

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<List<JqueryValidate.Models.MyModel>>" %>
<%@ Import Namespace="JqueryValidate.Models" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Home Page
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <%=Html.ActionLink("新?建¨", "Edit", new {}, new {@class = "d", width = 400, height = 300})%>
    <table>
        <thead>
        <tr>
            <th>Id</th>
            <th width="200">姓?名?</th>
            <th width="200">地?址·</th>
            <th>年ê龄?</th>
            <th>&nbsp;</th>
            </tr>
        </thead>
        <tbody>
        <%foreach (MyModel item in ViewData.Model)
            {%>
            <tr>
                <td><%=item.Id%></td>
                <td><%=item.Name%></td>
                <td><%=item.Address%></td>
                <td><%=item.Age%></td>
                <td><%=Html.ActionLink("编à辑-", "Edit", new {id = item.Id},new {@class = "d", width = 400, height = 300})%></td>
            </tr>
        <%}%>
        </tbody>
    </table>
</asp:Content>

Edit.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<JqueryValidate.Models.MyModel>" %>
<script src="/Scripts/jquery.form.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.pack.js" type="text/javascript"></script>
<%Html.EnableClientValidation();%>
<%using (Html.BeginForm("Edit", "Home", new {}, FormMethod.Post, new {})){%>
        <%=Html.EditorForModel()%>
        <input type="submit" value="确·认?" />
<%}%>
<script src="/Scripts/MicrosoftMvcJQueryValidation.js" type="text/javascript"></script>
<script type="text/javascript">
    //<![CDATA[
    $("input:text").css({
        border: "solid 1px #ccc",
        padding: "4px"
    });
    $("#form0 input:submit").button();
    function validate(formData, jqForm, options){
        return $("#form0").valid();
    }
    $(function(){
        var ajaxProjectImageListOptions = {
            beforeSubmit: validate ,            
            success: function(html){
                var result;
                try {
                    result = eval('(' + html + ')');
                } 
                catch (ex) {
                    result = html;
                }
                if (result.Error != undefined) {
                    alert(result.Text);
                }
                else {
                    $("#dialogPanel").dialog({
                        autoOpen: false,
                        modal: true,
                        buttons: {
                            关?闭?: function(){
                                $("#dialogPanel").empty();
                                $(this).dialog('close');
                            }
                        }
                    });
                    $("#dialogPanel").html(html);
                    $("#dialogPanel").dialog('open');
                }
            }
        };
        $("#form0").ajaxForm(ajaxProjectImageListOptions);
    });
    //]]>
</script>

源码下载:/Files/francis67/JqueryValidate.rar

[转载]Silverlight 4 中摄像头的运用—part1

mikel阅读(1048)

[转载]Silverlight 4 中摄像头的运用—part1 – 我和未来有约会 – 博客园.

输入的视频

摄像头经过一个Video对象就能让你看到视频,而这个对象是一个显示对象,所以显示对象能做得事情,它都能做,比如滤镜,变形,混合模式等 等。当然最强大的还是使用WriteableBitmap画出视频内容。这样,通过图像分析、 比较等等,对于图像处理来说就有着无限可能。

这里来看看如何引用摄像头并看到拍摄的视频。
===================================

01 public partial class MainPage : UserControl
02 {
03 CaptureSource _captureSource;
04 VideoCaptureDevice _video;
05 public MainPage()
06 {
07 InitializeComponent();
08 _captureSource = new CaptureSource();
09 _video = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
10 if (_video != null) {
11 _captureSource.VideoCaptureDevice = _video;
12 }
13 btnStart.Click += new RoutedEventHandler(btnStart_Click);
14 }
15
16
17
18 void btnStart_Click(object sender, RoutedEventArgs e)
19 {
20 if (CaptureDeviceConfiguration.AllowedDeviceAccess ||
21 CaptureDeviceConfiguration.RequestDeviceAccess())
22 {
23 _captureSource.Start();
24 }
25 }
26 }

===================================

有了摄像头,就该输出视频了。把摄像头的数据作为视频刷子赋值到Rectangle对象上即可,再把Rectangle加入到场景中。
===================================

01 public partial class MainPage : UserControl
02 {
03 CaptureSource _captureSource;
04 VideoCaptureDevice _video;
05 VideoBrush _videoBrush;
06 Rectangle _rect;
07 public MainPage()
08 {
09 InitializeComponent();
10 _captureSource = new CaptureSource();
11 _video = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
12 if (_video != null) {
13 _captureSource.VideoCaptureDevice = _video;
14 _videoBrush = new VideoBrush();
15 _videoBrush.SetSource(_captureSource);
16 }
17 btnStart.Click += new RoutedEventHandler(btnStart_Click);
18 }
19
20
21
22 void btnStart_Click(object sender, RoutedEventArgs e)
23 {
24 if (CaptureDeviceConfiguration.AllowedDeviceAccess ||
25 CaptureDeviceConfiguration.RequestDeviceAccess())
26 {
27 _rect = new Rectangle();
28 _rect.Width = 300;
29 _rect.Height = 225;
30
31
32
33 _rect.Fill = _videoBrush;
34
35
36
37 LayoutRoot.Children.Add(_rect);
38
39
40
41 _captureSource.Start();
42 }
43 }
44 }
===================================

视频尺寸和质量
场景上出现了摄像头拍摄的内容。但画面不够大,也不像其它看到的视频那样清晰。 这不是因为Silverlight不行,而是由于默认的设置不好。视频可以在创建时设置其大小,默认是640×480。我们可以查看Video对象。

这里可知他一共支持了21种格式。通过简单的设置便可以更改摄像头的清晰度。

_video.DesiredFormat = _video.SupportedFormats[3];

要 知道,越高的fps,机器在处理视频时越费劲。对于类似视频会议这种东西,视频的质量和尺寸并不是非常重要,所以不要为此花太大精力。

视频和位图
正如之前说的,摄像头最强大的应用是混合WriteableBitmap。所以,通过Render把视频绘 制在一个WriteableBitmap对象里,就能以像素级来控制整个东西了。当然,对于动画,就需要不停的绘制。

===================================

01 void btnStart_Click(object sender, RoutedEventArgs e)
02 {
03 if (CaptureDeviceConfiguration.AllowedDeviceAccess ||
04 CaptureDeviceConfiguration.RequestDeviceAccess())
05 {
06 _rect = new Rectangle();
07 _rect.Width = 320;
08 _rect.Height = 240;
09
10
11
12 _rect.Fill = _videoBrush;
13 _rect.Visibility = Visibility.Collapsed;
14 LayoutRoot.Children.Add(_rect);
15
16
17
18 _wb = new WriteableBitmap(_rect, null);
19 _wb_image = new Image();
20 _wb_image.Width = 320;
21 _wb_image.Height = 240;
22 _wb_image.Source = _wb;
23
24
25
26 LayoutRoot.Children.Add(_wb_image);
27
28
29
30
31 _captureSource.Start();
32 _isEnableCamera = true;
33 }
34 }
35 void OnRender(object sender, EventArgs e)
36 {
37 if (_isEnableCamera)
38 {
39 _wb.Render(_rect, null);
40 _wb.Invalidate();
41 }
42 }
===================================

反转图像
要知道,用户在摄像头前所做的反应,总希望和镜子中一样,人往左,视频中人也往左,人 往右,其也往右。但事实并不是这样,所以需要我们来为之做反转图像。
===================================

01 void OnRender(object sender, EventArgs e)
02 {
03 if (_isEnableCamera)
04 {
05
06
07
08 MatrixTransform transform = new MatrixTransform();
09 transform.Matrix = new Matrix(-1, 0, 0, 1, 320, 0);
10
11
12
13 _wb.Render(_rect, transform);
14 _wb.Invalidate();
15 }
16 }

===================================

对象的绘制以传入的变形矩阵为依据。通过矩阵能做很多事情,虽然只有很简单的前四个参数,它们却控制着缩放,旋转和形变。这里让图形的x轴缩放率等 于 -1,意味着水平反转它。接着,0的意思是指图形没有旋转和形变,最后一个1是说y轴保持100%不变。但仅仅如此,还看不到任何东西。因为此时的矩阵是 从右向左扩展的,而最后两个参数是把矩阵移到图形的右上角,这样就保证落入了可视范围。

分析像素
现在你拥有了像素的控制权,该如何处置?记得我之前说过不要高质量、高分辨率的视频嘛?我 现在要更进一步的说,越低分辨率、越低质量越好。就算只有320×240大小的视频图像,每一帧要处理的像素也有76800个啊。所以不要搞太大的视频来 玩。事实上,你将会发现我要做的第一件事情,是设计如何除掉这么多像素带来的巨大信息。一般来说,我们只对视频的某个颜色区域,或者对比度之类的感兴趣。
分析颜色
首先我们尝试着跟踪一个具体颜色。假如用户拿一个颜色(红色)比较突出的东西在摄像头前面晃动,我们能跟踪其位置就算成功。

首先了解一下像素数组的排列方式。

所以像素的算法是

int pixel = bmSource.Pixels[y * imageWidth + x]
接下来根据pixel 反向算回颜色
1 Color color = Color.FromArgb(
2 (byte)((pixel >> 24) & 0xff),
3 (byte)((pixel >> 16) & 0xff),
4 (byte)((pixel >> 8) & 0xff),
5 (byte)((pixel) & 0xff));
有了以上的原理就可以分析WriteableBitmap的颜色了。

[转载]Silverlight中摄像头的运用—part2

mikel阅读(1304)

[转载]Silverlight中摄像头的运用—part2 – 我和未来有约会 – 博客园.

将跟踪颜色视作输入

好了,我们能够跟踪到这个颜色了,那这么做的意义是什么呢?实际上,我们可以根据它的位置来移动东西。接下来的例子中,创建的一个球会跟随这个颜色 一起移动。你可以用来作出很诡异的对象跟随画面移动的效果。

关键代码:

===================================
//part2
Ellipse ball = new Ellipse();
CompositeTransform ballctf;
ball.Width = 20;
ball.Height = 20;
ball.Fill = new SolidColorBrush(Colors.Orange);
ballctf = new CompositeTransform();
ball.RenderTransform = ballctf;


labRes.Content = “捕获颜色=” + found.ToString();
labPoint.Content = “坐标=” + _lastPoint.X.ToString() + “,” + _lastPoint.Y.ToString();

ballctf.TranslateX = _lastPoint.X;
ballctf.TranslateY = _lastPoint.Y;

Debug.WriteLine(found);
===================================

分析移动区域

在这一节,我们虽然还不去涉及如何跟踪物体的具体轨迹,但会知道如何判断是否有移动。  一个基本概念是:如果有移动,每帧的画面会明显不同。所以,如果发现两帧画面中位图的像素有不同的地方,就能知道发生了移动。

有两个潜在元素。第一,我们需要两张位图。第二,我们还需要一个比较函数。如果,你正在想着是否需要遍历所有像素来进行比较,那么我告诉你,这里有一个 很实用的技巧:使用混合模式。绘制时如果不指定混合模式,新的像素值就会完全覆盖以取代存在的像素值。这也是我们至今为止一直在做的事情。如果使用混合模 式,新的像素会影响已存在的像素,两张图片会以一种特别的方式混合在一起。而此刻,我们要用的混合模式叫做difference(差异),它对两张图片的 红、绿、蓝三个通道的每个像素进行一次比较,然后给出它们之间的相减所得的差值。如果两个像素完全一致,那么结果就是0,也就是黑色,否则就是别的其它什 么值(颜色)。这样,我们就把跟踪移动的问题简化了,只要寻找非黑色区域即可。
===================================
public partial class MotionTrackingUserControl
{
CaptureSource _captureSource;
VideoCaptureDevice _video;
VideoBrush _videoBrush;
Rectangle _rect;
Image _wb_image;
WriteableBitmap _wb = null;
bool _isEnableCamera = false;

WriteableBitmap _newFrameBitmap;
WriteableBitmap _oldFrameBitmap;

Image _newFrame;
Image _oldFrame;

public MotionTracking()
{
InitializeComponent();

_captureSource = new CaptureSource();
_video = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
if (_video != null)
{
_video.DesiredFormat = _video.SupportedFormats[1];
_captureSource.VideoCaptureDevice = _video;
_videoBrush = new VideoBrush();
_videoBrush.SetSource(_captureSource);
}
CompositionTarget.Rendering += new EventHandler(OnRender);

btnStart.Click += new RoutedEventHandler(btnStart_Click);

this.imageEffectsListBox.Items.Add(new NormalEffect());

this.imageEffectsListBox.Items.Add(new DarkenEffect());
this.imageEffectsListBox.Items.Add(new MultiplyEffect());
this.imageEffectsListBox.Items.Add(new ColorBurnEffect());
this.imageEffectsListBox.Items.Add(new LinearBurnEffect());

this.imageEffectsListBox.Items.Add(new LightenEffect());
this.imageEffectsListBox.Items.Add(new ScreenEffect());
this.imageEffectsListBox.Items.Add(new ColorDodgeEffect());
this.imageEffectsListBox.Items.Add(new LinearDodgeEffect());

this.imageEffectsListBox.Items.Add(new OverlayEffect());
this.imageEffectsListBox.Items.Add(new SoftLightEffect());
this.imageEffectsListBox.Items.Add(new HardLightEffect());
this.imageEffectsListBox.Items.Add(new VividLightEffect());
this.imageEffectsListBox.Items.Add(new LinearLightEffect());
this.imageEffectsListBox.Items.Add(new PinLightEffect());

this.imageEffectsListBox.Items.Add(new DifferenceEffect());
this.imageEffectsListBox.Items.Add(new ExclusionEffect());

this.imageEffectsListBox.Items.Add(new GlowEffect());
this.imageEffectsListBox.Items.Add(new ReflectEffect());

this.imageEffectsListBox.Items.Add(new HardMixEffect());
this.imageEffectsListBox.Items.Add(new NegationEffect());
this.imageEffectsListBox.Items.Add(new PhoenixEffect());

this.imageEffectsListBox.Items.Add(new AverageEffect());

this.imageEffectsListBox.SelectedIndex = 0;
}

void btnStart_Click(object sender, RoutedEventArgs e)
{
if (CaptureDeviceConfiguration.AllowedDeviceAccess ||
CaptureDeviceConfiguration.RequestDeviceAccess())
{
_rect = new Rectangle();
_rect.Width = 320;
_rect.Height = 240;

_rect.Fill = _videoBrush;
_rect.Visibility = Visibility.Collapsed;
video_Canvas.Children.Add(_rect);

_newFrameBitmap = new WriteableBitmap(_rect, null);
_oldFrameBitmap = new WriteableBitmap(_rect, null);

_newFrame = new Image();
_newFrame.Width = 320;
_newFrame.Height = 240;
_newFrame.Source = _newFrameBitmap;
_newFrame.Visibility = Visibility.Collapsed;

_oldFrame = new Image();
_oldFrame.Width = 320;
_oldFrame.Height = 240;
_oldFrame.Source = _oldFrameBitmap;

video_Canvas.Children.Add(_oldFrame);
video_Canvas.Children.Add(_newFrame);

_captureSource.Start();
_isEnableCamera = true;

Thread thread = new Thread(new ThreadStart(ThreadProc));
thread.Start();

}
}

void OnRender(object sender, EventArgs e)
{
if (_isEnableCamera)
{
MatrixTransform transform = new MatrixTransform();
transform.Matrix = new Matrix(-1, 0, 0, 1, 320, 0);

_newFrameBitmap.Render(_rect, transform);
_newFrameBitmap.Invalidate();
}
}

void ThreadProc()
{
while (true)
{
Thread.Sleep(20);

//Do the action in the UI thread
Dispatcher.BeginInvoke(ThreadUpdate);
}
}

void ThreadUpdate()
{
if (_isEnableCamera)
{
_oldFrameBitmap.Render(_newFrame, null);
_oldFrameBitmap.Invalidate();
}
}

private void imageEffectsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
BlendModeEffect effect = e.AddedItems[0] as BlendModeEffect;
if (effect != null)
{
if (_oldFrameBitmap != null)
{
ImageBrush _newImageBrush = new ImageBrush();
_newImageBrush.ImageSource = _newFrameBitmap;
ImageBrush _oldImageBrush = new ImageBrush();
_oldImageBrush.ImageSource = _oldFrameBitmap;

//effect.AInput = _oldImageBrush;
effect.BInput = _newImageBrush;
this._oldFrame.Effect = effect;
}
}
}
}

===================================

当运行刚启动,会出现一张纯黑色的矩形。但接着就会看到鬼一样移动的轮廓。这个轮廓就是两帧画面的不同之处。

接下来再使用threshold滤镜来对图片进行一下处理,对移动区域做更加精细的捕捉。

参考这里:http://kodierer.blogspot.com/2009/07/livin-on-edge- silverlight-parametric_4324.html

[转载]浅谈OSGi.NET开放服务平台和Discuz插件系统

mikel阅读(1015)

[转载]浅谈OSGi.NET开放服务平台和Discuz插件系统 – 道法自然 – 博客园.

我们团队从2008年5月份开始设计一个App Store,目前已经实现了产品的原型,现在产品已经进入了完善阶段。该产品最开始是基于我在2005年开发的一个Common Form Framework,此后结合了CAB & SCSF设计了Common UI Platform,从2008年5月份开始正式确定了产品的RoadMap,当然中间还是有不少变更了。它类似Google App Engine和Sina App Engine,只不过市场策略和模式是完全不同的。对于构建一个企业级的App Store,我个人认为.NET平台自身的特性在这领域确实不如Java,不过,好在也不是有什么是不可解决的。在产品设计过程中,我时刻关注业内的各种 App Engine、App Store和Plugin Framework,包括Equinox、SharpDevelop、Egeye、Mono.Addin、MAF、MEF、SCSF、 Google/Sina App Engine、Discuz等。每出现一种类似的产品,我都有一种心惊肉跳的感觉,担心别人抢在我之前设计了更好的同质产品,当然,也好在目前还没有看到 同质的产品。

今天我先介绍一下该Store的内核OSGi.NET,同时也谈一下我见过比较简陋的插件系统Discuz。

OSGi 是一个开放服务规范,“开放”意味着基于该平台可以使得很多人来共同使用和协作,而“服务”则是实现协作的一个手段。该规范可以总结为:A 插件化支持规范; B 面向服务支持规范; C 插件扩展规范; D 安全性与隔离性规范; E 系统服务规范。插件化规范完整详细定义了插件的结构、插件依赖、插件类加载、L10N和I18N、宿主插件和片段插件;面向服务支持规范定义了模块间服务 协作的支持,这个服务并不是传统意义的企业级Web Service,确切的讲,仅是“接口+实现”,并对实现的引用进行管理;插件扩展规范定义了一个插件如何对另一个插件进行扩展,这种扩展手段非常简单, 在这里提出了扩展点概念;安全性与隔离性则要确保被内核加载的插件不会对内核和其它插件产生一些副作用,比如我们决不能允许一个非法的插件来停止另一个插 件;系统服务规范则预定义了几个系统服务。OSGi规范是基于Java编写的,此前还没有一个针对.NET平台OSGi规范,原因在于.NET并不支持 ava那样优雅的类加载机制,不过还在我们目前都找到了绕过这些固有缺陷的方法并设计了针对.NET的规范。

我们团 队在设计OSGi.NET时候,把易用性放在首位,通过场景驱动来设计Usecase。从而,基于OSGi.NET开发一个插件和开发一个.NET项目方 式基本一样(当然,我们并不认为目前已经做到最好了,易用性肯定还有很大的改进空间)。以下是一个Hello World的插件,开发人员应该可以在5分钟内开发一个Hello World插件的。

1 在OSGi.NET的Plugins目录下,创建一个Class Library Project,并创建一个MyActivator类。

1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using UIShell.OSGi;
5
6 namespace UIShell.TestBundle
7 {
8 public class MyActivator : IBundleActivator
9 {

10 public void Start(IBundleContext context)
11 {
12 Console.WriteLine(Hello World! Plugin is started.);
13 }
14 public void Stop(IBundleContext context)
15 {
16 Console.WriteLine(Plugin is stopped.);
17 }
18 }
19}
20

2 定义一个Manifest.xml文件

1 <?xml version=”1.0″ encoding=”utf-8″ ?>
2 <Bundle Name=”TestBundle”
3 SymbolicName=”UIShell.TestBundle”
4 InitializedState=”Started”>
5 <Activator Type=”UIShell.TestBundle.MyActivator” />
6 <Runtime>
7 <Assembly Path=”bin/Debug/UIShell.TestBundle.dll” Share=”false” />
8 </Runtime>
9 </Bundle>
10

通过Console运行OSGi.NET后,这个插件便会被内核加载启 动,然后Print出Hello World。

基于OSGi.NET插件开发,一般只需做的事情有:(1)定义插件要实现 的功能;(2)判断插件是否需要引用其它插件的功能,如果有,则可以通过A)在Runtime声明一个Dependeny节点;B)在 MyActivator.Start方法中使用context.GetService方法获取依赖的服务;(3)通过定义Extension来扩展其它插 件功能,通过ExtensionPoint暴露扩展点。

对于Discuz的关注是始于我们在设计基于ASP.NET的 Store的。在设计这个Store的时候,我想比较一下基于OSGi.NET的ASP.NET插件平台和其它插件平台。Discuz插件系统是我见过设 计的最为简单也是最为简陋的插件系统了。我只看了一下反编译的结果,就没有深入研究它的欲望了。以下是通过反编译看到的插件定义。

它的插件系统是基于接口和实现类来设计了。这 种简陋的设计从严格意义上来讲不是一个插件平台,对开放性的支持也就更弱了,注定不可能有太多的人参与到这个产品插件的设计与扩展了。此外,每次内核的升 级都可能会导致原有的插件无法正常使用。不过,这也可能是因为Discuz在产品设计阶段并没有提出开放性目标。

Creative  Commons License

本文基于Creative Commons Attribution 2.5 China Mainland License发布,欢迎转载,演绎或用于商业目的,但是必 须保留本文的署名道法自然(包含链接)。如您有任何疑问或者授权方面的协商,请给我留言

[转载]什么是序列化和反序列

mikel阅读(1070)

[转载]什么是序列化和反序列 – YangLei’s – 博客园.

net的运行时环境用来支持用户定义类型的流化的机制。它是将对象实例的状态存储到存储媒体的过程。在此过程中,先将对象的公共字段和私有字段以及 类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本。

序列化的目的:
1、以某种存储形式使自定义对象持久化;
2、将对象从一个地方传递到另一个地方。

实质上序列化机制是将类的值转化为一个一般的(即连续的)字节流,然后就可以将该流写到磁盘文件或任何其他流化目标上。而要想实际的写出这个流,就要使用 那些实现了IFormatter接口的类里的Serialize和Deserialize方法。
在.net框架里提供了这样两个类:
一、BinaryFormatter

BinaryFormatter使用二进制格式化程序进行序列化。您只需创建一个要使用的流和格式化程序的实例,然后调用格式化程序的   Serialize   方法。流和要序列化的对象实例作为参数提供给此调用。类中的所有成员变量(甚至标记为   private   的变量)都将被序列化。

首先我们创建一个类:
[Serializable]
public   class   MyObject   {
public   int   n1   =   0;
public   int   n2   =   0;
public   String   str   =   null;
}
Serializable属性用来明确表示该类可以被序列化。同样的,我们可以用NonSerializable属性用来明确表示类不能被序列化。
接着我们创建一个该类的实例,然后序列化,并存到文件里持久:
MyObject   obj   =   new   MyObject();
obj.n1   =   1;
obj.n2   =   24;
obj.str   =   “一些字符串”;
IFormatter   formatter   =   new   BinaryFormatter();
Stream   stream   =   new   FileStream(“MyFile.bin”,   FileMode.Create,
FileAccess.Write,   FileShare.None);
formatter.Serialize(stream,   obj);
stream.Close();

而将对象还原到它以前的状态也非常容易。首先,创建格式化程序和流以进行读取,然后让格式化程序对对象进行反序列化。
IFormatter   formatter   =   new   BinaryFormatter();
Stream   stream   =   new   FileStream(“MyFile.bin”,   FileMode.Open,
FileAccess.Read,   FileShare.Read);
MyObject   obj   =   (MyObject)   formatter.Deserialize(fromStream);
stream.Close();

//   下面是证明
Console.WriteLine(“n1:   {0}”,   obj.n1);
Console.WriteLine(“n2:   {0}”,   obj.n2);
Console.WriteLine(“str:   {0}”,   obj.str);

二、SoapFormatter

前面我们用BinaryFormatter以二进制格式来序列化。很容易的我们就能把前面的例子改为用SoapFormatter的,这样将以xml格式 化,因此能有更好的可移植性。所要做的更改只是将以上代码中的格式化程序换成   SoapFormatter,而   Serialize   和   Deserialize   调用不变。对于上面使用的示例,该格式化程序将生成以下结果。

<SOAP-ENV:Envelope
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”
xmlns:SOAP-   ENC=http://schemas.xmlsoap.org/soap/encoding/
xmlns:SOAP-   ENV=http://schemas.xmlsoap.org/soap/envelope/
SOAP-ENV:encodingStyle=
“http://schemas.microsoft.com/soap/encoding/clr/1.0
http://schemas.xmlsoap.org/soap/encoding/”
xmlns:a1=”http://schemas.microsoft.com/clr/assem/ToFile”>

<SOAP-ENV:Body>
<a1:MyObject  >
<n1>1</n1>
<n2>24</n2>
<str  >一些字符串</str>
</a1:MyObject>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

在这里需要注意的是,无法继承   Serializable   属性。如果从   MyObject   派生出一个新的类,则这个新的类也必须使用该属性进行标记,否则将无法序列化。例如,如果试图序列化以下类实例,将会显示一个   SerializationException,说明   MyStuff   类型未标记为可序列化。

public   class   MyStuff   :   MyObject
{
public   int   n3;
}

然而关于格式化器,还有个问题,假设我们只需要xml,但不需要soap特有的额外信息,那么该怎么做?有两个方案:1、编写一个实现 IFormatter接口的类,采用的方式类似于SoapFormatter,但是可以没有你不需要的信息;2、使用框架提供的类 XmlSerializer。
XmlSerializer类和前两个主流的序列化类的几个不同点是:
1、不需要Serializable属性,Serializable和NonSerializable属性将会被忽略,但是使用XmlIgnore属性, 和NonSerializable属性类似。
2、该类不能安全地访问私有变成员,所以学要将私有成员改为公共成员,或者提供合适的公共特性。
3、要求被序列化的类要有一个默认的构造器。

我们改一下前面的MyObject类为:

public   class   MyObject   {
public   int   n1;
public   String   str;
public   MyObject(){}
public   MyObject(n1,str)
{
this.n1=n1;
this.str=str;
}
public   override   string   ToString()
{
return   String.Format(“{0}:{1}”,this.str,this.n1);
}
}
现 在我们用XmlSerializer类来对修改后的MyObject进行序列化。因为XmlSerializer类的构造器里有个Type参数,所以 XmlSerializer对象被明确的   连到该Type参数所表示的类了。XmlSerializer类也有Serialize和Deserialize方法:
MyObject   obj   =   new   MyObject(12,”some   string…”);
XmlSerializer   formatter   =   new   XmlSerializer(typeof(MyObject));
Stream   stream   =   new   FileStream(“MyFile.xml”,   FileMode.Create,
FileAccess.Write,   FileShare.None);
formatter.Serialize(stream,   obj);
//下面是反序列化
stream.Seek(0,SeekOrigin.Begin)
MyObject   obj_out=(MyObject)formatter.Deserialize(stream)
stream.Close();
Console.WriteLine(obj_out);

这个简单的列子可以加以扩展,以便利用更多的XmlSerializer功能,包括使用属性控制xml标记、使用xml模式和进行soap编码。

自定义序列化

如果你希望让用户对类实现序列化,但是对数据流的组织方式不完全满意,那么可以通过在对象上实现   ISerializable   接口来自定义序列化过程。这一功能在反序列化后成员变量的值失效时尤其有用,但是需要为变量提供值以重建对象的完整状态。除了必须将类申明为   Serializable   的同时,还要要实现   ISerializable接口,需要实现   GetObjectData   方法以及一个特殊的构造函数,在反序列化对象时要用到此构造函数。在实现   GetObjectData   方法时,最常调用的SerializationInfo的方法是AddValue,这个方法具有针对所有标准类型(int、char等等)的重载版本;而   StreamingContext   参数描述给定的序列化流的源和目标,这样我们就可以知道我们是将对象序列化到持久性存储还是在将他们跨进程或机器序列化。而在反序列化时,我们调用 SerializationInfo提供的一组Getxxx方法,他们针对所有标准类型数据执行各种AddValue重载版本的逆操作。下代码示例说明了 如何在前一部分中提到的   MyObject   类上实现   ISerializable。

[Serializable]
public   class   MyObject   :   ISerializable
{
public   int   n1;
public   int   n2;
public   String   str;

public   MyObject()
{
}

protected   MyObject(SerializationInfo   info,   StreamingContext   context)
{
n1   =   info.GetInt32(“i”);
n2   =   info.GetInt32(“j”);
str   =   info.GetString(“k”);
}

public   virtual   void   GetObjectData(SerializationInfo   info,
StreamingContext   context)
{
info.AddValue(“i”,   n1);
info.AddValue(“j”,   n2);
info.AddValue(“k”,   str);
}
}
在序列化过程中调用   GetObjectData   时,需要填充方法调用中提供的   SerializationInfo   对象。只需按名称/值对的形式添加将要序列化的变量。其名称可以是任何文本。只要已序列化的数据足以在反序列化过程中还原对象,便可以自由选择添加至   SerializationInfo   的成员变量。如果基对象实现了   ISerializable,则派生类应调用其基对象的   GetObjectData   方法。

需要强调的是,将   ISerializable   添加至某个类时,需要同时实现   GetObjectData   以及特殊的具有特定原型的构造函数--重要的是,该构造函数的参数列表必须与GetObjectData相同,这个构造函数将会在反序列化的过程中使用: 格式化器从流中反序列化数据,然后通过这个构造函数对对象进行实列化。如果缺少   GetObjectData,编译器将发出警告。但是,由于无法强制实现构造函数,所以,缺少构造函数时不会发出警告。如果在没有构造函数的情况下尝试反 序列化某个类,将会出现异常。在消除潜在安全性和版本控制问题等方面,当前设计优于   SetObjectData   方法。例如,如果将   SetObjectData   方法定义为某个接口的一部分,则此方法必须是公共方法,这使得用户不得不编写代码来防止多次调用   SetObjectData   方法。可以想象,如果某个对象正在执行某些操作,而某个恶意应用程序却调用此对象的   SetObjectData   方法,将会引起一些潜在的麻烦。

在反序列化过程中,使用出于此目的而提供的构造函数将   SerializationInfo   传递给类。对象反序列化时,对构造函数的任何可见性约束都将被忽略,因此,可以将类标记为   public、protected、internal   或   private。一个不错的办法是,在类未封装的情况下,将构造函数标记为   protect。如果类已封装,则应标记为   private。要还原对象的状态,只需使用序列化时采用的名称,从   SerializationInfo   中检索变量的值。如果基类实现了   ISerializable,则应调用基类的构造函数,以使基础对象可以还原其变量。

[转载]web地图信息格网索引-----基于google map api和oracle查询的生活地图

mikel阅读(1263)

[转载]web地图信息格网索引—–基于google map api和oracle查询的生活地图 – 梦开始的地方 – 博客园.

当前项目中的优惠促销信息需要和地图位置相关起来,所以就用到了google map。根据需求,要做一个在地图上显示郑州地区所有优惠促销信息的页面。首先对这个需求进行了分析,郑 州的优惠促销信息是非常多的,如何在地图上将所有的信息显示出来?如果全部显示出来,将会出现信息的压盖和信息将地图压盖起来,用户看不到地图,也看不清 楚信息。所以就需要一定的处理。

网上通行的做法就是对地图上显示的信息进行分页展示,这样在页面上显示的信息就不会很多,用可以很 清晰的看到地图和地图上所展示的信息。如google map

其它的都是同样的方式,只是显示的样式有所不同。

Google map还提供了另一种方式,

将所有的信息都呈现在地图上,google map所呈现的所有信息点是绘制在图片上的,也就是说,google map的开发人员利用api有开发和制作了一个图层,和影像、纹理是同一级别的图片索引集合,数据传送到本地浏览器,这样数据不 用做显示,提高了浏览器的速度,也完成了信息的查询。如果利用google map提供的marker来做,显然数据量是非常巨大的,浏览器无法 承受如此多的marker对象的绘制。所以google map才用了静态图片来实现。

我们也想在地图上显示所有的数据,但是我们没有google那么强大的实力,说以就仿照了google 高考2009地图:

将信息聚合显示在地图之上,也就是说在某一位置,告诉用户这个地方有多少优惠促销信息,你可以放大或 点击进入进行查看。

目标有了之后就开始研究如何实现。我的测试数据是100万条优惠促销信息。

初始实现方法:根据查询,将所有符合条件的数据读取出来,通过ajax发送到前台,然后在前台进行相交压盖判断。

试验结果是一次读取的数据量太大,消耗太多的时间,另一个就是数据读取完成之后发送到前台占用大量的带宽。再有就是前台浏览器运算压盖消耗太多 的时间。

经过试验,不可行,需要进行改进,不过流程上没有太大的问题。所以感觉的思路如下:

1、 减少数据库中读取出来的数据进行。

2、 在前台建立格网索引,加快计算压盖速度。

经过分析,如果减少数 据库读取出来的数据,那就没有在地图上显示或提示出网站所有的优惠促销信息,在前台建立格网索引,依然需要将大量的数据传送到前台,那为什么不在后台建立 格网索引呢。分析过后,将格网索引放在后台进行处理。经过改进,是比第一次有所提高,但还不够。

经过一系列的试验分析思考之后, 得出了解决方案。

将格网索引放置在数据库中,一个查询就可聚合分组查询出所以优惠促销。减少数据的读取和网络间的传 送,简化前台开发(oracle 有支持地理实体的组件,Oracle Spatial,但是对于这个需求并不实用)

具体方法如下:

1、 google map区域的建立格网

因为google map的缩放分级,所以,建立格网时需要根据不同的级别进行创建。当前所做的指真对郑州地图,所以格网重12级到19级。如 图:

12级为16*16的 格网

13级为32*32的 格网

….

格网的递增为四叉树。因为从12级开始就需要显示丰富的信息,说以第一级不是一个2*2的网 格,而是一个15*15的网格。

格网个2维的数组,将其转换为1维数组,即12级格网号顺序从1开始到256结束。 第一排从116,第二排从17-32以 次类推,最终完成格网的模型创建。

下边开始对数据库进行设计。

创建数据表。

字段

类型描述

信息id

关联的信息id

Lev12

整型格网号

Lev13

整型格网号

Lev14

整型格网号

Lev15

整型格网号

Lev16

整型格网号

Lev17

整型格网号

Lev18

整型格网号

Lev19

整型格网号

通过计算,将二维的点状信息转换为相应级别下的格网好

insert into shopindex (shopid) values (sid);

更新索引

update shopindex s

set (lev12, lev13, lev14, lev15, lev16, lev17, lev18, lev19) = (select GetGirdNo(px,

py,

1212) lev12,

GetGirdNo(px,

py,

1312) lev13,

GetGirdNo(px,

py,

1412) lev14,

GetGirdNo(px,

py,

1512) lev15,

GetGirdNo(px,

py,

1612) lev16,

GetGirdNo(px,

py,

1712) lev17,

GetGirdNo(px,

py,

1812) lev18,

GetGirdNo(px,

py,

1912) lev19

from ooshop f

where s.shopid = f.id)

where s.shopid = sid;

根据xylev获取格网号 函数

create or replace function GETGIRDNO(x in integer,

y in integer,

lev in integer)

return integer is

girdnum INT := 15;

width INT := 110866;

height INT := 77900;

gnum INT;

stpwidth int;

stpheight int;

xmin int := 40869800;

ymin int := 12467548;

col int;

row int;

res int;

BEGIN

gnum := girdnum * POWER(2, lev);

stpwidth := width / gnum;

stpheight := height / gnum;

col := (x – xmin) / stpwidth;

row := (y – ymin) / stpheight;

res := col + row * gnum;

if res < 0 or res >girdnum*girdnum * POWER(4, lev) then

res := 0;

end if;

return res;

END GetGirdNo;

信息如图

有了这个索引表之后,就可以用oracle的分组查询,一个查询将给定范围的数据进行分组查询出来,分组的依据就个格网,这样查询出的数据就是 在这个格网下有多少条优惠促销信息。

获取12级的分组数据

select lev12,count(shopid) from shopindex t group by lev12


真正的查询要比这复杂点,要根据给定的地图范围进行查询,这样就需要一个函数,即根据地图范围获取这个范围下的所有格网。函数如下:

create or replace function GETGIRDNOS(stcol in integer,

strow in integer,

encol in integer,

enrow in integer,

lev   in integer)

RETURN colrows IS

gnum    int;

i       int;

j       int;

girdnum INT := 15;

colrow  colrows := colrows();

BEGIN

i := strow;


gnum := girdnum * POWER(2, lev);

while (i <= enrow) loop

j := stcol;

while (j <= encol) loop

colrow.EXTEND;

colrow(colrow.COUNT) := j + i * gnum;

j := j + 1;

end loop;

i := i + 1;

end loop;

RETURN colrow;

END GetGirdNos;

数 据的读取

var result = new Collection<ooBFeature>();

while (reader.Read())

{

ooBFeature gm = new ooBFeature(1);

gm.I = reader.GetInt32(0);

gm.N = reader.GetValue(1).ToString();

int row = (int)(gm.I / grid.GNum);

int col = (int)(gm.I % grid.GNum);

gm.X = col * grid.StpWidth + grid.StpWidth / 2 + ooGridModel.x;

gm.Y = row * grid.StpHeight + grid.StpHeight / 2 + ooGridModel.y;

result.Add(gm);

}

return result;

根据读取出的格网计算出地 图上的坐标。以上就是用oracle建立地理信息索引的方法,如有问题, 可进行交流。

效果页面http://www.map85.com/map

[转载]群发邮件2

mikel阅读(1035)

[转载]群发邮件2 – pcajax – 博客园.

群发邮件,对于网站和一些推广应用,可谓不可或缺的“技能”

对于.NET而言,从2.0开始,发邮件已经是一件非常easy 的事了。下面我给出一个用C#群发邮件的实例,做了比较详细的注解,希望对有需要的朋友有所help。看了这篇BLOG,// 引入命名空间

using System.Net;
using System.Net.Mail;

SmtpClient smtp = new SmtpClient(); //实例化一个SmtpClient
smtp.DeliveryMethod = SmtpDeliveryMethod.Network; //将smtp的出站方式设为 Network
smtp.EnableSsl = false;//smtp服务器是否启用SSL加密
smtp.Host = "smtp.163.com"; //指定 smtp 服务器地址
smtp.Port = 25;             //指定 smtp 服务器的端口,默认是25,如果采用默认端口,可省去
//如果你的SMTP服务器不需要身份认证,则使用下面的方式,不过,目前基本没有不需要认证的了
smtp.UseDefaultCredentials = true;
//如果需要认证,则用下面的方式
smtp.Credentials = new NetworkCredential("邮箱帐号@163.com", "邮箱密码");
MailMessage mm = new MailMessage(); //实例化一个邮件类
mm.Priority = MailPriority.High; //邮件的优先级,分为 Low, Normal, High,通常用 Normal即可
mm.From = new MailAddress("邮箱帐号@163.com", "真有意思", Encoding.GetEncoding(936));
//收件方看到的邮件来源;
//第一个参数是发信人邮件地址
//第二参数是发信人显示的名称
//第三个参数是 第二个参数所使用的编码,如果指定不正确,则对方收到后显示乱码
//936是简体中文的codepage值

注:上面的邮件来源, 一定要和你登录邮箱的帐号一致,否则会认证失败

mm.ReplyTo = new MailAddress("test_box@gmail.com", "我的接收邮箱", Encoding.GetEncoding(936));
//ReplyTo 表示对方回复邮件时默认的接收地址,即:你用一个邮箱发信,但却用另一个来收信
//上面后两个参数的意义, 同 From 的意义
mm.CC.Add("a@163.com,b@163.com,c@163.com");
//邮件的抄送者,支持群发,多个邮件地址之间用 半角逗号 分开
//当然也可以用全地址,如下:
mm.CC.Add(new MailAddress("a@163.com", "抄送者A", Encoding.GetEncoding(936)));
mm.CC.Add(new MailAddress("b@163.com", "抄送者B", Encoding.GetEncoding(936)));
mm.CC.Add(new MailAddress("c@163.com", "抄送者C", Encoding.GetEncoding(936)));
mm.Bcc.Add("d@163.com,e@163.com");
//邮件的密送者,支持群发,多个邮件地址之间用 半角逗号 分开
//当然也可以用全地址,如下:
mm.CC.Add(new MailAddress("d@163.com", "密送者D", Encoding.GetEncoding(936)));
mm.CC.Add(new MailAddress("e@163.com", "密送者E", Encoding.GetEncoding(936)));
mm.Sender = new MailAddress("xxx@xxx.com", "邮件发送者", Encoding.GetEncoding(936));
//可以任意设置,此信息包含在邮件头中,但并不会验证有效性,也不会显示给收件人
//说实话,我不知道有啥实际作用,大家可不理会,也可不写此项
mm.To.Add("g@163.com,h@163.com");
//邮件的接收者,支持群发,多个地址之间用 半角逗号 分开
//当然也可以用全地址添加
mm.To.Add(new MailAddress("g@163.com", "接收者g", Encoding.GetEncoding(936)));
mm.To.Add(new MailAddress("h@163.com", "接收者h", Encoding.GetEncoding(936)));
mm.Subject = "这是邮件标题"; //邮件标题
mm.SubjectEncoding = Encoding.GetEncoding(936);
// 这里非常重要,如果你的邮件标题包含中文,这里一定要指定,否则对方收到的极有可能是乱码。
// 936是简体中文的pagecode,如果是英文标题,这句可以忽略不用
mm.IsBodyHtml = true; //邮件正文是否是HTML格式
mm.BodyEncoding = Encoding.GetEncoding(936);
//邮件正文的编码, 设置不正确, 接收者会收到乱码
mm.Body = "<font color="red">邮件测试,呵呵</font>";
//邮件正文
mm.Attachments.Add( new Attachment( @"d:a.doc", System.Net.Mime.MediaTypeNames.Application.Rtf ) );
//添加附件,第二个参数,表示附件的文件类型,可以不用指定
//可以添加多个附件
mm.Attachments.Add( new Attachment( @"d:b.doc") );
smtp.Send( mm ); //发送邮件,如果不返回异常, 则大功告成了。

OH, 卖糕的,终于写完了