使用实体映射实现SQL包装器

时间:2019-01-02 10:50:51

标签: c# sql-server

我试图在C#中为SQL Server实现包装。

没有包装程序的正常工作流程是使用datatable将数据提取到direct SQL query中,然后按名称将列映射到实体中。

但是,作为包装器,最好接受mapping function来描述哪一列映射到可枚举的哪些字段。

所以,像这样的东西:

public class UserInfo
{
    public string FirstName{ get; set; }
    public string LastName{ get; set; }
}
enumerableList = dbManager.Execute("** sql query **", /* some method to specify mapping */);

然后,可枚举将包含来自database的结果,该结果由execute方法映射。但是我不确定如何指定映射?

即使我这样做了,如何处理映射中每一列的不同数据类型呢?

2 个答案:

答案 0 :(得分:1)

如果我正确理解,您想要这样的东西:

public static List<T> ReadRows<T>(this SqlHelper sql, string query, SqlParameter[] 
parameters, Func<SqlDataReader, T> projection)
{
        var command = GetSqlCommand(query, CommandType.StoredProcedure, parameters);
        return sql.ExecuteReader(command, reader => reader.Select(projection).ToList());
}

并像这样使用:

var members = _unitOfWork.SqlHelper.ReadRows("spGetMembersByUserCompanies", parameters, _memberProjection);

readonly Func<SqlDataReader, MemberVm> _memberProjection = (r) => new MemberVm
    {
        InvitationId = r.Get<int?>("InvitationId"),
        UserName = r.Get<string>("UserName"),
        RoleName = r.Get<string>("RoleName"),
        InvitationStatus = (InvitationStatus)r.Get<int>("InvitationStatus"),
        LogoUrl = r.Get<string>("LogoUrl")
    };

这是我的一部分代码。希望它开始解决您的问题。

答案 1 :(得分:1)

从裸露的骨头上实现这种包装并不容易。但是有可能。 Github中有一个ADO包装器库:ADOWrapper

实现非常简单。

简短回答

  1. 如何指定列之间的映射? -使用Func
  2. 如何处理不同的数据类型?您可以编写扩展名

长期回答

使通用方法接受查询和Func委托(以及可选的第三个参数,以将查询参数作为字典传递)作为输入

    public ICollection<T> Execute<T>(string query, Func<IDataReader, T> map, IDictionary<string, object> parameters = null)
    {
        ICollection<T> collection = new List<T>();

        using (SqlConnection connection = CreateConnection())
        {
            connection.Open();

            using (SqlCommand command = CreateCommand(connection, query, parameters))
            {
                using (IDataReader reader = await command.ExecuteReader())
                {
                    while(reader.Read())
                    {
                        collection.Add(map.Invoke(reader));
                    }
                }
            }

            connection.Close();
        }

        return collection;
    }

AddParameter和CreateCommand的实现:

        private void AddParameter(IDbCommand command, string parameter, object value)
        {
            IDbDataParameter param = command.CreateParameter();
            param.ParameterName = parameter;
            param.Value = value;

            command.Parameters.Add(param);
        }

        private SqlCommand CreateCommand(SqlConnection connection, string query, 
            IDictionary<string, object> parameters = null)
        {
            SqlCommand command = connection.CreateCommand();
            command.CommandText = query;

            if(parameters != null && parameters.Count > 0)
            {
                foreach(KeyValuePair<string, object> parameter in parameters)
                {
                    AddParameter(command, parameter.Key, parameter.Value);
                }
            }

            return command;
        }

您可以这样调用方法:

public class UserInfo
{
    public string FirstName{ get; set; }
    public string LastName{ get; set; }
}
var enumerableList = manager.Execute("** query **",
    (reader) =>
    {
        return new UserInfo()
        {
            FirstName = reader.Get<string>("FirstName"),
            LastName = reader.Get<string>("LastName "),
        };
    })

Get方法使管理从列中获取的不同数据类型变得容易。但这不是内置方法。因此,您需要为Data Reader编写扩展名:

public static class DataReaderExtension
    {
        public static T Get<T>(this IDataReader reader, string column) where T : IComparable
        {
            try
            {
                int index = reader.GetOrdinal(column);

                if (!reader.IsDBNull(index))
                {
                    return (T)reader[index];
                }
            }
            catch (IndexOutOfRangeException) { throw new Exception($"Column, '{column}' not found."); }

            return default(T);
        }

        public static IEnumerable<string> GetColumns(this IDataReader reader)
        {
            IEnumerable<string> columns = new List<string>();
            if (reader != null && reader.FieldCount > 0)
            {
                columns = Enumerable.Range(0, reader.FieldCount)
                                    .Select(index => reader.GetName(index))
                                    .ToList();
            }

            return columns;
        }
    }