这个东东是去年我看着ASP:标记突发奇想花4天时间设计编写的类库, 原名叫 HtmlGenerator, 最近发现PHP和JAVA有很多类似的项目, 但是都设计的很渣(不同意的表打我@_@), 于是把 HtmlGenerator 重构了一下, 改叫 CodeGenerator. 配合我的数据库迁移工具和数据库实体类生成品..... 好像跑题了 -____-
CodeGenerator 的特点:
1. 标记简结实用, 所有网页美工都能在一分钟内掌握. 而且不与HTML标准冲突, 模板页可用任何WYSIWYG工具编辑, 和编辑普通HTML网完全相同.
2. 标记只与表示层相关, 不包括任何业务逻辑, 丝毫不影响你应用多层结构.
3. 标记到后台被解析成了生成器对象, 完全面向对象, 不像绝大多数生成器要死嗑字符串.
4. 生成器对象使用DataSource属性取得数据, DataSource可以为 简单值类型(如 int, DateTIme), 也可以为简单数组(如 decimal[], string[]), 还可以为ADO.NET数据集(如DataTable), 甚至单个对象实体或对象集合或列表(如 SomeClassCollection, List<SomeClass>), 所有数据源类型通吃! 哈哈, 比ASP.NET带的数据控件支持的类型还多.
5. 标记的Name直接与数据源的列名ColumnName或属性名PropertyName, 好处不言而喻了吧.
6. 说到这里好了, 留一手先. 呵呵
演示程序下载地址: http://files.cnblogs.com/ericfine/Demo.rar
EFPlatform.CodeGenerator 源代码下载地址: http://files.cnblogs.com/ericfine/EFPlatform.CodeGenerator.rar (过时了)
EFPlatform.TemplateEngine 源代码下载地址: http://files.cnblogs.com/ericfine/EFPlatform.TemplateEngine.rar (其实就是CodeGenerator的优化版:))
应用项目:
http://portray.mz99.com/
http://music.mz99.com/
http://joke.mz99.com/
http://www.mcuol.com/
应用流程:

Default.aspx.cs:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using EFPlatform.TemplateEngine;

public partial class _Default : Page


