本文原文来自:http://skywalkersoftwaredevelopment.net/blog/a-closer-look-at-content-types-drivers-shapes-and-placement

在本文中,我们将看看那些引让我夜不能寐的概念,因为我的生活不能找出与:shapes, content types, parts, fields, drivers and placement 等类型对应的东西。如果你有些使用Orchard的经验,但仍然觉得有点笨拙的控制shapes,那么这篇文章是为你而准备的。

首先让我们看看,当用户的请求(request)到来时,Orchard是如何呈现Content的,drivers和placement是如何负责绘制页面,shapes的Content(值)是如何转化为HTML并发送给用户的。

本文是为那些模块和主题开发者,并对drivers、content、shapes、placement有一定使用经验人,希望更确切的领会到这一切是如何工作的人准备的。
如果准备好了,那我们将从shapes说起。

一、什么是 shape

那么什么是shape,是我们通常谈论的物体的形状?例如,地球的形状好似椭球,而埃及的金字塔是一个锥体结构多面体形装。shape这个词的意思是什么,Orchard的“shape”吗?,嗯,shape是一个实例类或静态类的动态对象。我们可以把各种各样的信息附加到运行时的shape,甚至可以添加一个列表。本质上我们可塑造我们想要的东西。我认为“shape”是:对象的动态形状能力(注:shape的本意是形状,但中文形状这一词并不能完全表达shape的意思,shape不仅包括外观,还有要演染的数据以及演染的方法等,比如:WCF中通道形状(Channel Shape),就是这个意思,如果Orchard的shape最终还能从视觉上感知到,那么Channel Shape只是一种概念,是实现具体的消息交换模式)。shape类看起就是:

1: public class Shape : Composite, IShape, IEnumerable<object> {
 2:    public ShapeMetadata Metadata { get; set; }
 3:    public Id { get; set; }
 4:    public IList<string> Classes { get; }
 5:    public IDictionary<string, string> Attributes { get; }
 6:    public IEnumerable<dynamic> Items { get; }
 7:  
 8:    public virtual Shape Add(object item, string position = null) {}
 9:    public virtual Shape AddRange(IEnumerable<object> items, string position = "5") {}
 10:    IEnumerator<object> IEnumerable<object>.GetEnumerator() {}
 11:    public virtual IEnumerator GetEnumerator() {}
 12:    public override bool TryConvert(ConvertBinder binder, out object result) {}
 13:    public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) {}
 14:    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) {}
 15: }

它的魔力就是使我们能通过Shape实现 IEnumerable<object>(可以遍历子Shape),经由TryInvokeMember方法,调用用一个内联的键值对字典(由Shape的基类实现称为Composite的类),当你调用TryInvokeMember,就能获取一个动态实现的Shape,代码可写成:

1: @{
 2:    var mySampleShape = New.MySampleShape();
 3:    mySampleShape.Color = "Green";
 4: }

第2行,通过Shape工厂类创建了一个新的Shape,这个新的属性存储在View上,新的属性是一个dynamic类型
第3行,为刚创建的shape,设置了一个Color的属性,当我们执行代码的时候,通过TryInvokeMember来查询字典一个叫“Color”的对应的值。

这相当智能。

下面的文章有一些有趣的关于shape的额外信息: Using shapes as Html helpers in Orchard.http://www.szmyd.com.pl/blog/using-shapes-as-html-helpers-in-orchard

现在我们知道了更多关于shape知识,接下来让我们来谈谈他们用于什么。答案很简单:当然是渲染的目的。但这到底意味着什么呢?

二、渲染shape —— 命名语法

