具有属性映射的Dapper [英] Dapper with Attributes mapping

查看:127
本文介绍了具有属性映射的Dapper的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试将Id字段与Column Attributes映射,但是由于某种原因,这似乎不起作用,我无法弄清原因。我设置了一个测试项目来演示我的尝试。

I try to map my Id fields with the Column Attributes but for some reason this doesn't seem to work and I can't figure out why. I set up a test project to demonstrate what I am trying.

首先,我得到了2个实体:

First, I got my 2 entities:

实体表1

using System.Data.Linq.Mapping;

namespace DapperTestProj
{
    public class Table1
    {
        [Column(Name = "Table1Id")]
        public int Id { get; set; }

        public string Column1 { get; set; }

        public string Column2 { get; set; }

        public Table2 Table2 { get; set; }

        public Table1()
        {
            Table2 = new Table2();
        }
    }
}

和实体Table2

using System.Data.Linq.Mapping;

namespace DapperTestProj
{
    public class Table2
    {
        [Column(Name = "Table2Id")]
        public int Id { get; set; }

        public string Column3 { get; set; }

        public string Column4 { get; set; }
    }
}

在我的数据库中,我有2个表,也称为表1和表2。除了Table1的列Table2Id以及Table1.Table2Id和Table2.Id之间的外键外,两个表的列均与实体相等。

In my database I got 2 tables, also named Table1 and Table2. Both tables got their columns named equal to the entities with the exception that Table1 has a column named Table2Id and there is also a foreign key between Table1.Table2Id and Table2.Id.

在两个表中每个都有1条记录,并且都获得了ID 2。

Also there is 1 record each in both tables and those got both the Id 2.

我尝试什么接下来是使用dapper执行查询,它应该返回Table1类型的对象。这可以工作,但是属性Table1.Id和Table1.Table2.Id都保持为0(默认整数)。我希望column属性可以映射Id字段,但显然这不是在隐藏。

What I try next is to execute a query with dapper and it should return a object of type Table1. This works, but both the property Table1.Id and Table1.Table2.Id remains 0 (default integer). I expect the column attributes would map the Id fields but clearly this isn't happing.

这是我在代码中执行的查询和映射:

This is the query and mapping I am executing in code:

private Table1 TestMethod(IDbConnection connection)
{
    var result = connection.Query<Table1, Table2, Table1>(
        @"SELECT 
             T1.Id as Table1Id, 
             T1.Column1 as Column1,
             T1.Column2 as Column2,
             T2.Id as Table2Id,
             T2.Column3 as Column3,
             T2.Column4 as Column4
          FROM Table1 T1 
          INNER JOIN Table2 T2 ON T1.Table2Id = T2.Id",
        (table1, table2) =>
            {
                table1.Table2 = table2;
                return table1;
            },
        splitOn: "Table2Id"
        ).SingleOrDefault();

    return result;
}

现在我可以将实体中的两个Id属性字段重命名为Table1Id和Table2Id但我更喜欢使用Id而不是使用更多逻辑代码,例如Table1.Id,而不是Table1.Table1Id。所以我想知道,是否可能我想要在这里?如果需要,怎么办?

Now I could rename the both Id property fields in the entities to Table1Id and Table2Id but I prefer Id instead cause of the more logic code like Table1.Id instead of Table1.Table1Id. So I was wondering, is it possible what I want here and if so, how?

我找到了这个主题:
手动映射具有类属性的列名称

使用Kaleb Pederson第一篇文章中的代码,可以在需要时使用FallBackTypeMapper类和ColumnAttributeTypeMapper类使用属性。所需要做的就是使用以下方法将所需的类添加到类型映射中:

And with the code in the first post of Kaleb Pederson it is possible to use attributes when needed with the FallBackTypeMapper class and the ColumnAttributeTypeMapper class. All that is needed is to add the required classes to the typemapping with:

SqlMapper.SetTypeMap(typeof(Table1), new ColumnAttributeTypeMapper<Table1>());
SqlMapper.SetTypeMap(typeof(Table2), new ColumnAttributeTypeMapper<Table2>());

但是对于许多实体来说,这个列表会持续很长时间。另外,您需要手动将每个类添加到列表中,我想知道是否可以使用Reflection自动完成此操作。我发现了一个能够获取所有类型的代码片段:

But with many entities this list will grow long. Also you need to add every class manually to the list and I was wondering if this could be done automaticly en more generic with Reflection. I found a code fragment that is able to get all the types:

        const string @namespace = "DapperTestProj.Entities";

        var types = from type in Assembly.GetExecutingAssembly().GetTypes()
                    where type.IsClass && type.Namespace == @namespace
                    select type;

然后遍历所有类型,我可以做到这一点,唯一的问题就是我现在是什么代码片段我是否需要或需要放在问号现在所在的地方?

And looping through all the types, I can do this, only problem I have now is what code fragment do I need to have or need to put on the place where the questionmarks are right now?

        typeList.ToList().ForEach(type => SqlMapper.SetTypeMap(type, 
                               new ColumnAttributeTypeMapper</*???*/>()));

编辑:

更多搜索后,我找到了最后一个问题的解决方案:

After more searching, I found the solution for my last problem:

        typeList.ToList().ForEach(type =>
            {
                var mapper = (SqlMapper.ITypeMap)Activator.CreateInstance(
                    typeof(ColumnAttributeTypeMapper<>)
                        .MakeGenericType(type));
                SqlMapper.SetTypeMap(type, mapper);
            });