{
private string outputPath;
private string categoryFileName;
private string productFileName;
private static DbProviderFactory dbFactory;
private DbConnection connection;

protected void Page_Load(object sender, EventArgs e)

{
outputPath = Server.MapPath("./");
categoryFileName = string.Format(@"{0}\Template\Category.html", outputPath);
productFileName = string.Format(@"{0}\Template\Product.html", outputPath);
string currentConnection = ConfigurationManager.AppSettings["Connection"];
ConnectionStringSettings css = ConfigurationManager.ConnectionStrings[currentConnection];
this.GetConnection(css);
}

private void GenerateCategory()

{
string template = Helper.ReadTextFile(categoryFileName);
Generator gen = new Generator(template);
gen.ParseTemplate();
Region rgnTitle = gen.GetRegion("Title");
Region rgnCategory = gen.GetRegion("Category");
Region rgnProducts = gen.GetRegion("Products");
Region rgnNavigator = gen.GetRegion("Navigator");

if(rgnTitle == null || rgnCategory == null || rgnProducts == null || rgnNavigator == null)

{
Response.Write("Missing region.");
return;
}

int categoryId;
string outputFileName;
DataView dvCategory = this.GetCategoryTable().DefaultView;
Pager pgrCategory = new Pager(1, dvCategory.Count);

for(int i = 0; i < pgrCategory.PageCount; i++)

{
rgnTitle.DataSource = (string)dvCategory[i]["CategoryName"]; //Use a string as data source
rgnCategory.DataSource = dvCategory[i]; //Use a DataRowView object as data source
pgrCategory.CurrentPage = i + 1;
rgnNavigator.DataSource = pgrCategory; //Use a Pager object as data source
categoryId = (int)dvCategory[i]["CategoryID"];
rgnProducts.DataSource = this.GetProductTable(categoryId); //Use a DataTable object as data souce
outputFileName = string.Format(@"{0}\Html\Category{1}.html", outputPath, categoryId);
Helper.WriteTextFile(outputFileName, gen.Generate());
}
}

private void GenerateProduct()

{
string template = Helper.ReadTextFile(productFileName);
Generator gen = new Generator(template);
gen.ParseTemplate();
Region rgnTitle = gen.GetRegion("Title");
Region rgnProduct = gen.GetRegion("Product");
Region rgnNavigator = gen.GetRegion("Navigator");

if(rgnTitle == null || rgnProduct == null || rgnNavigator == null)

{
Response.Write("Missing region.");
return;
}

string outputFileName;
List<Product> productList = this.GetProductList();
Pager pgrProduct = new Pager(1, productList.Count);

for(int i = 0; i < pgrProduct.PageCount; i++)

{
rgnTitle.DataSource = productList[i].CategoryName; //Use a string as data source
rgnProduct.DataSource = productList[i]; //Use a Product object as data source
pgrProduct.CurrentPage = i + 1;
rgnNavigator.DataSource = pgrProduct; //Use a Pager object as data source
outputFileName = string.Format(@"{0}\Html\Product{1}.html", outputPath, productList[i].ProductID);
Helper.WriteTextFile(outputFileName, gen.Generate());
}
}


DataSourcePreparing#region DataSourcePreparing
private void GetConnection(ConnectionStringSettings css)

{
if(dbFactory == null)

{
dbFactory = DbProviderFactories.GetFactory(css.ProviderName);
}

this.connection = dbFactory.CreateConnection();
this.connection.ConnectionString = css.ConnectionString;
}

private DataTable GetCategoryTable()

{
string commandText = "SELECT CategoryID, CategoryName, Description FROM Categories";
DbDataAdapter da = dbFactory.CreateDataAdapter();
da.SelectCommand = dbFactory.CreateCommand();
da.SelectCommand.Connection = this.connection;
da.SelectCommand.CommandText = commandText;
DataTable dt = new DataTable();
this.connection.Open();
da.Fill(dt);
this.connection.Close();
return dt;
}

private DataTable GetProductTable(int categoryId)

{
string commandText = string.Format("SELECT * FROM Products WHERE CategoryID = {0}", categoryId);
DbDataAdapter da = dbFactory.CreateDataAdapter();
da.SelectCommand = dbFactory.CreateCommand();
da.SelectCommand.Connection = this.connection;
da.SelectCommand.CommandText = commandText;
DataTable dt = new DataTable();
this.connection.Open();
da.Fill(dt);
this.connection.Close();
return dt;
}

private List<Product> GetProductList()

{
string commandText = "SELECT p.*, c.CategoryName, s.CompanyName FROM (Products AS p INNER JOIN Categories AS c ON p.CategoryID = c.CategoryID) INNER JOIN Suppliers AS s ON p.SupplierID = s.SupplierID ORDER BY p.ProductID";
DbCommand command = this.connection.CreateCommand();
command.CommandText = commandText;
List<Product> productList = new List<Product>();
Product product;
this.connection.Open();

using(DbDataReader dr = command.ExecuteReader())

{
while(dr.Read())

{
product = new Product();
Helper.FillModel(product, dr);
productList.Add(product);
}
}

this.connection.Close();
return productList;
}

private class Product

{
private int productID;

public int ProductID

{

get
{ return productID; }

set
{ productID = value; }
}

private string productName;

public string ProductName

{

get
{ return productName; }

set
{ productName = value; }
}

private string companyName;

public string CompanyName

{

get
{ return companyName; }

set
{ companyName = value; }
}

private int categoryID;

public int CategoryID

{

get
{ return categoryID; }

set
{ categoryID = value; }
}

private string categoryName;

public string CategoryName

{

get
{ return categoryName; }

set
{ categoryName = value; }
}

private string quantityPerUnit;

public string QuantityPerUnit

{

get
{ return quantityPerUnit; }

set
{ quantityPerUnit = value; }
}

private decimal unitPrice;

public decimal UnitPrice

{

get
{ return unitPrice; }

set
{ unitPrice = value; }
}

private int unitsInStock;

public int UnitsInStock

{

get
{ return unitsInStock; }

set
{ unitsInStock = value; }
}

private int unitsOnOrder;

public int UnitsOnOrder

{

get
{ return unitsOnOrder; }

set
{ unitsOnOrder = value; }
}

private int reorderLevel;

public int ReorderLevel

{

get
{ return reorderLevel; }

set
{ reorderLevel = value; }
}

}
#endregion

protected void Button1_Click(object sender, EventArgs e)

{
this.GenerateCategory();
}

protected void Button2_Click(object sender, EventArgs e)

{
this.GenerateProduct();
}
}