当我们谈论呈现一个shape,我们真正的意思是:使用shape对象模型来呈现一个Razor视图,。渲染操作的结果是一个字符串,该字符串发送到HTTP响应输出,就象Razor视图呈现Razor视图引擎那样做的。但Orchard是如何知道Razor怎样对待Shape时渲染呢?,我们可以控制哪些可用的Razor视图?是的,答案在于shape的Metadata(元数据)属性(注:一个ShapeMetadata类型的Metadata属性)。Metadata对象的字符串属性称为类型,并可能类似于“MySampleShape”。基于这个名字,Orchard将指示Razor视图引擎找到一个名为“MySampleShape.cshtml”的视图,使用Shape模型执行这一视图。Metadata属性还有一个称为 Alternates (替代)属性的IList<string>集合,我们将稍后讨论。

重要的是要知道,必须使用c#成员和变量命名规则命名shape的名称。这样做的原因是,我们可以创建shape,就好像是shape工厂方法名(正如我们之前看到的),而不需要确定的指定它的名字作为一个字符串值,当使用静态类型作为IShapeFactory的shape工厂(稍后详细介绍)。我们可以创建shape,好像我们是调用一个方法。例如,下面是有效的shape的名字:

MySampleShape
    My_Sample_Shape
    My__Sample_Shape

下面是非法的名称:

My.Sample-Shape
    My_Sample-Shape
    My-SampleShape

这些无效的原因是因为这些名字不会在c#编译。
  
然而,Orchard开发人员可能不喜欢强制使用这样的文件名(理应如此),所以他们想出了一个约定的Shape类型名称映射到Razor视图的文件名。
约定如下:

1、一个单一的下划线“_”被替换为一个点(.)。  
2、双下划线“__”被替换为一个连字符(-)。

所以如果你有一个Shape命名为My_Sample__ShapeRazor文件,应查找“My.Sample-Shape.cshtml”。  

三、Shapes从哪里来?

Shape实际上是从哪里来的呢?它是如何创建的?我们自己如何创建它们?这些问题的答案是:Shape是由一个叫做IShapeFactory接口实现类来创建。

正如我们已经看到的,在一个Razor视图中,你可以通过每个继承于Razor视图的Shape工厂访问新的属性。新属性是dynamic的静态类型,但它是一个实现了IShapeFactory类的实例。这新的属性类型是dynamic的原因是:让它更容易通过Shape工厂处理。例如,如果您有IShapeFactory静态类,下面代码演示的就是如何创建一个Shape:(注:以下代码应该放在一个 Razor文件。你可以选择你主题的Layout.cshtml文件,New工厂方法是 Orchard.Mvc.ViewEngines.Razor的WebViewPage类的一个方法);
 1: @{
 2:       
 3:     var shapeFactory = (IShapeFactory)New; //为了演示,创建一个新的 IShapeFactory
 4:  
 5:     var myShape = (dynamic)shapeFactory.Create("MySampleShape", Parameters.From({
 6:            MyProperty1 = "Some property value",
 7:           MyProperty2 = 42
 8:     }));
 9:  
 10: }

比较下面我们通过作为dynamic的shape工厂工作时:
 1: @{
 2:  
 3:     var shapeFactory = New;
 4:  
 5:     var myShape = shapeFactory.MySampleShape(
 6:       MyProperty1: "Some property value",
 7:        MyProperty2: 42
 8:     );
 9:  
 10: }

虽然的代码行数是相同,但语法更简单,看起来更好。还需要注意的是,我们能够就像shapeFactory上实现了“MySampleShape”方法一样创建一个Shape。当然不是这样(注:这里的意思是我们还没实现这样一个类,仍可以这样使用,但如果要让代码能正常运行,还是要实现的),但Shape工厂实现其动态行为拦截方法调用和基于被调用的方法名创建一个Shape,其参数和他们变成了Shape特性。非常整洁。

所以它是:shape来自IShapeFactory。只要你有一个IShapeFactory,您就可以创建shape。Razor视图通过访问shape工厂的New属性,并通过IShapeFactory注入您自己的代码。

四、渲染Shape —— Alternates(替代)

