zoukankan      html  css  js  c++  java
  • Mini ORM——PetaPoco笔记

    Mini ORM——PetaPoco笔记

    记录一下petapoco官网博客的一些要点。这些博客记录了PetaPoco是如何一步步改进的。

    目录:

    Announcing PetaPoco

    PetaPoco-Improvements

    PetaPoco-Improvements II

    PetaPoco-T4 Template

    PetaPoco-NuGet Package

    PetaPoco-Paged Queries

    PetaPoco-Named Columns,Result Columns and int/long conversion

    PetaPoco-NUnit Test Cases

    PetaPoco-Value Conversions and UTC Times

    PetaPoco-T4 Template support for SQL Server

    Some Minor PetaPoco Improvements

    PetaPoco-Transaction Bug and Named Parameter Improvements

    PetaPoco-Custom mapping with the Mapper interface

    PetaPoco-Smart Consecutive Clause Handling in SQL Builder

    PetaPoco-Performance Improvements using DynamicMethods

    PetaPoco-More Speed

    Benchmarking SubSonic's Slow Performance

    PetaPoco - Support for SQL Server Compact Edition

    PetaPoco - PostgreSQL Support and More Improvements

    PetaPoco - A couple of little tweaks

    PetaPoco - Working with Joins

    PetaPoco - Oracle Support and more

    PetaPoco - Not So Poco!(or, adding support for dynamic)

    PetaPoco - Version 2.1.0

    PetaPoco - Incorporating Feedback

    PetaPoco - Single Column Value Requests

    PetaPoco - Version 3.0.0

    PetaPoco - Experimental Multi Poco Queries

    PetaPoco - What's new in v4.0

    PetaPoco - Mapping One-to-Many and Many-to-One Relationships

    PetaPoco - Partial Record Updates

    Long Time No Post and PetaPoco v5

    Announcing PetaPoco

    http://www.toptensoftware.com/Articles/68/Announcing-PetaPoco

    第一个版本。

    PetaPoco-Improvements

    http://www.toptensoftware.com/Articles/69/PetaPoco-Improvements

    如果运行查询时不以“select”开头,则petapoco会自动加上:

    // Get a record
    var a=db.SingleOrDefault<article>("SELECT * FROM articles WHERE article_id=@0", 123);
    
    
    // can be shortened to this: Get a record
    var a=db.SingleOrDefault<article>("WHERE article_id=@0", 123);

    增加了IsNew和Save方法:

    如果现在有一个poco对象,要确认它是否在数据库中还是一个新记录,可以通过检查它的主键是否被设置了默认值以外的值来判断:

    // Is this a new record 
    if (db.IsNew(a))
    {
        // Yes it is...
    }

    相关的,有一个Save方法,来自动决定是插入记录还是更新记录:

    // Save a new or existing record
    db.Save(a);

    PetaPoco-Improvements II

    http://www.toptensoftware.com/Articles/70/PetaPoco-Improvements-II

    改进列映射:PetaPoco支持[Ignore]属性来指定某个属性被忽略,现在添加了两个新属性:

    [ExplicitColumns]:添加到POCO类来表示只有明确标出的列才进行映射

    [Column]:添加到所有需要映射的列

    缓存POCO类型信息

    跟踪最后一个SQL语句:公开了三个参数:

    LastSQL

    LastArgs:一个Object[]数组传递的所有参数

    LastCommand:一个字符串,显示SQL和参数

    可以在调试监视窗口监视LastCommand属性。

    异常处理:通过OnException方法来找出错误

    一些错误:

    Fetch方法返回一个List,而不是一个IEnumerable。

    有些方法使用默认参数,无法与旧版本C#兼容。

    自动Select无法检测到以空白开始的select语句。

    MySQL参数管理和用户自定义连接字符串不能正常工作。

    PetaPoco-T4 Template

    http://www.toptensoftware.com/Articles/71/PetaPoco-T4-Template

    增加了T4模板支持:

    自动生成PetaPoco对象:

    复制代码
    [TableName("articles")]
    [PrimaryKey("article_id")]
    [ExplicitColumns]
    public partial class article  
    {
        [Column] public long article_id { get; set; }
        [Column] public long site_id { get; set; }
        [Column] public long user_id { get; set; }
        [Column] public DateTime? date_created { get; set; }
        [Column] public string title { get; set; }
        [Column] public string content { get; set; }
        [Column] public bool draft { get; set; }
        [Column] public long local_article_id { get; set; }
        [Column] public long? wip_article_id { get; set; }
    }
    复制代码

    还可以生成一些常用方法,比如Save(),IsNew(),Update(),SingleOrDefault()...可以这样使用:

    var a = article.SingleOrDefault("WHERE article_id=@0", id);
    a.Save();

    T4模板从PetaPoco.Database推导出一个类来描述数据库本身,这个类有一个静态方法GetInstance(),可以用这个方法来得到数据库的实例,这样来使用:

    var records=jabDB.GetInstance().ExecuteScalar<long>("SELECT COUNT(*) FROM articles");

    T4模板包括三个文件:

    PetaPoco.Core.ttinclude:包括所有读取数据库架构的常规方法

    PetaPoco.Generator.ttinclude:定义生成内容的实际模板

    Records.tt:模板本身,包括一些设置,包括其他两个模板文件

    一个Records.tt文件看起来是这样的:

    复制代码
    <#@ include file="PetaPoco.Core.ttinclude" #>
    <#
        // Settings
        ConnectionStringName = "jab";
        Namespace = ConnectionStringName;
        DatabaseName = ConnectionStringName;
        string RepoName = DatabaseName + "DB";
        bool GenerateOperations = true;
    
        // Load tables
        var tables = LoadTables();
    
    #>
    <#@ include file="PetaPoco.Generator.ttinclude" #>
    复制代码

    使用模板:

    添加这三个文件到C#项目。

    确认在app.config或web.config里定义了数据库连接字符串connection string and provider name

    编辑Records.tt里的ConnectionStringName为实际的ConnectionStringName

    保存Records.tt文件。T4模板会自动生成Records.cs文件,从数据库中所有的表来生成POCO对象。

    PetaPoco-NuGet Package

    http://www.toptensoftware.com/Articles/73/PetaPoco-NuGet-Package

    现在可以从NuGet来安装了。

    PetaPoco-Paged Queries

    http://www.toptensoftware.com/Articles/74/PetaPoco-Paged-Queries

    支持分页查询,通过FetchPage方法:

    public PagedFetch<T> FetchPage<T>(long page, long itemsPerPage, string sql, params object[] args) where T : new()

    注意一点page参数是从0开始。返回值是一个PagedFetch对象:

    复制代码
    // Results from paged request
    public class PagedFetch<T> where T:new()
    {
        public long CurrentPage { get; set; }
        public long ItemsPerPage { get; set; }
        public long TotalPages { get; set; }
        public long TotalItems { get; set; }
        public List<T> Items { get; set; }
    }
    复制代码

    CurrentPage和ItemsPerPage只是反映传递过来的page和itemsPerPage参数。因为在构造分页控件的时候需要用到。

    注意返回的是一个List<T>而不是IEnumerable<T>。在PetaPoco里这是一个Fetch而不是一个Query。添加一个IEnumerable版本也很简单,但考虑到结果集的大小是分页决定的,因此没必要添加。

    背后的故事:

    我总是觉得构建分页查询语句很乏味,这一般涉及到两个不同但很类似的SQL语句:

    1.分页查询本身

    2.查询所有记录数量。

    接下来讲到如何处理MySQL和SQL Server分页查询的异同,为了支持不同的数据库,使用了不同的查询语句。略过。

    PetaPoco-Named Columns,Result Columns and int/long conversion

    http://www.toptensoftware.com/Articles/75/PetaPoco-Named-Columns-Result-Columns-and-int-long-conversion

    命名列:

    现在可以修改映射的列名称,通过给[Column]属性一个参数:

    [PetaPoco.Column("article_id")] long id { get; set; }

    注意,表的[PrimaryKey]属性和其他PetaPoco.Database 方法的primaryKeyName参数指的是列名,不是映射的属性名。

    结果列:

    有时候运行查询不仅返回表中的列,还会有计算或连接的列。我们需要查询结果能够填充这些列,但在Update和Insert操作的时候忽略它们。

    为此目的增加了一个新的[ResultColumn]属性。

    假设你有一个categories表,你想能够检索每个类别的文章数量。

    复制代码
    [TableName("categories")]
    [PrimaryKey("category_id")]
    [ExplicitColumns]
    public class category
    {
        [Column] public long category_id { get; set; }
        [Column] public string name { get; set; }
        [ResultColumn] public long article_count { get; set; }
    }
    复制代码

    你仍然可以像以前一样执行Update和Insert方法,aritical_count属性将被忽略。

    var c =  db.SingleOrDefault<category>("WHERE name=@0", "somecat");
    c.name="newname";
    db.Save(c);

    但是你也可以用它来捕获join的结果:

    复制代码
    var sql = new PetaPoco.Sql()
        .Append("SELECT categories.*, COUNT(article_id) as article_count")
        .Append("FROM categories")
        .Append("JOIN article_categories ON article_categories.category_id = categories.category_id")
        .Append("GROUP BY article_categories.category_id")
        .Append("ORDER BY categories.name");
    
    foreach (var c in db.Fetch<category>(sql))
    {
        Console.WriteLine("{0}	{1}	{2}", c.category_id, c.article_count, c.name);
    }
    复制代码

    注意,填充一个[ResultColumn]你必须在你的select字句中显式引用它。PetaPoco从自动生成的select语句中生成的列中不会包括它们(比如在上一个例子中的SingleOrDefault命令)。

    自动long/int转换

    MySQL返回的count(*)是一个long,但是有时候把这个属性声明为int更好些。这将在试图定义这个属性的时候导致异常。

    现在PetaPoco可以自动做这个转换。当long转换为int的时候如果值超出范围则抛出一个异常。

    IDataReaders销毁

    上一个版本有个bug,Fetch或Query方法后data readers没有被销毁。现在已经修复了这个错误。

    PetaPoco-NUnit Test Cases

    http://www.toptensoftware.com/Articles/76/PetaPoco-NUnit-Test-Cases

    如果要在生产环境中使用PetaPoco,很需要能够在一个更可控的方式中进行测试。

    为了能够用相同的一组测试来测试SQL Server和MySQL,设置了两个连接字符串:"mysql"和"sqlserver",然后把这些字符串当做参数来运行测试。

    [TestFixture("sqlserver")]
    [TestFixture("mysql")]
    public class Tests : AssertionHelper
    {

    数据库不需要任何特殊的方式配置测试用例比如创建一个名为petapoco的表然后进行清理工作。有一个嵌入式的SQL资源脚本来进行初始化和清理.

    测试用例本身简单直接的使用每个特性。对SQL builder功能来说也有测试用例。

    主要测试都可以通过,没有任何问题。有几个预期的SQL Server的bug已经被修正(比如FetchPage方法有一些SQL语法错误和一些类型转换问题)。

    测试用例包含在github库,但NuGet包中没有。

    PetaPoco-Value Conversions and UTC Times

    http://www.toptensoftware.com/Articles/84/PetaPoco-Value-Conversions-and-UTC-Times

    这篇文章已经过时了。

    PetaPoco-T4 Template support for SQL Server

    http://www.toptensoftware.com/Articles/78/PetaPoco-T4-Template-support-for-SQL-Server

    增加了支持SQL Server的T4模板。

    Some Minor PetaPoco Improvements

    http://www.toptensoftware.com/Articles/89/Some-Minor-PetaPoco-Improvements

    一些小的改进,让分页请求更加容易。

    在使用时总是忘掉FetchPage还是PagedFetch的返回类型。现在统一分页方法和返回类型,现在都叫做Page。

    接受Adam Schroder的建议,page number从1开始比从0开始更有意义。

    以前的用法是这样:

    PagedFetch<user> m = user.FetchPage(page - 1, 30, "ORDER BY display_name");

    现在这样用:

    Page<user> m = user.Page(page, 30, "ORDER BY display_name");

    同时接受Adam Schroder的建议,现在有一个构造函数,接受一个connection string name和provider name作为参数。

    PetaPoco-Transaction Bug and Named Parameter Improvements

    http://www.toptensoftware.com/Articles/90/PetaPoco-Transaction-Bug-and-Named-Parameter-Improvements

    我刚注意到(已经修复)PetaPoco的支持事务中的bug,并对Database类增加了命名参数的支持。

    PetaPoco的Sql builder一直支持命名参数的参数属性:

    sql.Append("WHERE name=@name", new { name="petapoco" } );

    现在Database类也支持这个功能了:

    var a=db.SingleOrDefault<person>("WHERE name=@name", new { name="petapoco" } );

    PetaPoco-Custom mapping with the Mapper interface

    http://www.toptensoftware.com/Articles/92/PetaPoco-Custom-mapping-with-the-Mapper-interface

    使用Mapper接口自定义映射

    最简单的使用PetaPoco的方法是用声明哪些属性应该被映射到哪些列的属性装饰你的POCO对象。有时候,这不太实际或有些人觉得这太有侵入性了。所以我添加了一个声明这些绑定的方法。

    PetaPoco.Database类现在支持一个叫做Mapper的静态属性,通过它你可以使用自己的列和表的映射信息。

    首先,你需要提供一个PetaPoco.IMapper接口的实现:

    public interface IMapper
    {
        void GetTableInfo(Type t, ref string tableName, ref string primaryKey);
        bool MapPropertyToColumn(PropertyInfo pi, ref string columnName, ref bool resultColumn);
    }

    当GetTableInfo方法被调用时,tableName和primaryKey将被设置为PetaPoco定义的默认值,如果你需要其他的,修改即可,记得首先检查类型。

    类似的,MapPropertyToColumn方法-修改columnName和resultColumn的值来适应你的需要。允许映射返回true,或忽略它返回false。

    一旦你实现了IMapper接口,你只需要设置PetaPoco的静态属性Mapper:

    PetaPoco.Database.Mapper = new MyMapper();

    注意这有一些限制,不过我觉得这是值得的。

    1、这个mapper是被所有Database的实例共享的。PetaPoco在全局缓存这些列映射,所以不能为不同的数据库实例提供不同的映射。

    2、只能安装一个mapper。

    PetaPoco-Smart Consecutive Clause Handling in SQL Builder

    http://www.toptensoftware.com/Articles/91/PetaPoco-Smart-Consecutive-Clause-Handling-in-SQL-Builder

    有时需要添加多个可选的Where字句。PetaPoco的连续子句处理可以自动正确加入它们。

    想象一下,你正在查询一个数据库,有两个可选条件,一个开始日期,一个结束日期:

    复制代码
    List<article> GetArticles(DateTime? start, DateTime? end)
    {
        var sql=new Sql();
    
        if (start.HasValue)
            sql.Append("WHERE start_date>=@0", start.Value);
    
        if (end.HasValue)
        {
            if (start.HasValue)
                sql.Append("AND end_date<=@0", end.value);
            else
                sql.Append("WHERE end_data<@0", end.Value);
        }
    
        return article.Fetch(sql);
    }
    复制代码

    计算第二个条件是where还是and子句很乏味。现在PetaPoco可以自动检测连续的where子句并自动转换后续的为and子句。

    复制代码
    List<article> GetArticles(DateTime? start, DateTime? end)
    {
        var sql=new Sql();
    
        if (start.HasValue)
            sql.Append("WHERE start_date>=@0", start.Value);
    
        if (end.HasValue)
            sql.Append("WHERE end_data<@0", end.Value);
    
        return article.Fetch(sql);
    }
    复制代码

    有一些注意事项,但很容易处理。

    1、where子句必须是Sql片段的第一个部分,所以下面的不会工作:

    sql.Append("WHERE condition1 WHERE condition2");

    但这样的可以:

    sql.Append("WHERE condition1").Append("WHERE condition2");

    2、Sql片段必须相邻,所以这样的不会工作:

    sql.Append("WHERE condition1").Append("OR condition2").Append("WHERE condition3");

    3、你也许需要给个别条件加上括号来确保得到正确的优先级:

    sql.Append("WHERE x");
    sql.Append("WHERE y OR Z");

    应该写成:

    sql.Append("WHERE x");
    sql.Append("WHERE (y OR z)");

    这个功能也适用于Order By子句:

    var sql=new Sql();
    sql.Append("ORDER BY date");
    sql.Append("ORDER BY name");

    将生成:

    ORDER BY date, name

    PetaPoco-Performance Improvements using DynamicMethods

    http://www.toptensoftware.com/Articles/93/PetaPoco-Performance-Improvements-using-DynamicMethods

    使用动态方法的性能改进

    PetaPoco已经比典型的Linq实现快。通过消除反射用动态生成的方法取代它,现在甚至更快-约20%。

    在原始版本,PetaPoco一直使用反射来设置它创建的从数据库中读取的POCO对象的属性。反射的优势是很容易使用,不足之处是有一点点慢。

    在.NET里可以使用DynamicMethod和ILGenerator动态生成一段代码。这是相当复杂的实现,需要了解MSIL。但它的速度更快,约20%。事实上我希望性能能够更好,所以也许不该给反射扣上速度慢的坏名声。

    所以这里的想法很简单-使用一个IDataReader并动态生成一个函数,它知道如何从data reader中读取列值,并直接将它们分配给POCO相应的属性。

    为了实现此目的,我做了一个公开可见的变化-即virtual Database。ConvertValue方法已废弃,在IMapper接口使用一个新的GetValueConverter方法替代之。

    public interface IMapper
    {
        void GetTableInfo(Type t, ref string tableName, ref string primaryKey);
        bool MapPropertyToColumn(PropertyInfo pi, ref string columnName, ref bool resultColumn);
        Func<object, object> GetValueConverter(PropertyInfo pi, Type SourceType);
    }

    这么做的主要目的是,提供了一个可以在生成MSIL的时候采用的决策点。如果一个converter是调用它的MSIL需要的,则生成。如果不则省略。

    添加动态方法生成增加了一些成本,.cs文件大小增加了120行左右。但我认为值得。

    PetaPoco-More Speed

    http://www.toptensoftware.com/Articles/94/PetaPoco-More-Speed

    我注意到Sam Saffron的Dapper项目……(Dapper也是一个很好的微型ORM框架)

    首先我忘了昨天的帖子中提到的DynamicMethod支持的想法来自Sam Saffron在Stack Overflow上发表的帖子,如何压榨更多的性能。

    其次,Sam Saffron开源了Dapper项目,包括一个比较各种ORM的基准测试程序。当然我忍不住更新它来支持PetaPoco,很快就突破了几个小瓶颈。一点点小调整,这是一些典型结果(就是说PetaPoco很快,肯定比EF快了):

    运行500次迭代载入一个帖子实体
    手工编码 65ms
    PetaPoco(Fast) 67ms
    PetaPoco(Normal)  78ms
    Linq 2 SQL 841ms
    EF 1286ms

    我运行了PetaPoco的两个测试模式,Normal和Fast

    Normal - 所有的默认选项和启用smarts

    Fast - 所有的smarts,比如自动select子句,强制DateTime到UTC转换,命名参数等都禁用

    Normal模式是我很期望通常使用PetaPoco的方式,但禁用这些额外功能一直是可选的,当你真的试图压榨所有的性能的时候。

    共享连接支持

    主要的修复是可以重用一个单一数据库连接。在之前的版本中每个查询都会导致一个新的连接。通过公开OpenSharedConnection方法,你可以预先调用它,所有随后的查询都将重用相同的连接。调用OpenSharedConnection和CloseSharedConnection是引用计数(reference counted),可以嵌套。

    为一个HTTP请求的持续时间打开共享连接可能会是一个好主意。我还没有尝试,but I going to try hooking this in with StructureMap's HttpContextScoped .

    最后关于共享连接要注意的。PetaPoco一旦被销毁会自动关闭共享连接。这意味着,如果调用OpenSharedConnection一次,and the Database is disposed everything should be cleaned up properly。

    其他可选行为:

    其他功能是禁止一些PetaPoco行为的能力:

    -EnableAutoSelect,是否自动添加select子句,禁用时,以下无法运行:

    var a=db.SingleOrDefault<article>("WHERE article_id=@0", id);

    -EnableNamedParams,是否处理Database类的参数,禁用时,以下无法运行:

    var a=db.SingleOrDefault<article>("WHERE article_id=@id", new { id=123 } );

    -ForceDateTimesToUtc,禁用时,日期时间会返回数据库提供的完全相同的数据。启用时,PetaPoco返回DateTimeKind.Utc类型。

    禁用这些特性可以带来一点小的性能提升。如果他们造成了一些不良影响也提供了可能性来绕过这些特性。

    其他优化

    还做了一些其他的优化:

    First(), FirstOrDefault(), Single() and SingleOrDefault()这些方法基准测试程序并不使用,单我还是提供了优化版本,返回一个记录,保存建立一个列表,或单条记录的enumerable。

    用try/finally来代理using子句,PetaPoco内部使用一个可清理对象和using语句来确保连接被关闭。我已经更换了这些,用一个直接调用和finally块来保存实例化一个额外的对象。

    Benchmarking SubSonic's Slow Performance

    http://www.toptensoftware.com/Articles/95/Benchmarking-SubSonic-s-Slow-Performance

    本文主要是说Subsonic性能如何慢,以SingleOrDefault方法为例。

    PetaPoco - Support for SQL Server Compact Edition

    http://www.toptensoftware.com/Articles/96/PetaPoco-Support-for-SQL-Server-Compact-Edition

    支持SQL Server Compact版本。略过。

    PetaPoco - PostgreSQL Support and More Improvements

    http://www.toptensoftware.com/Articles/98/PetaPoco-PostgreSQL-Support-and-More-Improvements

    支持PostgreSQL数据库。略过。

    PetaPoco - A couple of little tweaks

    http://www.toptensoftware.com/Articles/99/PetaPoco-A-couple-of-little-tweaks

    几个小调整。

    Sql.Builder

    为了使Sql.Builder更流畅,添加了一个新的静态属性返回一个Sql实例。以前写法:

    new Sql().Append("SELECT * FROM whatever");

    现在写法:

    Sql.Builder.Append("SELECT * FROM _whatever");

    这是一个微不足道的变换,但可读性更好。

    自动Select子句改进

    此前,PetaPoco可以自动转换:

    var a = db.SingleOrDefault<article>("WHERE id=@0", 123);

    转换为:

    var a = db.SingleOrDefault<article>("SELECT col1, col2, col3 FROM articles WHERE id=@0", 123);

    现在它也会处理这个问题:

    var a = db.SingleOrDefault<article>("FROM whatever WHERE id=@0", 123);

    换言之,如果它看到一个语句以FROM开始,它只添加SELECT语句和列名列表,而不添加FROM子句。

    T4模板改进

    由于NuGet的奇怪的特性,安装文件顺序为字母倒序排列。导致T4模板在必须的文件之前安装,随之而来一堆错误。根据David Ebbo的建议Record.tt改名为Database.tt。

    PetaPoco - Working with Joins

    http://www.toptensoftware.com/Articles/101/PetaPoco-Working-with-Joins

    通常情况下,使用PetaPoco有一个相当直接的映射,从C#类映射到数据库。大部分时间这工作的很好,但是当你需要一个JOIN时-你需要比C#类的属性更多的列来保存。

    方法1-手动定义一个新的POCO类

    第一个方法是简单的创建一个新类来保存所有的JOIN后的列。比如需要一个文章标题列表和每篇文章的评论计数,SQL看起来像这样:

    SELECT articles.title, COUNT(comments.comment_id) as comment_count
    FROM articles
    LEFT JOIN comments ON comments.article_id = articles.article_id
    GROUP BY articles.article_id;

    定义一个C#类来保存结果(注意属性名称匹配SQL的列名)

    复制代码
    public class ArticleWithCommentCount
    {
        public string title 
        { 
            get; 
            set; 
        }
    
        public long comment_count
        {
            get;
            set;
        }
    }
    复制代码

    使用新类型来查询:

    var articles = db.Fetch<ArticleWithCommentCount>(sql);

    方法2-扩展现有的POCO对象

    更有可能的是你已经有了一个POCO对象,包括了JOIN结果的大部分列,你只是想加入额外的几个列。

    与上个例子相同,你已经有了一个article对象,你只是想加入一个comment_count属性,这就需要[ResultColumn]属性了,添加一个新属性到现有的类:

    [ResultColumn]
    public long comment_count
    {
        get;
        set;
    }

    通过声明一个属性为[ResultColumn],如果结果集的列名匹配它将被自动填充,但是Update和Insert的时候会被忽略。

    方法3-扩展T4模板中的POCO对象

    上面的方法很好,如果你手动编写自己的POCO类。但是如果你用T4模板来生成呢?你如何扩展这些类的属性,重新运行模板不会覆盖新属性?答案在于T4模板生成的是partial class。

    如果你不知道什么是partial class……

    还是上面这个例子,添加一个新类,使用与T4模板生成的类相同的名字(确保名称空间也要匹配)。声明为partial class,给任何JOIN的列添加[ResultColumn]属性:

    复制代码
    public partial class article
    {
        [ResultColumn]
        public long comment_count
        {
            get;
            set;
        }
    }
    复制代码

    这是最后生成的查询(使用PetaPoco的SQL builder)

    var articles = db.Fetch<article>(PetaPoco.Sql.Builder
                    .Append("SELECT articles.title, COUNT(comments.comment_id) as comment_count")
                    .Append("FROM articles")
                    .Append("LEFT JOIN comments ON comments.article_id = articles.article_id")
                    .Append("GROUP BY articles.article_id")
                    );

    方法4-对象引用其他POCO类

    当然如果PetaPoco能够使用属性对象引用来映射JOIN的表会很好-像一个完全成熟的ORM一样。但PetaPoco不能这样做,也许永远不会-这是不值得的复杂性,这也不是PetaPoco设计用来解决的问题。

    更新 方法5-使用C#4.0的dynamic

    从最初发布这篇文章以来,PetaPoco已经更新支持C#的dynamic expando objects。这提供了一个伟大的方法来处理JOIN,GROUP BY和其他计算的查询。

    PetaPoco - Oracle Support and more... 

    http://www.toptensoftware.com/Articles/103/PetaPoco-Oracle-Support-and-more

    Oracle支持。略过

    Single和SingleOrDefault的主键版本

    取一个单一记录是很简单的:

    var a = db.SingleOrDefault<article>("WHERE article_id=@0", some_id);

    当然最常见的情况是根据主键取记录,所以现在对Single和SingleOrDefault有一个新的重载:

    var a = db.SingleOrDefault<article>(some_id);

    作为边注,不要忘了如果你使用T4模板的话,上面的可以更简化:

    // Normal version
    var a = article.SingleOrDefault("WHERE title=@0", "My Article");
    
    // New simpler PK version
    var a = article.SingleOrDefault(some_id);

    对于Joins的SQL builder方法

    现在增加了两个新的方法:InnerJoin和LeftJoin:

    var sql = Sql.Builder
        .Select("*")
        .From("articles")
        .LeftJoin("comments").On("articles.article_id=comments.article_id");

    枚举属性类型

    此前如果你试图使用有枚举属性的POCO对象会抛出一个异常。现在PetaPoco会正确的转换整数列到枚举属性。

    新OnExecutingCommand虚拟方法

    OnExecutingCommand是一个新的虚拟方法,在PetaPoco命中数据库之前被调用。

    // Override this to log commands, or modify command before execution
    public virtual void OnExecutingCommand(IDbCommand cmd) { }

    你在两种情况下也许会用到它:

    1、日志-你可以抓取SQL语句和参数值并记录任何你需要的。

    2、调整SQL-你可以在返回之前调整SQL语句-也许为某个特殊平台的数据库。

    PetaPoco - Not So Poco!(or, adding support for dynamic) 

    http://www.toptensoftware.com/Articles/104/PetaPoco-Not-So-Poco-or-adding-support-for-dynamic

    PetaPoco最初的灵感来自Massive-通过dynamic Expando objects返回一切。对于大多数情况我觉得这比较麻烦,更喜欢强类型的类。但是有些时候支持dynamic也是有用的-特别是用于Join、Group By和其他计算查询时。

    构造一个dynamic查询只需使用现有的查询方法,只是传递一个dynamic的泛型参数。返回的对象将为每个查询返回的列对应一个属性:

    foreach (var a in db.Fetch<dynamic>("SELECT * FROM articles"))
    {
        Console.WriteLine("{0} - {1}", a.article_id, a.title);
    }

    注意此时不支持自动添加SELECT子句,因为PetaPoco不知道表名。

    你也可以做Update、Insert、Delete操作但是你需要指定表名和要更新的主键。

    复制代码
    // Create a new record
    dynamic a = new ExpandoObject();
    a.title = "My New Article";
    
    // Insert it
    db.Insert("articles", "article_id", a);
    
    // New record ID returned with a new property matching the primary key name
    Console.WriteLine("New record @0 inserted", a.article_id")
    复制代码

    更新:

    // Update
    var a = db.Single("SELECT * FROM articles WHERE article_id=@0", id);
    a.title="New Title";
    db.Update("articles", "article_id", a);

    Delete()、Save()和IsNew()方法都类似。

    有一个编译指令,可以禁用dynamic支持。因为.NET3.5不支持。

    PetaPoco - Version 2.1.0 

    http://www.toptensoftware.com/Articles/105/PetaPoco-Version-2-1-0

    支持dynamic

    收集了是否应该包括支持dynamic的反馈意见后,我决定采用它。但是如果你运行较早版本的.NET也可以禁用它。

    关闭dynamic支持:

    1、打开项目属性

    2、切换到“生成”选项卡

    3、在“条件编译符号”添加PETAPOCO_NO_DYNAMIC

    包含空格的列(和其他非标识符字符的)

    以前版本的PetaPoco假设你的数据库的任何列名都是有效的C#标识符名称。如果列名中包含空格,当然会出错,现在已经通过两种方式来纠正这个错误:

    1、PetaPoco可以正确转义SQL数据库的分隔符,如[column], `column` or "column"

    2、T4模板清除列名称以与C#兼容,使用Column属性来设置列的DB name。

    需要注意的是,如果你使用了dynamic的不兼容的列名,PetaPoco在这种情况下并不试图纠正它们。你仍将需要修改SQL来返回一个可用的列名。

    var a=db.SingleOrDefault<dynamic>(
            "SELECT id, [col with spaces] as col_with_spaces FROM whatever WHERE id=@0", 23); 
    Console.WriteLine(a.col_with_spaces);

    或者,把返回的Expandos转换为dictionary:

    var a=db.SingleOrDefault<dynamic>(
            "SELECT id, [col with spaces] FROM whatever WHERE id=@0", 23); 
    Console.WriteLine((a as IDictionary<string, object>)["col with spaces"]);

    Ansi String支持

    DBA专家Rob Sullivan昨天指出,SQL Server在尝试使用Unicode字符串的参数来查询数据类型为varchar的列的索引的时候,会导致严重的性能开销。为了解决这个问题需要把参数约束为DbType.AnsiString。现在可以使用新的AnsiString类的字符串参数:

    var a = db.SingleOrDefault<article>("WHERE title=@0", new PetaPoco.AnsiString("blah"));

    Exists(PrimaryKey) and Delete(PrimaryKey)

    可以检查是否存在一个主键的记录。

    if (db.Exists<article>(23)) 
    db.Delete <article>(23);

    PetaPoco - Incorporating Feedback

    http://www.toptensoftware.com/Articles/106/PetaPoco-Incorporating-Feedback

    支持无标识的主键列

    在以前的版本中,PetaPoco假设主键列的值总是有数据库生成和填充。但情况并非总是如此。现在PetaPoco的PrimaryKey属性有了一个新的property - autoIncrement。

    复制代码
    [TableName("subscribers")]
    [PrimaryKey("email", autoIncrement=false)]
    [ExplicitColumns]
    public partial class subscribers
    {
          [Column] public string email { get; set; }
          [Column] public string name { get; set; }
    }
    复制代码

    autoIncrement默认设置为true,你只需要指定不是自动生成主键的表即可。当autoIncrement设置为false的时候PetaPoco可以正确的插入记录-忽略主键的值而不是试图取回主键。

    如果你没有用这个属性装饰,Insert方法还有一个新的重载,可以让你指定是否自动生成主键。

    public object Insert(string tableName, string primaryKeyName, bool autoIncrement, object poco)

    你还可以通过IMapper来指定这个属性。因为GetTableInfo方法因为它的ref参数变得有点失控了,我把它改成这样:

    复制代码
    void GetTableInfo(Type t, TableInfo ti);
    
    public class TableInfo
    {
        public string TableName { get; set; }
        public string PrimaryKey { get; set; }
        public bool AutoIncrement { get; set; }
        public string SequenceName { get; set; }
    }
    复制代码

    不幸的是这是一个不向后兼容的改变。

    有一个警告,对所有没有自增主键列的表来说,IsNew()和Save()方法无法工作,因为没有办法知道记录是否来自数据库。这种情况下你应该知道是调用Insert()还是Update()。

    最后,T4模板已经更新为自动生成autoIncrement属性。这适用于SQL Server、SQL Server CE、MySQL和PostgreSQL,但不适用于Oracle。

    架构调整

    PetaPoco的T4模板可以支持调整在生成最后一个POCO类之前导入的架构信息。这可以用来重命名或忽略某些表和某些列。

    复制代码
    // To ignore a table
    tables["tablename"].Ignore = true;
    
    // To change the class name of a table
    tables["tablename"].ClassName = "newname";                  
    
    // To ignore a column
    tables["tablename"]["columnname"].Ignore = true;            
    
    // To change the property name of a column
    tables["tablename"]["columnname"].PropertyName = "newname"; 
    
    // To change the property type of a column
    tables["tablename"]["columnname"].PropertyType = "bool";        
    
    // To adjust autoincrement 
    tables["tablename"]["columnname"].AutoIncrement = false;        
    复制代码

    调用LoadTables方法后在Database.tt中使用这个方法。可以查看最新的Database.tt。

    改善存储过程支持

    PetaPoco已经支持存储过程-你必须关闭EnableAutoSelect让它在查询的时候起作用。我已经小小的修正了一下,以便PetaPoco不会在以Execute或Call开头的语句前自动插入Select子句,这意味着你可以调用存储过程:

    db.Query<type>("CALL storedproc")     // MySQL stored proc
    db.Query<type>("EXECUTE stmt")        // MySQL prepared statement
    db.Query<type>("EXECUTE storedproc")  // SQL Server

    这只是一个很小的改进,不支持out参数。

    T4 Support for SQL Server Geography and Geometry

    你可以添加一个Microsoft.SqlServer.Types.dll引用。

    PetaPoco - Single Column Value Requests

    http://www.toptensoftware.com/Articles/107/PetaPoco-Single-Column-Value-Requests

    单列值查询

    之前的版本只支持返回POCO对象,现在支持这样的查询:

    foreach (var x in db.Query<long>("SELECT article_id FROM articles"))
    {
        Console.WriteLine("Article ID: {0}", x);
    }

    这可以支持所有的Type.IsValueType,字符串和byte数组

    @字符转义

    PetaPoco使用@<name>作为名称参数但是可能会和某些Provider冲突。以前你可以为MySQL转义,现在可以支持所有的Provider了。在这个例子中,@@id将作为@id传递到数据库中而@name将被用作在传递的参数中查找属性名。(怎么翻译?)

    复制代码
    select
        t.Id as '@@id'
    from
        dbo.MyTable as t
    where
        t.Name = @name
    for xml path('Item'), root ('Root'), type 
    复制代码

    Where子句的自动括号

    SQL builder可以自动附加连续的Where子句,比如:

    sql.Where("cond1");
    sql.Where("cond2");

    会变成:

    WHERE cond1 AND cond2

    这挺好的,但是很容易导致不注意的操作法优先级错误。比如:

    sql.Where("cond1 OR cond2");
    sql.Where("cond3");

    会变成:

    cond1 OR cond2 AND cond3

    老实说我并不知道实际的And和Or的优先级-我也不关心,但是我知道使用SQL builder的Where()方法会导致很容易出现这种问题。所以现在Where()方法会自动给参数加括号,会生成下面的语句:

    (cond1 OR cond2) AND (cond3)

    注意,这只适用于Where()方法,当使用Append("WHERE cond")时无效。

    PetaPoco - Version 3.0.0

    http://www.toptensoftware.com/Articles/108/PetaPoco-Version-3-0-0

    本文主要介绍3.0版本的改进,都在前面介绍过了。略过。

    PetaPoco-Experimental-Multi-Poco-Queries

    http://www.toptensoftware.com/Articles/111/PetaPoco-Experimental-Multi-Poco-Queries

    首先这归功于Sam Saffron的Dapper项目。PetaPoco的多POCO查询支持与Dapper的很类似但PetaPoco的实现是相当不同的,列之间的分割点是不同的,它还可以在POCO对象间自动猜测和分配对象的关系。

    背景

    多POCO查询背后的想法是构造一个Join的SQL查询,从每个表返回的列可以自动映射到POCO类。换句话说,不是第一个N列映射到第一个POCO,接下来的N列映射到另一个……

    用法

    var sql = PetaPoco.Sql.Builder
                    .Append("SELECT articles.*, authors.*")
                    .Append("FROM articles")
                    .Append("LEFT JOIN users ON articles.user_id = users.user_id");
    
    var result = db.Query<article, user, article>( (a,u)=>{a.user=u; return a }, sql);

    一些说明:

    1、SQL查询从两个表返回列。

    2、Query方法的前两个泛型参数指定了拥有每行数据的POCO的类型。

    3、第三个泛型参数是返回集合的类型-一般是第一个表的对象类型,但也可以是其他的。

    4、Query方法需要它的第一个参数作为回调委托,可以用来连接两个对象之前的关系。

    所以在这个例子中,我们返回一个IEnumerable<article>,每个article对象都通过它的user属性拥有一个相关user的引用。

    PetaPoco支持最多5个POCO类型,Fetch和Query方法也有变化。

    选择分割点

    返回的列必须和Query()方法中的泛型参数的顺序相同。比如第一个N列映射到T1,接下来N列映射到T2……

    如果一个列名已经被映射到当前POCO类型它就被假定是一个分割点。想象一下这组列:

    article_id, title, content, user_id, user_id, name

    这些POCO:

    复制代码
    class article
    {
        long article_id { get; set; }
        string title { get; set; }
        string content { get; set; }
        long user_id { get; set; }
    }
    
    class user
    {
        long user_id { get; set; }
        string name { get; set; }
    }
    复制代码

    查询类似这样:

    db.Query<article, user, article>( ... )

    感兴趣的是user_id。当映射这个结果集的时候,第一个user_id列将被映射到article,当看到第二个user_id的时候PetaPoco将意识到它已经被映射到article了,于是将其映射到user。

    最后一种确定分割点的方法是当一个列不存在于当前的POCO类型但是存在于下个POCO。注意如果一个列不存在于当前POCO也不存在与下个POCO,它将被忽略。

    自动连接POCO

    PetaPoco可以在返回对象上自动猜测关系属性并自动分配对象引用。

    这种写法:

    var result = db.Query<article, user, article>( (a,u)=>{a.user=u; return a }, sql);

    可以写成:

    var result = db.Query<article, user>(sql);

    两点需要注意的:

    1、第三个返回参数类型不是必需的。返回的结果集合永远是T1类型。

    2、设置对象关系的回调方法不是必需的。

    很明显的,做这项工作PetaPoco有一点小猜测,但是这是一个常见的情况,我认为这是值得的。要实现这个目的,T2到T5必需有一个属性是和它左边的类型是相同的类型。换句话说:

    1、T1必需有一个T2的属性

    2、T1或T2必需有一个T3的属性

    3、T1或T2或T3必需有一个T4的属性

    ……

    同时,属性是从右往左搜索的。所以如果T2和T3都有一个T4的属性,那将使用T3的属性。

    结论和可用性

    你可能需要多阅读这篇文章几次来理解这个新特性,但是一旦你习惯了我相信你会发现这是一个很有用的补充。

    PetaPoco - What's new in v4.0

    http://www.toptensoftware.com/Articles/114/PetaPoco-What-s-new-in-v4-0

    使用一个方法代替Transaction属性

    using(var scope = db.Transaction)

    改成这样:

    using(var scope = db.GetTransaction())

    多POCO查询

    上一篇文章已经介绍过了。

    另外可以直接调用MultiPocoQuery方法:

    IEnumerable<TRet> MultiPocoQuery<TRet>(Type[] types, object cb, string sql, params object[] args)

    这个方法接受一个POCO数组作为参数,而不是泛型参数。

    支持IDbParameters作为SQL arguments

    PetaPoco现在支持直接传递IDbParameters对象到查询中。如果PetaPoco没有正确映射一个属性的时候这很方便。

    例如SQL Server不会将DbNull分配给VarBinary列触发参数配置了正确的类型。现在可以这样做:

    databaseQuery.Execute("insert into temp1 (t) values (@0)", 
                    new SqlParameter() { SqlDbType = SqlDbType.VarBinary, Value = DbNull.Value });

    一个有趣的副作用是你还可以从PetaPoco返回一个IDbParameters。IMapper接口从全局覆盖了PetaPoco的默认参数映射功能。

    在每个基础查询禁用自动select生成的功能

    PetaPoco做了一个合理的工作,猜测何时应该自动插入Select子句-但是这不太完美而且有各种运行不正确的情况。以前的版本你需要关闭EnableAutoSelect属性,运行你的查询然后再改回来。

    现在你可以用一个分号开头来表明Select子句不应被插入。PetaPoco在查询之前会移除分号。

    // Leading semicolon in query will be removed...
    db.Query<mytype>(";WITH R as....");

    T4模板改进-自定义插件清理功能

    现在可以替换标准的T4模板中用来清理表和列名的方法。在T4模板中,在调用LoadTables方法之前设置全局CleanUp属性为一个委托:

    CleanUp = (x) => { return MyCleanUpFunction(x); }

    T4模板改进-包括架构视图和过滤的功能

    T4模板现在可以为数据库中的所有架构生成类,或者仅为一个架构。如果只包括一个特定架构的表,在调用LoadTables方法之前设置全局的SchemaName属性。你也可以用一个前缀来生成类:

    SchemaName = "MySchema";
    ClassPrefix = "myschema_";

    如果你想要一个特定的主架构或其他架构或多架构,设置多个不同SchemaName的Database.ttj即可。

    你还可以用T4模板生成类视图:

    IncludeViews = true;

    ReaderWriterLockSlim多线程支持改进

    PetaPoco使用ReaderWriterLockSlim来保护访问共享数据来提高多线程性能-比如在web程序中。

    支持protected构造函数和属性

    PetaPoco现在可以访问POCO的private和protected成员-包括private构造函数和属性访问器。

    新的Page<>.Context属性

    在一些情况下,我一直为MVC视图使用PetaPoco的强类型的Model对象,但需要一些额外的数据。不想使用ViewData或新建一个新的类,因此为Page类添加了一个Context属性。这可以用来传递一些额外的上下文数据。

    比如有一个页面需要一个partial view来显示网站页的缩略图。当有页面显示的时候是正常的,单如果列表是空的我想显示一个根据上下文来显示的“blank slate”信息。这可能是“你还没有收藏”或“没有更多网站了”或“你还没有喜欢任何网站”……

    为了处理这个问题,在controller中我设置了Context属性可以表明如果没有数据的时候该如何显示空白信息。

    bug修复

    PetaPoco - Mapping One-to-Many and Many-to-One Relationships :http://www.toptensoftware.com/Articles/115/PetaPoco-Mapping-One-to-Many-and-Many-to-One-Relationships

    现在PetaPoco支持多POCO查询。很多人问我PetaPoco如何或是否能够映射一对多和多对一的关系。

    简单的回答是,不会。但你可以自己做,如果你想的话。

    这就是说,请确定你是否真的需要它。如果你只是做一般的Join查询返回POCO那是没必要的。多POCO查询的重点是在捕获Join结果的时候避免定义新的或扩展现有的POCO对象-不是真的要提供Instance Identity。

    实例标识和废弃POCO

    那么究竟当我说"Instance Identity"的时候是什么意思呢?我意思是,如果从两个或更多地方的查询返回一个特定的记录,则所有的情况下都返回相同的POCO实例,或该POCO实例有唯一的标识。例如,如果你正在做一个articles和authors的Join查询,如果两个article有相同的author,那么将引用相同的author的对象实例。

    PetaPoco的多POCO查询总是为每个行创建一个新的实例。因此在上面的例子中,每一行都将创建一个新的author对象。要获得正确的Instance Identity,我们将最终丢弃重复的-所以不要把一对多和多对一作为提高效率的办法-只有在更准确的对象图对你有用的时候再使用它。

    Relator Callbacks

    自动映射和简单关系

    当我们写一个relator callback时,我们看看简单的自动映射多POCO查询看起来像这样:

    var posts = db.Fetch<post, author>(@"
            SELECT * FROM posts 
            LEFT JOIN authors ON posts.author = authors.id ORDER BY posts.id
            ");

    使用自动映射,第一个泛型参数是返回类型。因此这个例子将返回一个List<post>,post对象有一个author类型的属性,PetaPoco将它连接到创建的author对象。

    写relator callback,看起来像这样:

    var posts = db.Fetch<post, author, post>(
            (p,a)=> { p.author_obj = a; return p; },
            @"SELECT * FROM posts 
            LEFT JOIN authors ON posts.author = authors.id ORDER BY posts.id
            ");

    注意上面做了两件事:

    1、在泛型参数中有一个额外的<post,author,post>。最后一个参数表明了返回集合的类型。使用自定义的relator你可以决定使用不同的类代表Join的行。

    2、lambda表达式连接了post和author。

    测试用例地址:https://github.com/toptensoftware/PetaPoco/blob/master/PetaPoco.Tests/MultiPocoTests.cs

    多对一的关系

    为了实现多对一的关系,我们需要做的是保持一个映射的RHS对象,并每次都重用相同的一个。

    复制代码
    var authors = new Dictionary<long, author>();
    var posts = db.Fetch<post, author, post>(
        (p, a) =>
        {
            // Get existing author object
            author aExisting;
            if (authors.TryGetValue(a.id, out aExisting))
                a = aExisting;
            else
                authors.Add(a.id, a);
    
            // Wire up objects
            p.author_obj = a;
            return p;
        },
        "SELECT * FROM posts LEFT JOIN authors ON posts.author = authors.id ORDER BY posts.id"
        );
    复制代码

    实现是很简单的:寻找以前的相同author实例,如果找到了就使用它的引用。如果没有找到就提供一个并存储起来供以后使用。

    当然如果你需要在很多地方这样做很快就会乏味。所以包装一个helper:

    复制代码
    class PostAuthorRelator
    {
        // A dictionary of known authors
        Dictionary<long, author> authors = new Dictionary<long, author>();
    
        public post MapIt(post p, author a)
        {
            // Get existing author object, or if not found store this one
            author aExisting;
            if (authors.TryGetValue(a.id, out aExisting))
                a = aExisting;
            else
                authors.Add(a.id, a);
    
            // Wire up objects
            p.author_obj = a;
            return p;
        }
    }
    复制代码

    现在可以这样运行查询:

    var posts = db.Fetch<post, author, post>(
        new PostAuthorRelator().MapIt,
        "SELECT * FROM posts LEFT JOIN authors ON posts.author = authors.id ORDER BY posts.id"
        );

    好多了,继续……

    一对多关系

    在一对多关系,我们想从RHS得到的对象集合来填充每个LHS对象。比如上面的例子,我们想要一个author列表,每个都有一个作者的文章集合。

    SELECT * FROM authors 
    LEFT JOIN posts ON posts.author = authors.id ORDER BY posts.id

    使用这个查询我们会得到LHS结果集中的重复的author信息,文章信息在右面。左边的author需要去重得到单一的POCO,文章需要为每个author收集成一个list。

    返回的集合事实上会比数据库返回的行有更少的项,所以relator callback需要能够hold back当前的author直到检测到一个新的author。

    为了支持这点,PetaPoco允许一个relator callback来返回null表示还没为当前记录准备好。为了清空最后的记录PetaPoco将在结果集末尾最后调用一次relator,为所有的参数传递null(但它只能做这个,如果relator在结果集中至少返回一次-relator不用检查null参数更简单了)

    看一下一对多的relator:

    复制代码
    class AuthorPostRelator
    {
        public author current;
        public author MapIt(author a, post p)
        {
            // Terminating call.  Since we can return null from this function
            // we need to be ready for PetaPoco to callback later with null
            // parameters
            if (a == null)
                return current;
    
            // Is this the same author as the current one we're processing
            if (current != null && current.id == a.id)
            {
                // Yes, just add this post to the current author's collection of posts
                current.posts.Add(p);
    
                // Return null to indicate we're not done with this author yet
                return null;
            }
    
            // This is a different author to the current one, or this is the 
            // first time through and we don't have an author yet
    
            // Save the current author
            var prev = current;
    
            // Setup the new current author
            current = a;
            current.posts = new List<post>();
            current.posts.Add(p);
    
            // Return the now populated previous author (or null if first time through)
            return prev;
        }
    }
    复制代码

    上面的注释很清楚的表明发生了什么-我们只是简单的保存author直到我们检测到一个新的然后添加文章列表到当前的author对象,这样来用:

    var authors = db.Fetch<author, post, author>(
        new AuthorPostRelator().MapIt,
        "SELECT * FROM authors LEFT JOIN posts ON posts.author = authors.id ORDER BY posts.id"
        );

    双向映射,映射两个以上的对象

    在上面的例子中,我要么把author映射到post要么添加post到author列表。relator没有理由做不到同时使用这两种方式创建的引用。我没有包括这个例子只是为了证明这是可行的但是你懂得。

    最后,上面的例子只是展示了如何联系两个对象。如果你连接更多的表你需要做更多复杂的工作,单只是上面例子的扩展。

    PetaPoco-Partial Record Updates

    http://www.toptensoftware.com/Articles/116/PetaPoco-Partial-Record-Updates

    默认情况下,PetaPoco更新记录的时候会更新所有的被映射到POCO属性的列。根据不同的使用情况,通常是可以的但也许无意中覆盖了已经被其他事务更新过的字段。

    例如:

    var u = user.SingleOrDefault("WHERE name=@0", username);
    u.last_login = DateTime.UtcNow;
    u.Update();

    问题是所有的字段都被更新了-用户名、邮件地址、密码,所有的都重写到数据库。如果只是更新last_login字段会更好一些。我们可以这样写:

    u.Update(new string[] { "last_login" });

    或类似的:

    db.Update<user>(u, new string[] { "last_login" });

    所有的Update方法现在都有一个新的重载,接受一个新参数,定义为IEnumerable<string>指定应该被更新的列的名称(不是属性).

    这是有用的除非跟踪哪些列需要更新非常痛苦。T4模板生成的POCO类现在可以自动跟踪修改的属性。为了启用它,Database.tt中有一个设置选项:

    TrackModifiedColumns = true;

    当设置为false的时候,POCO属性以旧方式实现:

    [Column] string title { get; set; }

    当为true时,它生成跟踪修改列的访问器方法;

    复制代码
    [Column] 
    public string title 
    { 
        get
        {
            return _title;
        }
        set
        {
            _title = value;
            MarkColumnModified("title");
        }
    }
    string _title;
    复制代码

    基本的Record类有一些新方法:

    复制代码
    private Dictionary<string,bool> ModifiedColumns;
    private void OnLoaded()
    {
        ModifiedColumns = new Dictionary<string,bool>();
    }
    protected void MarkColumnModified(string column_name)
    {
        if (ModifiedColumns!=null)
            ModifiedColumns[column_name]=true;
    }
    public int Update() 
    { 
        if (ModifiedColumns==null)
            return repo.Update(this); 
    
        int retv = repo.Update(this, ModifiedColumns.Keys);
        ModifiedColumns.Clear();
        return retv;
    }
    public void Save() 
    { 
        if (repo.IsNew(this))
            repo.Insert(this);
        else
            Update();
    }
    复制代码

    解释一下:

    1、OnLoaded是一个新方法,PetaPoco在从数据库填充任何POCO实现后都将立即调用它。

    2、MarkColumnsModified-简单的记录OnLoaded被调用后有值被更改的列名。

    3、执行Update时Update和Save已经更新为传递一个修改列的list给PetaPoco。

    有一点需要注意的,set访问器,它们标志了列被修改 实际上值并没有改变。这是故意的,有两个原因:

    1、它确保值无论如何确实被发送到数据库,帮助保持数据一致性。

    2、这意味着查询数据库不依赖于用户输入的数据。例如:如果两个用户使用同样的表单来改变他们的资料,一个改变了他们的邮件地址,另一个改变了他们的显示名称,均会导致数据库相同的update查询-数据库只能优化一次。

    Long Time No Post and PetaPoco v5

    http://www.toptensoftware.com/Articles/137/Long-Time-No-Post-and-PetaPoco-v5

    本文主要是V5版本的一些更新……实在没有力气翻译了。8-(

    转自:http://www.cnblogs.com/yanxiaodi/archive/2013/03/25/2978606.html#Announcing-PetaPoco

  • 相关阅读:
    matlab矩阵中如何去掉重复的行;如何找到相同的行,并找到其位置
    Coursera 机器学习 第9章(下) Recommender Systems 学习笔记
    机器学习基石笔记1——在何时可以使用机器学习(1)
    Coursera 机器学习 第9章(上) Anomaly Detection 学习笔记
    matlab安装过程的被要求的配置程序
    jdk环境变量配置
    Coursera 机器学习 第8章(下) Dimensionality Reduction 学习笔记
    Coursera 机器学习 第8章(上) Unsupervised Learning 学习笔记
    Coursera 机器学习 第7章 Support Vector Machines 学习笔记
    linux服务器---squid限制
  • 原文地址:https://www.cnblogs.com/james641/p/5984217.html
Copyright © 2011-2022 走看看