如何在不使用实体框架时将存储过程结果返回到Class类中?

时间:2014-05-08 22:16:57

标签: c# tsql stored-procedures

我一直在使用实体框架很长一段时间,并且已经习惯使用它来将数据转储到类列表中。

我现在正在开发一个没有EF的旧项目,我没有时间将其转换为EF(我也不需要完全重写)。

我在那里虽然做了一些小的调整,需要从存储过程中撤回数据。我想将数据存储在Class of Class中,类是我自己创建的类。

我知道我可以使用DataReader并只读取传回的每条记录并创建一个Class项并将其添加到列表中。

                List<MyClass> myClassGuy = new List<MyClass>();
                dr = cmd.ExecuteReader(CommandBehavior.Default);

                while (dr.Read())
                {
                    MyClass myClassItemToAdd = new MyClass();
                    myClassItemToAdd.VarA = (dr, "VARA");
                    myClassItemToAdd.VarB = (dr, "VARB");
                    myClassItemToAdd.VarC = (dr, "VARC");
                    //etc
                    myClassGuy.Add(myClassItemToAdd);
                }

我知道我可以做到以上但是在使用EF这么长时间之后。我不得不相信有更好的方法吗?有吗?

7 个答案:

答案 0 :(得分:2)

我写了一大堆代码来处理这些东西,然后发现AutoMapper在处理SQL查询的结果时(通常)做得非常好:

// setup mapping (this is a once-per-execution thing)
if (AutoMapper.Mapper.FindTypeMapFor<IDataReader, MyData>() == null)
    AutoMapper.Mapper.CreateMap<IDataReader, MyData>();

// Read from the DataReader into a list
IList<MyClass> data = AutoMapper.Mapper.Map<IDataReader, IList<MyData>>(dr);

答案 1 :(得分:1)

EF的一大优点是它可以自动化实现存储在数据库中的对象的过程。

使用ADO.Net,这项工作落在你身上。您需要读出列值,如果合适的话将它们转换,并将它们放在对象的正确部分。

没有引入额外的框架(例如AutoMapper),没有比现在更多的方法。

答案 2 :(得分:0)

您应该编写自己的可重用方法,以便在处理下一个类似任务时更轻松。关于你的确切问题,不,没有更简单的解决方案,但没有什么可以阻止你尽可能地简化你未来的任务。

答案 3 :(得分:0)

你可以重构你所拥有的,一个DAL和SomeClass。

不使用EF的好处是您可以更好地控制应用程序的性能。你的马力有多大并不重要,你的骑师仍然必须轻松。

当然,如果您的应用是关键或一些琐碎的网页。

答案 4 :(得分:0)

如果您绝对无法使用ORM框架,那么您可以通过修改存储过程以返回XML流(这应该相当容易)来使代码更通用,并且在应用程序端,反序列化流进入物体。

例如,我可以定义以下数据访问方法,该方法采用泛型类型,存储过程的名称和任意数量的SqlParameter s(可选)并反序列化并返回T的列表。辅助方法Deserialize<T>接收XML流并将其反序列化为List<T>

public static IEnumerable<T> GetEntity<T>(string storedProcedureName, params SqlParameter[] parameters)
{
    try
    {
        using (SqlConnection connection =
            new SqlConnection("connectionString"))
        {
            connection.Open();
            SqlCommand command = new SqlCommand(storedProcedureName, connection);
            command.CommandType = System.Data.CommandType.StoredProcedure;

            if (parameters != null && parameters.Any())
            {
                command.Parameters.AddRange(parameters);
            }

            string result = (string)command.ExecuteScalar();

            return Deserialize<T>(result);
        }
    }
    catch (Exception ex)
    {
        // Handle the exception
        return (IEnumerable<T>)default(T);
    }
}

private static IEnumerable<T> Deserialize<T>(string xmlStream, params Type[] additionalTypes)
{
    XmlSerializer serializer = additionalTypes == null ? new XmlSerializer(typeof(List<T>))
        : new XmlSerializer(typeof(List<T>), additionalTypes);


    using (var reader = new XmlTextReader(new StringReader(xmlStream)))
    {
        if (!serializer.CanDeserialize(reader))
        {
            return (IEnumerable<T>)default(T);
        }

        return (IEnumerable<T>)serializer.Deserialize(reader);
    }
}

要使用它,我可以拥有像

这样简单的东西

IEnumerable<MyObject> myObjects = DataAccess.GetEntity<MyObject>("MyStoredProc");

存储过程如下所示:

SELECT t.Id as 'Id', t.Name as 'Name'
FROM MyTable t

FOR XML PATH('MyObject'), ROOT('ArrayOfMyObject')

唯一棘手的部分是确保列别名与对象上的属性匹配。您还需要将[XmlRoot("ArrayOfMyObject")]属性添加到MyObject类。