我们简要地提及shape alternates。它们是什么?技术上来说,他们只是一个字符串列表,存储在一个shape alternates属性中的元数据。当Orchard即将指示视图引擎查找视图呈现,它检查所有的alternates是否有一个视图与之匹配。Orchard适用相同的文件名映射shape名称语法,Razor视图文件名称语法如前所述。那么填充alternates的列表吗?答案是:shape对象可以添加alternates的任何代码。通常是使用所谓的Shape Table Providers来操作的。

五、Shape Table Providers

Shape Table Providers是实现了IShapeTableProvider接口的类,并将事件处理程序附加到Shape的一种方法。可能的Shape事件:

1、Creating
2、Created
3、Displaying
4、Displayed

通常做的是提供一个显示事件处理程序,并在这个处理程序可以添加alternates的Shape。让我们看看一个简单的例子。
首先,让我们想象我们创建一个有Color属性的Shape。我们的目的是:渲染Shape时,提供两个可以使用的Razor视图。使用的视图应该基于Shape的Color值。创建和渲染的Shape看起来像这样:

1: @{
 2:    var myBlueColoredShape = New.ColorShape(Color: "Blue");
 3:    var myRedColoredShape = New.ColorShape(Color: "Red");
 4: }
 5:  
 6: @Display(myBlueColoredShape)
 7: @Display(myRedColoredShape)

为了让代码工作,我们至少应该创建一个视图文件,名称和ColorShape相匹配的名称。让我们创建一个默认的视图,显示指定的颜色:

Views/ColorShape.cshtml:
 1: @{
 2:    var color = Model.Color;
 3: }
 4: <p>My color is: @color</p>

看看ColorShape.cshtml视图是如何访问到在Model中的Color属性,当我们通过Shape调用 @Display时,Orchard 的视图引擎会查找视图文件, 并通过Shape执行视图的模型。

现在,我们想使用一个基于Color的完全不同的视图文件。为此,我们在我们的模块中创建一个新的类文件如下:

1: public class ColorShapeTableProvider : IShapeTableProvider {
 2:     public void Discover(ShapeTableBuilder builder) {
 3:         builder.Describe("ColorShape")
 4:             .OnDisplaying(displaying => {
 5:                 var shape = displaying.Shape;
 6:                 var color = shape.Color;
 7:  
 8:                 displaying.ShapeMetadata.Alternates.Add(String.Format("ColorShape__{0}", color));
 9:             });
 10:     }
 11: }

我们做的第一件事是“Describe”(描述)shape(第3行),表示:“配置如下的shape”。
我们通过配置一个特别的当Orchard即将渲染我们的shape时会调用OnDisplaying方法的handler 。如果你对此表是疑惑,你不必担心覆盖其他事件处理程序,API将为shape注册多个配置项,并且工作得很好。

请注意,我们可以访问我们的shape和Color属性(第6行)。使用这个属性的值来生成另一种shape,并将其添加到的元数据Aternates集合中(第8行)。

与这种形状表提供者匹配,我们现在可以创建以下两个视图文件:

1、ColorShape-Blue.cshtml
2、ColorShape-Red.cshtml

有了这种处理方式,Orchard变得更好使用。例如:通过提供基于内容的Content类型名称和所使用的显示类型的alternates来渲染Content shapes。稍后将进行更详细的讨论。

我们已经看到如何创建一个使用shape工厂和使用继承于每一个视图Display渲染方法。我们继续之前我想表明,你现在可以在一行中创建和渲染这个自定义shape了。它看起来像这样:

1: @Display.ColorShape(Color: "Yellow")

这样只需一行去创建!

六、添加Shapes到Zone(区域)

这很酷!我们有了Shape是什么的更好的理解,以及它们是如何工作的。事实证明,这并不是说把事性搞复杂了(注:这里指的是上面需一行去创建Shape,而要增加的那些辅助类工作)。

让我们谈谈Zone。在Orchard里,当我们讨论Zone,我们讨论的是呈现在一些视图中(通常在Layouts.cshtml),可以固定shape的东西。那么这个东西到底是什么呢?

