我一直在使用实体框架很长一段时间,并且已经习惯使用它来将数据转储到类列表中。
我现在正在开发一个没有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这么长时间之后。我不得不相信有更好的方法吗?有吗?
答案 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");
}
}