Web.config:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" />
<authentication mode="Windows" />
<customErrors mode="Off" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
</system.web>
<connectionStrings>
<add name="Access" providerName="System.Data.OleDb" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|Northwind.mdb"/>
<add name="SqlExpress" providerName="System.Data.SqlClient" connectionString="Data Source=.\SQLExpress;Integrated Security=True;User Instance=True;Database=Northwind;AttachDBFilename=|DataDirectory|Northwind.mdf"/>
</connectionStrings>
<appSettings>
<add key="Connection" value="Access"/>
</appSettings>
</configuration>
posted @ 2007-06-30 20:57
Eric Fine 阅读(8133)
评论(65) 编辑 收藏 网摘 所属分类:
Components & Controls
发表评论
见不到你的具体类的代码,不知道你是怎么实现替换的,如果是一个个地replace的话,性能不会太好。我最近也在做同一类型的东东,但感觉需要加强编译原理的学习才能做得更好,哈。不知道你有没有这方面的资料呢?
#3楼[
楼主]2007-06-30 21:57 |
汗.......再发上来就反映了....呵呵
我设计的这个类主要是标记精简,数据填充非常方便,模板可以让网页设计师直接设计,不需要开发人员二次加工.
#7楼[
楼主]2007-07-01 00:39 |
呵呵。不知是否 Replace?不过对于我这种程序员美工,暂且不用了。
@Eric Fine
是否提供default.aspx的下载,或者是整个的完整demo的下载啊
好回去研究下
建议大家一起搞个生成静态页面的建站系统出来。asp,php的都有了,就.net还落后,有意象的可以联系我
#15楼[
楼主]2007-07-01 15:22 |
@布尔
说话要负责任的哦 ^_^
#16楼[
楼主]2007-07-01 15:38 |
已经把DLL下载换成项目源代码下载了, 方便不会用Reflector的新丁们研究:)
推荐使用StringTemplate或TemplateEngine ,这两个都是开源的非常优秀的模板引擎,俺特别喜欢后者。
#21楼[
楼主]2007-07-02 12:08 |
@罗庆伟
模板页和结果页在演示地址里有啊
兄台,可否将你的 “数据库迁移工具和数据库实体类生成品” 也共享一下
把源码全部放出来吧?……哈哈
大家如果都用你的,来验证你所做的,再那么一反馈,自己能学到好多东西呢……
你生成的实例.不得到title中的内容.
就是绑定单个string的时间有点问题.
能不能在改进下啊.
因为工作需要,我也实现了一个,不过因为现在所做的代码都不能公布出来的,所以没办法和你一起探讨了.
#28楼[
楼主]2007-08-21 08:38 |
@ten
这个BUG在 优化版本 EFPlatform.TemplateEngine 里解决了
@Eric Fine
你好,非常高兴能看到您这篇文章,
现在正好急需用,希望您能给一个DEMO,看了你DEMO的演示,不是很懂,希望能给个完整的DEMO提供下载,或者email给我,谢谢
我的email: zrc000@gmail.com
Pager pgrCategory = new Pager(1, dvCategory.Count);
for(int i = 0; i < pgrCategory.PageCount; i++)
{
rgnTitle.DataSource = (string)dvCategory[i]["CategoryName"]; //Use a string as data source
rgnCategory.DataSource = dvCategory[i]; //Use a DataRowView object as data source
pgrCategory.CurrentPage = i + 1;
rgnNavigator.DataSource = pgrCategory; //Use a Pager object as data source
categoryId = (int)dvCategory[i]["CategoryID"];
rgnProducts.DataSource = this.GetProductTable(categoryId); //Use a DataTable object as data souce
outputFileName = string.Format(@"{0}\Html\Category{1}.html", outputPath, categoryId);
Helper.WriteTextFile(outputFileName, gen.Generate());
}
我想每个页面绑定20条数据,这边好象不能实现,无论怎样,都只能绑定一条数据而已,要怎么办?
用sql2005新特性 row_number()解决这个问题了。。谢谢
这几天我也正在研究这个,看看对我有没有帮助,感谢Eric Fine
看了一遍,但有疑问
比如我在制作模板时,要在首页某一位置调用指定的某一类型商品10件显示,这时怎么解决呢?
示例只是最简单的一种情况啊
如何像其它模板生成那样,在标记里就包含了要调用的类别名称,数量,这样就可以了,在这是如何解决这个问题的呢?
#34楼[
楼主]2007-09-13 09:07 |
@小小水晶
数据显示出几条, 是由你在后台绑定时提供的数据源决定的.
我的意思是这样:
比如像动网新网动易等支持生成静态页面的系统,都是通过预定义相当多的标记和属性,这样程序一旦发布以后,其它人想制作新的模板,就可以完全不需要涉及到程序改动
而你目前发布的这个,是获取模板中的标记,再使用程序在后台绑定数据源,这样数据就被限定死了
而上面提到的这些文章发布系统静态化时,模板制作人员可以在模板中包含要调用的类别,数量等这样的属性,然后设定系统使用这个模板就可以了
请问这样的功能EFPlatform.TemplateEngine支持吗?我没有发现啊,不然摆脱不了改动程序,就没办法通用了
谢谢
代码有问题!!
在GenerateProduct()方法中, 以下语句从
outputFileName = string.Format(@"{0}\Html\Product{1}.html", outputPath, productList[i].ProductID);
从ProductID获取值作为文件名一部分.
而在模板中,分页获取的数据是
<a href="product{FirstPage}.html">, FirstPage永远都是数字.
如果ProductID是非数字,则分页根据没办法指向生成的html文件.
总之一句话,分页指向的页面 不能和 生成的页面名字对应
#37楼[
楼主]2007-09-17 13:10 |
--引用--------------------------------------------------
zqz: 代码有问题!!
在GenerateProduct()方法中, 以下语句从
outputFileName = string.Format(@"{0}\Html\Product{1}.html", outputPath, productList[i].ProductID);
从ProductID获取值作为文件名一部分.
而在模板中,分页获取的数据是
<a href="product{FirstPage}.html">, FirstPage永远都是数字.
如果ProductID是非数字,则分页根据没办法指向生成的html文件.
总之一句话,分页指向的页面 不能和 生成的页面名字对应
--------------------------------------------------------
outputFileName = string.Format(@"{0}\Html\Product{1}.html", outputPath, productList[i].ProductID);
中的productList[i].ProductID换成 i+1.
TO:Eric Fine
我在使用你的EFPlatform.TemplateEngine时,根据你的demo有问题,
问题如下 :
------------------------------------------------------------
Region rgnTitle = gen.Regions["Title"];
Region rgnCategory = gen.Regions["Category"];
Region rgnProducts = gen.Regions["Products"];
Region rgnNavigator = gen.Regions["Navigator"];
------------------------------------------------------------
在EFPlatform.TemplateEngine已经不能用了,因为gen里面已经没有Regions了。
我应该怎么去用啊 ?
希望能有答复啊,急用 ,万分感谢!
#39楼[
楼主]2007-10-23 18:35 |
@stone_zhu
请改用 GetRegion 方法
希望能给个完整的DEMO提供下载?或发到我邮箱:763727231@qq.com
怎么所有的产品都放在一个Category Page页里呀?在哪里设置每页的数量?
牛....X....喜欢!~!~
支持一下!~!~
我在学习你的这个模板感觉真不错,佩服啊,可不可以提供一下你的这个演示例子里面的代码让大家下载啊,那样看起来更容易明白点,先谢谢了
异常详细信息: System.ArgumentException: 类型“System.String”的对象无法转换为类型“System.Decimal”。
源错误:
行 134: if(pi != null)
行 135: {
行 136: pi.SetValue(model, dr[i], null);
行 137: }
行 138: }
源文件: F:\EFPlatform.TemplateEngine\Helper.cs 行: 136
请问怎么解决呀?
#48楼[
楼主]2008-04-23 19:51 |
有问题可以EMAIL我~
#49楼[
楼主]2008-04-25 17:25 |
看到此代码很兴奋,一直想用一个在.net环境下的,总找不到,楼主能否把最新的DLL发到我邮箱yeahjob@163.com,谢谢
请问下这模板类能实现下面这样分页显示数据吗?
~~~~~~~~~~显示的数据~~~~~~~~~~~~
1,2,3,4,5,6...>>
这样的分页吗?
能不能提供一个完整的实例给我?
<table>
<thead>
<tr>
<td>Key</td><td>Value</td>
</tr>
</thead>
<tbody>
<!--{#foreach /HttpContext/Request/Params/Item}-->
<tr>
<td>{=Key}</td><td>{=Value}</td>
</tr>
<!--{#end}-->
</tbody>
</table>
要解析这个东西,C#应该要怎么写啊?
我的邮箱:sunyunjs@126.com
老实讲,这个实例跟普通的替换没多大差别
我是想看一下如何解析类似:
<table>
<thead>
<tr>
<td>Key</td><td>Value</td>
</tr>
</thead>
<tbody>
<!--{#foreach /HttpContext/Request/Params/Item}-->
<tr>
<td>{=Key}</td><td>{=Value}</td>
</tr>
<!--{#end}-->
</tbody>
</table>
的调用方法的
LateBindingNavigator工具类应该引用什么组件?在网上找了很久都没有找出来啊
#56楼[
楼主]2008-06-01 22:25 |
@泡泡版主
49楼放的就是最新的....其实是好久没更新过了, 整理了下再放出来而已
#57楼[
楼主]2008-06-01 22:29 |
@sunqy
我这个东东的意图是分离界面和逻辑, 让网页美工去写JS已经是很不规范的情景了, 让他们再去学用别人自定义的模板语言就跑得更远了
我想问一下,使用你这个生成引擎时,生成单列表已没什么问题了,但如果一个页面要多不同分类的列表,要怎么做?能不能通过在模版里设置类别ID的?
segments.Add(new Field(m.Value, name, len, format));
里面的len,如果变化了一次没有初始化回0,以后创建的field都会限制到5了吧?
博主看看吧!
--引用--------------------------------------------------
rocky820420: segments.Add(new Field(m.Value, name, len, format));
里面的len,如果变化了一次没有初始化回0,以后创建的field都会限制到5了吧?
博主看看吧!
--------------------------------------------------------
不是5, 是第一次变化成的值!呵呵!
那个Demo怎么下不了啊,兄弟急用,麻烦发一个我吧,谢谢啦
38487079@QQ.Com
美工 根本不可能知道属性名是多少,所以这个也不是很通用。。
我也要个Demo,麻烦也发给我一个
383063879@qq.com
谢谢