好亲爱的同学们(注:原文如此^o^),事实证明,Zone只是另一个shape,唯一的区别在于,它继承自称为ZoneHolding的类(注:定义在Orchard.UI.Zones),而ZoneHolding又是继承自shape。很快我们将会看到为什么这很重要。

我们通常谈论全局Zone和局部Zone。在root-of-all-shapes中义的区域定义的是全局Zone,是布局shape。Orchard为我们创造了这个shape,可以在每个视图中通过WorkContext属性访问。

如果您正在修改视图的视图布局shape,然后你就可以通过视图的Model属性直接访问这些Zone。再次重申,当你想要从其它views中的布局shape的zone增加shapes,您可以访问通过此布局shape的WorkContext对象。

Views/Layout.cshtml
 1: @{
 2:    // 创建一个新的 shape.
 3:    var mySampleShape = New.SampleShape();
 4:    // 把这个 shape加入到 AsideSecond zone
 5:    // 这是一个Layout shape视图
 6:    // 因此我们能通过视图的Model属性访问到Layout shape
 7:    // 而不是通过WorkContext.Layout访问Layout shape
 8:    // 因为在 Layout.cshtml 视图中 Model=WorkContext.Layout
 9:    Model.AsideSecond.Add(mySampleShape);
 10: }
 11:  
 12: @*在视图的某个地方, 呈现到AsideSecond zone*@
 13: @Display(Model.AsideSecond)

上面的代码将创建一个示例形状(确保你创建一个相应的视图文件时这一点),将其添加到一个区域称为AsideSecond,然后显示zone。结果将是,我们的示例形状会呈现在 AsideSecond zone。

七、ZoneHolding

因此这里有一个问题:我们可以取能想出任意区域的名字,还是我们必须用某种方式来定义他们?

实际上,布局shape是一种特殊类型的shape ,称为ZoneHolding shape 。ZoneHolding类来源于shape类,将创建一个zone shape动态布局的shape作为访问属性。

谢天谢地ZoneHolding shape类型,将为我们自动完成这一切。

但是等等——那么为什么我们必须名称在theme(主题)清单列表(theme.txt)文件创建一个zone ?

非常好的问题。之所以这样,原因是我们在主题列表清单指定的一个区域名称,实际上并不意味着我们有将它呈现在任意区域的能力,相反,这个列表是用于渲染Widgets的可用区部分。例如,让我们看看TheThemeMachine。
下面的区域名称定义的 Theme.txt 文件:
Header, Navigation, Featured, BeforeMain, AsideFirst, Messages, BeforeContent, Content, AfterContent, AsideSecond, AfterMain, TripelFirst, TripelSecond, TripelThird, FooterQuadFirst, FooterQuadSecond, FooterQuadThird, FooterQuadFourth, Footer

这些区域名称会导致他们出现在Widgets 配置页上:

当您创建一个Widgets并将它添加到一个区域,呈现Widgets的代码实际上是使用存储区域名称来添加Widgets形状区域。非常好。

八、呈现的Content(Rendering Content)

现在我们已经看到什么是shape,,以及我们如何使用和渲染,让我们真正深入研究如何呈现一个Content项。为了更好的理解如何呈现一个Content项,我们必须深入了解、drivers和content handlers是什么,Placement.info发挥什么作用。

九、深入了解的Content Item(Anatomy of a Content Item)

一个Content Item基本上由以下组成:

1、它有一个Content类型:Content类型本质上是一个Content项的蓝图( blueprint)。例如:Page content 类型,它有一个专业名称叫做“Page”和显示名称(也称为友好名称)称为“Page”。

2、它有一个Content Parts的集合
    如:
        1)、TitlePart
        2)、BodyPart

这些Content Parts定义Content Item的Content类型的数据和行为

