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

von*_* v. 13 orm dapper dapper-rainbow micro-orm

我刚开始研究Dapper.我正在测试它并能够做基本的CRUD,我的基本意思是在这个结构的类上工作:

public class Product {
    public int Id {get;set;}
    public string Name {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

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

public class Product {
    public int Id {get;set;}
    public string Name {get;set;}
    public ProductCategory Category {get;set;}
}
Run Code Online (Sandbox Code Playgroud)

我将无法做到这一点:

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

因为这个原因:

The member Category of type ProductCategory  cannot be used as a parameter value
Run Code Online (Sandbox Code Playgroud)

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

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

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
}
Run Code Online (Sandbox Code Playgroud)

使用NHibernate可以实现这种情况,我可以在其中拥有代理对象Category并将其分配Product并保存,映射完美运行.但我很乐意使用Dapper,这就是我探索的原因,并希望了解如何做到这一点.

von*_* v. 23

不是用Dapper.Rainbow

Dapper.Rainbow在目前的形式下是不可能的,但是我在github中的pull请求使这成为可能.

我很惊讶没有人建议使用Dapper.Contrib.我知道我问过功能是否在Rainbow中.但我没想到会有人注意到这句话(特别是粗体文字):

现在我正在寻找能够更容易地进行插入和更新的东西,并找到了Dapper.Rainbow.我检查了它,并能够使用它来获取和插入对象,如上所述.我的 问题是,当Product具有导航属性时,我无法在该字段上执行插入操作.

...并建议一个替代方案,一个已经在Dapper库中的解决方案.我想我应该更清楚我的问题并明确询问是否存在一个解决方案存在于整个Dapper库中的github中.因此,在对图书馆进行更多挖掘之后,我发现我的问题得到了支持.

Dapper.Contrib的路径

一切都与我的项目很好地合作,Rainbow直到我需要更多的东西.我有一些表中有很多字段.如果我只是向Rainbow提供我的对象,那么它将对所有字段进行更新,这并不是一件好事.但这并不能让我迅速跳出船并返回NH.所以在我实现自己之前change tracking,我不想重新发明轮子,特别是如果有人已经做好了工作,我用Google搜索并找到了这个SO线程.该线程证实了我的知识,Rainbow它不支持更改跟踪,但还有另一个野兽可以执行并且它被调用Dapper.Contrib.所以我开始尝试它.

所以我们再见面

ProductCategory类型的成员类别不能用作参数值

我遇到了和以前一样的问题Rainbow.Contrib不支持导航属性!?我开始觉得我在浪费时间Dapper,而且我提供的表现,我非常喜欢,只会是一厢情愿的想法.直到...

WriteAttribute,来救援......

此类存在于项目中SqlMapperExtensions.cs包含的文件中Dapper.Contrib.我没有找到关于这个类的任何文档,也没有任何评论可以让它很容易被发现并对我大喊大叫hey I'm the one you're looking for.如上所述,当我将Rainbow放在一边时,我偶然发现了这一点.

这个类的用法与我所做的一样IgnorePropertyAttribute,它是一个属性,你可以用你的类来装饰你的类.你应该装点,与此属性,你不希望包含在任何财产sqlDapper创造.所以在我的例子中,让我告诉Dapper排除Category我需要这样做的字段:

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;}
}
Run Code Online (Sandbox Code Playgroud)

我快到了

请记住,我选择的原因Contrib是因为change tracking功能.这个SO线程,我在上面给出的链接,指出要进行更改跟踪,您需要为您的类创建一个接口并使用它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;}
}
Run Code Online (Sandbox Code Playgroud)

我以为就是这样,差不多!您可能会问我为什么需要Category在界面中定义Dapper它是否完全不关心它.事实上,这只会导致一个问题,这个问题我会解决.

在我的特定场景中,有时我需要在该Category字段上工作,同时保持Product对象的更改跟踪.为了保持跟踪能力,get调用应该使用如下接口类型:

var product = connection.Get<IProduct>(id);
Run Code Online (Sandbox Code Playgroud)

并与呼叫我能够访问Category现场,如果我不会在我的接口定义.但是,如果我在界面中定义它,那么我会得到熟悉的错误

{type}类型的成员{member}不能用作参数值.

真的又来了?请停下来.

判决

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

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;}
}
Run Code Online (Sandbox Code Playgroud)

如果您愿意使用Contrib具有该change tracking功能的方法,则可以使用此方法.如果您想Rainbow使用导航属性并且遇到导航属性问题,那么您可以使用我的拉取请求.它的工作方式WriteAttribute与其唯一的工作方式相同Rainbow.

如果您不是使用属性装饰类的粉丝,则两个扩展项目都不适合您.我知道有另一部分的项目,将让你做某种流畅型的配置,但这并不能与(不包括作为一个核心部分)去Dapper是在github上图书馆.我的偏好,只是与核心库一起工作,让我调查整个库,看看是否已经存在,或者是否可以改进以满足我的需求.这就是我所做的,并解释在这里,对于RainbowContrib.

我希望这个贡献,我添加的非常简单的类,我展示的配置提示,以及引导我这些的场景,将帮助将来希望使用Dapper的人,并且我将拥有类似的设置.此外,这个答案将教育开发者更多的Dapper可以做什么和不能做什么.这个伟大的工具称为Dapper 值得更好的wiki,我即使在一个小的方式,使在这里帮助希望这个答案/条.


**如果我在这里写的东西已经写在某个地方,我在两周的时间内没有找到我一直在等待答案,那么我会很高兴有人把我联系起来.现在已经两周了,看了我的问题的29个人没有提出任何链接或解决方案所以我认为我在这里分享的信息对于Dapper来说是新的*:)

注意:我修改了我的问题标题,以便其他人可以看到这个问题的潜在解决方案.新标题基于我获得的关于Dapper的新知识.

  • 非常好,@ von,谢谢,并祝贺这项伟大的研究!我想我们大多数人都会错过一些简单的文档,而不仅仅是"查看测试".这告诉我没有像你的Q/A那样.谢啦! (3认同)