推荐答案

为完成解决方案,我想分享

For the completion of the solution, I want to share the code I found and put together with those who are interested.

代替(ab)使用System.Data.Linq.Mapping.ColumnAttribute,可能更合逻辑(并且可能会保存,尽管微软将linq更改为sql ColumnAttribute类的机会很小)来创建我们自己的ColumnAttribute类:

Instead of (ab)using the System.Data.Linq.Mapping.ColumnAttribute, it might be more logic (and probably save, although the chance will be very small that Microsoft will change the linq to sql ColumnAttribute class) to create our own ColumnAttribute class:

ColumnAttribute。 cs

using System;

namespace DapperTestProj.DapperAttributeMapper //Maybe a better namespace here
{
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
    public class ColumnAttribute : Attribute
    {
        public string Name { get; set; }

        public ColumnAttribute(string name)
        {
            Name = name;
        }
    }
}

在我提到的主题中找到之前的FallBackTypeMapper和ColumnAttributeTypeMapper类:

Found in the topic I mentioned earlier, the FallBackTypeMapper and the ColumnAttributeTypeMapper classes:

FallBackTypeMapper.cs

using System;
using System.Collections.Generic;
using System.Reflection;
using Dapper;

namespace DapperTestProj.DapperAttributeMapper
{
    public class FallBackTypeMapper : SqlMapper.ITypeMap
    {
        private readonly IEnumerable<SqlMapper.ITypeMap> _mappers;

        public FallBackTypeMapper(IEnumerable<SqlMapper.ITypeMap> mappers)
        {
            _mappers = mappers;
        }

        public ConstructorInfo FindConstructor(string[] names, Type[] types)
        {
            foreach (var mapper in _mappers)
            {
                try
                {
                    var result = mapper.FindConstructor(names, types);

                    if (result != null)
                    {
                        return result;
                    }
                }
                catch (NotImplementedException nix)
                {
                    // the CustomPropertyTypeMap only supports a no-args
                    // constructor and throws a not implemented exception.
                    // to work around that, catch and ignore.
                }
            }
            return null;
        }

        public SqlMapper.IMemberMap GetConstructorParameter(ConstructorInfo constructor, string columnName)
        {
            foreach (var mapper in _mappers)
            {
                try
                {
                    var result = mapper.GetConstructorParameter(constructor, columnName);

                    if (result != null)
                    {
                        return result;
                    }
                }
                catch (NotImplementedException nix)
                {
                    // the CustomPropertyTypeMap only supports a no-args
                    // constructor and throws a not implemented exception.
                    // to work around that, catch and ignore.
                }
            }
            return null;
        }

        public SqlMapper.IMemberMap GetMember(string columnName)
        {
            foreach (var mapper in _mappers)
            {
                try
                {
                    var result = mapper.GetMember(columnName);

                    if (result != null)
                    {
                        return result;
                    }
                }
                catch (NotImplementedException nix)
                {
                    // the CustomPropertyTypeMap only supports a no-args
                    // constructor and throws a not implemented exception.
                    // to work around that, catch and ignore.
                }
            }
            return null;
        }
    }
}

ColumnAttributeTypeMapper.cs

using System.Linq;
using Dapper;

namespace DapperTestProj.DapperAttributeMapper
{
    public class ColumnAttributeTypeMapper<T> : FallBackTypeMapper
    {
        public ColumnAttributeTypeMapper()
            : base(new SqlMapper.ITypeMap[]
                    {
                        new CustomPropertyTypeMap(typeof(T),
                            (type, columnName) =>
                                type.GetProperties().FirstOrDefault(prop =>
                                    prop.GetCustomAttributes(false)
                                        .OfType<ColumnAttribute>()
                                        .Any(attribute => attribute.Name == columnName)
                            )
                        ),
                        new DefaultTypeMap(typeof(T)) 
                    })
        {
        }
    }
}

最后是 TypeMapper.cs 初始化映射。

using System;
using System.Linq;
using System.Reflection;
using Dapper;

namespace DapperTestProj.DapperAttributeMapper
{
    public static class TypeMapper
    {
        public static void Initialize(string @namespace)
        {
            var types = from assem in AppDomain.CurrentDomain.GetAssemblies().ToList()
                    from type in assem.GetTypes()
                    where type.IsClass && type.Namespace == @namespace
                    select type;

            types.ToList().ForEach(type =>
            {
                var mapper = (SqlMapper.ITypeMap)Activator
                    .CreateInstance(typeof(ColumnAttributeTypeMapper<>)
                                    .MakeGenericType(type));
                SqlMapper.SetTypeMap(type, mapper);
            });
        }
    }
}

在启动时,使用TypeMapper。初始化需要调用:

At start up, TypeMapper.Initialize needs to be called:

TypeMapper.Initialize("DapperTestProj.Entities");

您可以开始使用实体属性的属性

And you can start using attributes for the entity properties

using DapperTestProj.DapperAttributeMapper;

namespace DapperTestProj.Entities
{
    public class Table1
    {
        [Column("Table1Id")]
        public int Id { get; set; }

        public string Column1 { get; set; }

        public string Column2 { get; set; }

        public Table2 Table2 { get; set; }

        public Table1()
        {
            Table2 = new Table2();
        }
    }
}

这篇关于具有属性映射的Dapper的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