因此,一个Page content项是一个拥有“Page”的content项,并拷贝了content类型的Content Parts。Content Parts的一个重要特点是,任何给定的Content类型只能有一个实例相同的Content Parts类型。例如,没有Content项可以有多个TitlePart。但是,Content Item可以既有TitlePar和BodyPart。

Parts自己可以拥有属性。例如,TitlePart有一个标题属性的类型字符串,而BodyPart文本属性的类型字符串。

除此之外,所有Parts集合属性称为Fields,这是一个Content Fields集合。与Content Item只能有一个某种类型的一部分相比,Content Parts可以有许多相同类型的fields的Content。几个content fields的例子:

Text Field
    Boolean Field
    Media Library Picker Field
    Taxonomy Field
    Date Time Field
    Content Picker Field
    Enumeration Field

用户可以使用控制面版(Orchard管理后端)给Content类型添加Content Parts和给Content Parts添加content fields。将属性添加到Content部分,开发人员必须创建一个与content part专业名称一至的c#类。

下面是一个简单的树形视图,它显示Content类型的层次结构,它的parts,每个parts的fields, 以及Content项的结构,它本质上是一样的Content类型:

正如你所看到的,一个Content类型可以有多个部分,每个部分可以有多个字段。

现在让我们使用代码创建一个新Page Content item,并使用view来按照我们的想法渲染它。我们需要有一个controller 和一个action ,看起来像下面一样:

Controller:

1:  public class ItemController: Controller {
 2:     private IContentManager _contentManager;
 3:   
 4:     public ItemController(IContentManager contentManager) {
 5:        _contentManager = contentManager;
 6:     }
 7:   
 8:     public ActionResult Display(int id) {
 9:        var contentItem = _contentManager.Create("Page", VersionOptions.Published);
 10:        var titlePart = contentItem.As<TitlePart>();
 11:        var bodyPart = contentItem.As<BodyPart>();
 12:  
 13:        titlePart.Title = "Hello Orchard";
 14:        bodyPart.Text = "<p>Welcome to my new page!</p>";
 15:             
 16:        return View(contentItem);
 17:    }
 18: }

View:
 1: @{
 2:    var contentItem = (ContentItem)Model; // 通过model来访问content item
 3:  
 4:    // 得到一个引用TitlePart和BodyPart,这样我们就可以正确渲染它们。
 5:    var titlePart = (TitlePart)contentItem.As<TitlePart>();
 6:    var bodyPart = contentItem.As<BodyPart>();
 7: }
 8:  
 9: <h1>@titlePart.Title</h1>
 10: @Html.Raw(bodyPart.Text)

第一个代码片段中我们所看到,创建一个新的Content item的Content类型“Page”和访问其TitlePart,BodyPart与一些初始化它们的值,最后返回一个Content item的 Model的view。

第二个代码片段显示了我们如何通过他们的属性值来访问两个part的Content项。

非常简单。然而,有一个警告:像这样的代码需要硬编码Content项。然而Orchard的真正力量是,您可以将可重用Content Part和Fields附加到任何Content类型,不必为这些Content类型创建特定的视图。如何做?答案是:让每个Content Part和Content Fields来决定自己如何渲染。

十、Content Part and Field Drivers

很早之前Orchard人就想出了一个API,可以作为输入Content项,并返回一个shape作为输出。shape可以渲染。这个API IContentManager被实现为一个方法称为BuildDisplay,及其签名如下:

1: /// <summary>
 2: /// Builds the display shape of the specified content item
 3: /// </summary>
 4: /// <param name="content">The content item to use</param>
 5: /// <param name="displayType">The display type (e.g. Summary, Detail) to use</param>
 6: /// <param name="groupId">Id of the display group (stored in the content item's metadata)</param>
 7: /// <returns>The display shape</returns>
 8: dynamic BuildDisplay(IContent content, string displayType = "", string groupId = "");

默认实现IContentManager(DefaultContentManager),本身并不做太多;相反,它代表shape的构建IContentDisplay的实例

在一个高级别,这是IContentDisplay的默认实现:

