orm – 如何使用Dapper.Rainbow(或可选地使用Dapper.Contrib)对具有导航属性的对象进行插入和更新

最近我开始研究Dapper.我正在测试它,并能够做基本的CRUD,我的意思是基本的是工作在一个类与这个结构:
public class Product {
    public int Id {get;set;}
    public string Name {get;set;}
}

现在我正在寻找一些可以更容易地进行插入和更新并发现Dapper.Rainbow的东西.我检查了它,并能够使用它来获取和插入对象,如上所述.我的问题是,当产品具有导航属性时,我不能在该字段上插入.所以如果我有这个:

public class Product {
    public int Id {get;set;}
    public string Name {get;set;}
    public ProductCategory Category {get;set;}
}

我将无法做到这一点:

// connection is a valid and opened connection            
 var db = TestDatabase.Init(connection,300);
 var newId = db.Products.Insert(newProduct);

因为这个原因:

The member Category of type ProductCategory  cannot be used as a parameter value

如果我用类型int(数据库中相同的数据类型)替换类别,则可以解决问题.但是,如果我这样做,我将无法使用其类别信息查询产品,而不仅仅是(category)Id.

所以没有诉诸原始的Dapper,我如何使用导航属性的类进行插入和更新?我希望我可以执行以下操作,并告诉Dapper.Rainbow在插入或更新时忽略类别.

public class Product {
    public int Id {get;set;}
    public string Name {get;set;}
    public ProductCategory Category {get;set;}
    public int CategoryId {get;set;} // this will be the same field name in the database
}

这个场景是可以使用NHibernate,我可以有一个代理对象的类别并将其分配给产品并保存,并且映射工作完美.但是我很想使用Dapper,这就是为什么我正在探索,想要学习如何做这样的事情.

解决方法

不用Dapper.Rainbow

这是不可能的Dapper.Rainbow在其当前的形式,但my pull request in github使这成为可能.

我很惊讶,没有人建议使用Dapper.Contrib.我知道我问Rainbow是否有功能.但是我没想到没有人会注意到这个声明(特别是粗体字):

Now I was looking for something that would make it easier to do
inserts and updates
and found Dapper.Rainbow. I checked it out and was
able to use it to get and insert objects as described above. My
problem is that when Product has a navigation property I can’t do an
insert on that field
.

…并提出一个替代方案,一个已经在Dapper库中的解决方案.我想我应该更清楚我的问题,并明确询问一个解决方案是否存在于整个Dapper library that is in github的某个地方.所以在更多的挖掘图书馆后,我发现有一个支持我的问题.

Dapper.Contrib的路径

所有这些都与我的项目和彩虹工作良好,直到我需要更多的工作.我有一些表中有很多字段.如果我只是给Rainbow我的对象,那么它将对所有的字段进行更新,这不是很好.但是,这并不意味着我很快就跳出船,回到NH.所以在我实现自己的变更跟踪之前,我不想重新发明,特别是如果有人已经做了一个很好的工作,我已经在7月2日上网了.这个线程证实了我的知识,Rainbow不支持更改跟踪,但是另一个野兽,它被称为Dapper.Contrib.所以我开始尝试了.

所以我们再见面

The member Category of type ProductCategory cannot be used as a
parameter value

我和彩虹有同样的问题. Contrib不支持导航属性!我开始感觉到我正在用Dapper浪费我的时间,而且我所追求的表现只会是一厢情愿.直到…

WriteAttribute来到救援…

该类生活在Dapper.Contrib项目中包含的sqlMapperExtensions.cs文件中.我没有找到关于这个课程的任何文件,也没有任何意见可以很容易找到,并且对我说话,并说嘿,我是你正在寻找的.当我按照上面所述放置彩虹时,我偶然发现.

这个类的用法和我用IgnorePropertyAttribute一样,它是一个属性,你可以用它来装饰你的类的属性.您应该使用此属性来装饰任何不需要包含在Dapper创建的sql中的属性.所以在我的例子中,我告诉Dapper排除我需要做的类别字段:

public class Product {
    public int Id {get;set;}

    public string Name {get;set;}

    [Write(false)] // tell Dapper to exclude this field from the sql
    public ProductCategory Category {get;set;}

    public int CategoryId {get;set;}
}

我快到了

记住,我去Contrib的原因是因为更改跟踪功能. This SO thread,我上面提到的相同的链接指出,要进行更改跟踪,您需要为您的课程提供一个界面,并与Contrib一起使用.所以对于我的示例类,我需要有:

public interface IProduct {
    int Id {get;set;}
    string Name {get;set;}
    ProductCategory Category {get;set;}
    int Category {get;set;}
}

// and implement it on my Product class
public class Product : IProduct {
    public int Id {get;set;}

    public string Name {get;set;}

    [Write(false)]
    public ProductCategory Category {get;set;}

    int Category {get;set;}
}

我以为是,几乎!你可能会问我为什么需要在我的界面中定义类别,如果Dapper根本不关心它.其实这只会造成一个问题,我会解决的问题.

在我的具体情况下,有时我需要在“类别”字段上工作,同时保留“产品”对象的更改跟踪.为了保持跟踪功能,应该使用如下所示的接口类型来提供get call:

var product = connection.Get<IProduct>(id);

并且通过该调用,如果我不在界面中定义它,我将无法访问类别字段.但是如果我在我的界面中定义它,那么我会得到一个熟悉的错误

The member {member} of type {type} cannot be used as a parameter
value.

真的呢请停止.

判决

不需要担心,通过装饰接口成员,就像我们为该类做的一样,这个容易解决.所以使一切工作的最终配置应该是:

public interface IProduct {
    // I will not discuss here what this attribute does
    // as this is documented already in the github source.
    // Just take note that this is needed,// both here and in the implementing class.
    [Key]
    int Id {get;set;}

    string Name {get;set;}

    [Write(false)]
    ProductCategory Category {get;set;}

    int Category {get;set;}
}

// and implement it on my Product class
public class Product : IProduct {
    [Key]        
    public int Id {get;set;}

    public string Name {get;set;}

    [Write(false)]
    public ProductCategory Category {get;set;}

    int Category {get;set;}
}

如果您喜欢使用具有更改跟踪功能的Contrib,则可以使用此方法.如果你想和Rainbow一起工作,并且像导航属性一样有问题,那么你可以在play with my pull request.它的工作方式与WriteAttribute一样,只能与Rainbow一起使用.

如果您不是使用属性装饰课程的粉丝,则扩展项目不适合您.我知道还有另一个扩展项目可以让你做某种流畅的配置,但这并不是与github中的Dapper库(不包括在内).我的偏好是与核心图书馆合作,导致我调查整个图书馆,看看是否已经存在,或者是否可以改进以满足我的需要.这就是我在这里所做的和解释,对于彩虹和Contrib.

我希望这个贡献,我添加的非常简单的类,我显示的配置提示,以及引导我的场景,将会帮助将来有人使用Dapper,并且将有类似的设置.此外,这个答案将教育开发者更多的是Dapper可以做什么和不能做的.这个伟大的工具叫做Dapper deserves a better wiki,我希望这里的这个答案/文章有助于甚至在一个小的方式.

**如果我在这里写的内容已经写在某个地方,那么我在两周的时间内没有发现我一直在等待一个答案,那么我会很乐意让任何人联系我.现在已经有两个星期了,29个人看了我的问题没有建议任何链接解决方案,所以我认为我在这里分享的信息是新的Dapper *

相关文章

最近看了一下学习资料,感觉进制转换其实还是挺有意思的,尤...
/*HashSet 基本操作 * --set:元素是无序的,存入和取出顺序不...
/*list 基本操作 * * List a=new List(); * 增 * a.add(inde...
/* * 内部类 * */ 1 class OutClass{ 2 //定义外部类的成员变...
集合的操作Iterator、Collection、Set和HashSet关系Iterator...
接口中常量的修饰关键字:public,static,final(常量)函数...