这看起来像很多代码但是如果你真的无法使用框架或库,那么这将是一条通向IMO的好方法。

答案 5 :(得分:0)

那有点依赖。您映射了多少属性 - 如果它只有少数属性,那么您当前的方法可能是最好的,尽管它最初很繁琐,但如果SP结果集发生变化,它仍然可以使维护变得容易。

如果我们再谈论从存储过程中返回的许多字段,那么你可以使用非常轻量级的Linq to SQL - 它是正式的&#34;死了&#34;但仍然适合这种事情,它可以映射到存储过程结果。或者像Rob Conery的Massive更轻松的东西,在700左右的地方是个不错的选择。

当然Automapper总是一种选择。

然而。如果由于某种原因你不能将L2S或任何第三方软件包引入遗留代码库,你可以使用一个简单的基于反射的映射器类,如下例所示。

鉴于此SQL:

create table MyRecords
(
    ID int identity,
    Name nvarchar(255),
    DateCreated datetime,
    IsSilly bit
)

Insert into MyRecords select 'John',getdate(),0
Insert into MyRecords select 'Andrew',getdate(),1
Insert into MyRecords select 'Steve',getdate(),0
Insert into MyRecords select 'Max',getdate(),1

然后这个控制台应用程序显示了将这种事情概括为多么简单;错误检查等,为简洁起见省略,并且它不支持多个结果集,但您可以了解需要多少代码。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;

namespace MiniORM
{
    //Attribute to map SQL result set field to class public instance property
    public class FieldInfo : Attribute
    {
        public string FieldName { get; set; }
        public Type DataType { get; set; }
    }

    public class MyRecord
    {
        [FieldInfo(DataType=typeof(int),FieldName="ID")]
        public int ID { get; set; }
        [FieldInfo(DataType = typeof(string), FieldName = "Name")]
        public string Name { get; set; }
        [FieldInfo(DataType = typeof(DateTime), FieldName = "DateCreated")]
        public DateTime DateCreated { get; set; }
        [FieldInfo(DataType = typeof(bool), FieldName = "IsSilly")]
        public bool IsSilly { get; set; }

        public override string ToString()
        {
            return string.Format("{0}-{1}-{2}-{3}", ID, Name, DateCreated, IsSilly);
        }
    }

    public class FieldInfoDBMapper<T> where T: class,new()
    {
        private readonly Dictionary<string,KeyValuePair<PropertyInfo,Type>> _mapping; 

        public FieldInfoDBMapper()
        {
            var t = typeof (T);
            _mapping = new Dictionary<string,KeyValuePair<PropertyInfo,Type>>();
            foreach (var pi in t.GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                var infos = pi.GetCustomAttributes(typeof(FieldInfo));
                if (infos.Any())
                {
                    var fieldInfo = (FieldInfo) infos.First();
                    _mapping.Add(fieldInfo.FieldName,new KeyValuePair<PropertyInfo, Type>(pi,fieldInfo.DataType));
                }
            }

        }
        public List<T> MapFromReader(IDataReader reader)
        {
            List<T> data = new List<T>();
            while (reader.Read())
            {
                T item = new T();
                foreach (var entry in _mapping)
                {
                    var value = Convert.ChangeType(reader[entry.Key],entry.Value.Value);
                    entry.Value.Key.SetValue(item,value);
                }
                data.Add(item);
            }
            return data;
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            List<MyRecord> records  =new List<MyRecord>();
            using (
                SqlConnection conn =
                    new SqlConnection("Your connection string here"))
            {
                conn.Open();
                using (SqlCommand comm = new SqlCommand("Select * from MyRecords", conn))
                {
                    var reader = comm.ExecuteReader();
                    var mapper = new FieldInfoDBMapper<MyRecord>();
                    records.AddRange(mapper.MapFromReader(reader));
                }
            }
            foreach (var record in records)
            {
                Console.WriteLine(record.ToString());
            }
            Console.ReadLine();
        }
    }
}

输出:

1-John-09/05/2014 00:39:37-False
2-Andrew-09/05/2014 00:39:37-True
3-Steve-09/05/2014 00:39:37-False
4-Max-09/05/2014 00:39:37-True

希望有所帮助。

答案 6 :(得分:0)

我有这样的东西的映射库。 https://www.nuget.org/packages/SprocMapper/

文档:https://github.com/gtaylor44/SprocMapper

public class MyClass
{
    string VarA;
    string VarB;
    string VarC;
}

public List<MyClass> GetResults()
{
    using (SqlConnection conn = new SqlConnection("YourConnection"))
    {
        return conn.Select().ExecuteReader<MyClass>(conn, "YourStoredProcedure");
    }
}