1、创建一个和Stereotype的shape一样的新的Content类型。默认情况下,这是一个“Content”,一个新的“Content”的shape 类型的将被创建。这个Shape的基类是ZoneHolding,因此能够自动创建zone shapes和Layout shape。

2、Shape的DisplayType参数设置为任何显示类型。这显示类型用于提供一个基于此显示类型的替代。如果没有显示指定,则使用“Detail”。

3、BuildDisplay方法调用的ContentHandler的实现。
 
4、有两种ContentHandler的实现,用来实现content part 的 driver 系统:一个是content part的ContentPartDriverCoordinator和一个是content fields的ContentFieldDriverCoordinator。这些处理程序的目的是遍历每个part和fields的Content项,找到他们的driver。对于每个找到的driver,BuildDisplayShape方法在被调用时,反过来调用其受保护的Display方法,其实现是由具体的driver来实现的。
 
5、这个具体的Display实现,最终返回DriverResult,DriverResult通常是ContentShapeResult的一个实例。ContentShapeResult持有一个渲染shape的名字,和所谓的会创建shape渲染shape工厂方法。文档演示了一个Content part driver的示例实现。

6、注意我说driver返回持有渲染shape的名字的content shape的值,以及将创建实际的形状和引用方法。我们不马上返回创建的shape的原因是,如果我们在某种程度上决定不渲染它,我们就不用创建shape。有一个叫做Placement的系统,将确定shape应该渲染在什么地方,或者根本就不渲染。不久我们将讨论这个,但是我想说,这是我们分别提供shape name和shape factory方法原因(因此shape是晚绑定的)。
 
7、因为每个driver的Display方法被调用时,调用代码将决定shape需要去的地方,如果它需要渲染。会文读取Placement.info的XML文件,它包含shape名称和位置信息。例如,如果一个driver返回一个名为“Parts_Title”的shape,placement.info文件有一个元素如<Place Parts_Title = "Header:1 " / >,将形状在一个zone称为“Header”位置,这个shape将在shale第1步创建它。
 
我们最终得到的是一个item的shape,俱有层次结构的 part和 field的shapes。Orchard所需要做的就是使这个shape,进而将渲染其子shape。正如你所看到的,这是非常强大的,每个part和field只有关心渲染自己,Placement.info用于外部控制这些shape应该显示在什么地方。这是特别强大的,并且你可以覆盖Placement.info主题,以满足不同的需求。

下面是一个流程图表示的代码执行路径ItemController的Content模块(Orchard.Core发现)的drive,和Controller,View和渲染的shape:

我不知道你是什么的感觉,但是我感到令人激动人心的,当我终于能够用我的head通过drivers,在请求Content项的管道(当要求使用它的路由,因为渲染挂件widgets从一个ActionFilter而不是一个控制器。但它是所有相同的流程),以及理解shape真的可以来自任何地方:从控制器controllers, drivers, action filters,甚至从视图上创建(也称为特殊shape)。

详细分析Orchard的Content、Drivers, Shapes and Placement 类型的更多相关文章

  1. Python爬虫爬取全书网小说,程序源码+程序详细分析

    Python爬虫爬取全书网小说教程 第一步:打开谷歌浏览器,搜索全书网,然后再点击你想下载的小说,进入图一页面后点击F12选择Network,如果没有内容按F5刷新一下 点击Network之后出现如下 ...

  2. PE节表详细分析

    目录 PE节表详细分析 0x00 前言 0x01 PE节表分析 节表结构 节表数量 节表名字 节表大小 节位置 节表属性 0x02 代码编写 PE节表详细分析 0x00 前言 上一篇文章我们学习了PE ...

  3. ZIP压缩算法详细分析及解压实例解释

    最近自己实现了一个ZIP压缩数据的解压程序,觉得有必要把ZIP压缩格式进行一下详细总结,数据压缩是一门通信原理和计算机科学都会涉及到的学科,在通信原理中,一般称为信源编码,在计算机科学里,一般称为数据 ...

  4. 1125MySQL Sending data导致查询很慢的问题详细分析

    -- 问题1 tablename使用主键索引反而比idx_ref_id慢的原因EXPLAIN SELECT SQL_NO_CACHE COUNT(id) FROM dbname.tbname FORC ...

  5. LinkedList详细分析

    一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据remove()7.数据获取get()8.数据复制clo ...

  6. android ListView 九大重要属性详细分析、

    android ListView 九大重要属性详细分析. 1.android ListView 一些重要属性详解,兄弟朋友可以参考一下. 首先是stackFromBottom属性,这只该属性之后你做好 ...

  7. C语言中的static 详细分析

    转自:http://blog.csdn.net/keyeagle/article/details/6708077/ google了近三页的关于C语言中static的内容,发现可用的信息很少,要么长篇大 ...

  8. Linux内核OOM机制的详细分析(转)

    Linux 内核 有个机制叫OOM killer(Out-Of-Memory killer),该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了 防止内存耗尽而内核会把该进程杀掉.典 ...

  9. Android-Native-Server 启动和注册详细分析

    Android-Native-Server 启动和注册详细分析     以mediaService为实例来讲解: mediaService的启动入口 是一个 传统的  main()函数 源码位置E:\ ...

随机推荐

  1. 一步一步理解 Java 企业级应用的可扩展性

    摘要:本文主要介绍如何理解 Java 应用的扩展方式以及不同类型的扩展技术和具体技巧,介绍一些有关 Java 企业级应用的一般扩展策略. 老实说,"可扩展性"是个全面且详尽的话题, ...

  2. 修改Delphi工具控件的默认字体

    修改Delphi工具控件的默认字体: 注册表: Delphi 6:    HKEY_CURRENT_USER\Software\Borland\Delphi\6.0Delphi 7:    HKEY_ ...

  3. android 上传图片到服务器Tomcat(Struts2)

    在做android开发的时候,有时你会用到图片的上传功能,在我的android项目中,我是选中图片,点击上传多张图片 android客户端上传图片部分的代码如下: package com.exampl ...

  4. c++ 名字粉碎(name mangling)

    转自Ibm: Name mangling is the encoding of function and variable names into unique names so that linker ...

  5. 标量子查询优化(用group by 代替distinct)

    标量子查询优化 当使用另外一个SELECT 语句来产生结果中的一列的值的时候,这个查询必须只能返回一行一列的值.这种类型的子查询被称为标量子查询 在某些情况下可以进行优化以减少标量子查询的重复执行,但 ...

  6. Redis教程01——命令

    APPEND key value追加一个值到key上 AUTH password验证服务器 BGREWRITEAOF 异步重写追加文件 BGSAVE 异步保存数据集到磁盘上 BITCOUNT key ...

  7. .\Obj\main.axf: Error: L6406E: No space in execution regions with .ANY selector matching sin_i.o(.co

    这个问题原因是 芯片的 空间不足 解决方法是  在KEIL 的DEVICE中选择 更大的空间的芯片型号

  8. 外显子分析:cutadapt,去除序列adapter详细解析

    外显子测序时带有adapt接头,因此我们需要去除adapt接头,cutadapt的作用是去除adapt接头,一般用到如下命令: cutadapt -a AACCGGTT -o output.fastq ...

  9. 更改Tomcat startup.bat启动窗口名称

    通常在Tomcat bin目录下用startup.bat启动Tomcat ,启动窗口显示的Title是Tomcat 如果遇到一个服务器上多个Tomcat的话就会容易混淆.更改方法如下: 1 在bin目 ...

  10. uva1393 Highways

    留坑(p.339) 已填(膜汪) 每条直线至少经过两个点,我们不妨在经过的所有点中的第二个点统计它 设f[i][j]表示i * j的答案,那么显然可以用f[i][j] = f[i - 1][j] + ...