返回匿名types结果?
使用下面的简单示例,使用Linq to SQL返回多个表的结果的最佳方法是什么?
说我有两个表:
Dogs: Name, Age, BreedId Breeds: BreedId, BreedName
我想把所有的狗都BreedName
他们的BreedName
。 我应该让所有的狗使用这样的东西没有问题:
public IQueryable<Dog> GetDogs() { var db = new DogDataContext(ConnectString); var result = from d in db.Dogs join b in db.Breeds on d.BreedId equals b.BreedId select d; return result; }
但是,如果我想要品种的狗,试试这个我有问题:
public IQueryable<Dog> GetDogsWithBreedNames() { var db = new DogDataContext(ConnectString); var result = from d in db.Dogs join b in db.Breeds on d.BreedId equals b.BreedId select new { Name = d.Name, BreedName = b.BreedName }; return result; }
现在我意识到,编译器不会让我返回一组匿名types,因为它期待狗,但有没有办法返回这个,而不必创build一个自定义types? 或者我必须为DogsWithBreedNames
创build自己的类并在select中指定该types? 还是有另一种更简单的方法?
我倾向于这种模式:
public class DogWithBreed { public Dog Dog { get; set; } public string BreedName { get; set; } } public IQueryable<DogWithBreed> GetDogsWithBreedNames() { var db = new DogDataContext(ConnectString); var result = from d in db.Dogs join b in db.Breeds on d.BreedId equals b.BreedId select new DogWithBreed() { Dog = d, BreedName = b.BreedName }; return result; }
这意味着你有一个额外的类,但是它的代码快速简单,易于扩展,可重用和types安全。
你可以返回匿名types, 但实际上并不漂亮 。
在这种情况下,我认为创build适当的types会好得多。 如果只是从包含该方法的types中使用,则使其成为嵌套types。
就个人而言,我希望C#获得“命名匿名types” – 即与匿名types相同的行为,但名称和属性声明,但就是这样。
编辑:其他人build议返回的狗,然后通过属性path访问品种名称等这是一个非常合理的方法,但IME导致的情况下,你已经做了一个特定的方式查询,因为你想要的数据使用 – 当你只是返回IEnumerable<Dog>
时,元信息会丢失 – 查询可能由于某些加载选项等而期望您使用(说) Breed
而不是Owner
,但是如果您忘记了这一点并开始使用其他属性,你的应用程序可能会工作,但不像你最初设想的那样有效。 当然,我可以说垃圾,或者过度优化等等。
只是为了增加我的两分钱的价值:-)我最近学会了一种处理匿名对象的方法。 它只能在面向.NET 4框架时使用,并且只有在添加对System.Web.dll的引用时才可以使用,但这很简单:
... using System.Web.Routing; ... class Program { static void Main(string[] args) { object anonymous = CallMethodThatReturnsObjectOfAnonymousType(); //WHAT DO I DO WITH THIS? //I know! I'll use a RouteValueDictionary from System.Web.dll RouteValueDictionary rvd = new RouteValueDictionary(anonymous); Console.WriteLine("Hello, my name is {0} and I am a {1}", rvd["Name"], rvd["Occupation"]); } private static object CallMethodThatReturnsObjectOfAnonymousType() { return new { Id = 1, Name = "Peter Perhac", Occupation = "Software Developer" }; } }
为了能够添加对System.Web.dll的引用,您必须遵循rushonerok的build议 :确保您的[项目]目标框架是“.NET Framework 4”而不是“.NET Framework 4客户端configuration文件”。
不,你不能通过一些欺骗手段返回匿名types。
如果你不使用C#,你会find什么(返回多个数据,没有具体types)被称为元组。
有很多C#元组实现,使用这里显示的一个,你的代码会像这样工作。
public IEnumerable<Tuple<Dog,Breed>> GetDogsWithBreedNames() { var db = new DogDataContext(ConnectString); var result = from d in db.Dogs join b in db.Breeds on d.BreedId equals b.BreedId select new Tuple<Dog,Breed>(d, b); return result; }
并在呼叫站点:
void main() { IEnumerable<Tuple<Dog,Breed>> dogs = GetDogsWithBreedNames(); foreach(Tuple<Dog,Breed> tdog in dogs) { Console.WriteLine("Dog {0} {1}", tdog.param1.Name, tdog.param2.BreedName); } }
您必须首先使用ToList()
方法从数据库中获取行,然后select项作为类。 尝试这个:
public partial class Dog { public string BreedName { get; set; }} List<Dog> GetDogsWithBreedNames(){ var db = new DogDataContext(ConnectString); var result = (from d in db.Dogs join b in db.Breeds on d.BreedId equals b.BreedId select new { Name = d.Name, BreedName = b.BreedName }).ToList() .Select(x=> new Dog{ Name = x.Name, BreedName = x.BreedName, }).ToList(); return result;}
所以,诀窍是第一个ToList()
。 立即进行查询并从数据库中获取数据。 第二个技巧是select项目并使用对象初始值设定项目来加载项目来生成新对象。
希望这可以帮助。
你可以做这样的事情:
public System.Collections.IEnumerable GetDogsWithBreedNames() { var db = new DogDataContext(ConnectString); var result = from d in db.Dogs join b in db.Breeds on d.BreedId equals b.BreedId select new { Name = d.Name, BreedName = b.BreedName }; return result.ToList(); }
现在我意识到,编译器不会让我返回一组匿名types,因为它期待狗,但有没有办法返回这个,而不必创build一个自定义types?
使用use 对象返回匿名types列表,而不创build自定义types。 这将工作没有编译器错误(在.net 4.0中)。 我将列表返回给客户端,然后在JavaScript上进行parsing:
public object GetDogsWithBreedNames() { var db = new DogDataContext(ConnectString); var result = from d in db.Dogs join b in db.Breeds on d.BreedId equals b.BreedId select new { Name = d.Name, BreedName = b.BreedName }; return result; }
只要select狗,然后使用dog.Breed.BreedName
,这应该工作正常。
如果你有很多狗,使用DataLoadOptions.LoadWith来减less数据库调用的次数。
你不能直接返回匿名types,但你可以循环他们通过你的通用方法。 所以大部分的LINQ扩展方法。 那里没有魔法,看起来他们会返回匿名types。 如果参数是匿名的,结果也可以是匿名的。
var result = Repeat(new { Name = "Foo Bar", Age = 100 }, 10); private static IEnumerable<TResult> Repeat<TResult>(TResult element, int count) { for(int i=0; i<count; i++) { yield return element; } }
下面是一个基于原始问题代码的例子:
var result = GetDogsWithBreedNames((Name, BreedName) => new {Name, BreedName }); public static IQueryable<TResult> GetDogsWithBreedNames<TResult>(Func<object, object, TResult> creator) { var db = new DogDataContext(ConnectString); var result = from d in db.Dogs join b in db.Breeds on d.BreedId equals b.BreedId select creator(d.Name, b.BreedName); return result; }
如果你在数据库中有一个关于在BreedId上设置关键限制的关系,你不知道吗?
DBML关系映射userfiles/image/relationship.png
所以我现在可以打电话给:
internal Album GetAlbum(int albumId) { return Albums.SingleOrDefault(a => a.AlbumID == albumId); }
在代码中调用:
var album = GetAlbum(1); foreach (Photo photo in album.Photos) { [...] }
所以在你的实例中,你会打电话像dog.Breed.BreedName – 正如我所说,这依赖于你的数据库与这些关系设置。
正如其他人所说,DataLoadOptions将有助于减less数据库调用,如果这是一个问题。
那么,如果你回来狗,你会做:
public IQueryable<Dog> GetDogsWithBreedNames() { var db = new DogDataContext(ConnectString); return from d in db.Dogs join b in db.Breeds on d.BreedId equals b.BreedId select d; }
如果您想要Breed热切加载而不是延迟加载,只需使用适当的DataLoadOptions构造即可。
Dog
表中的BreedId
显然是Breed
表中相应行的外键。 如果您的数据库设置正确,LINQ to SQL应自动在两个表之间创build一个关联。 由此产生的狗类将有一个品种属性,而品种类应该有一个狗的集合。 这样设置,你仍然可以返回IEnumerable<Dog>
,它是一个包含品种属性的对象。 唯一需要注意的是,你需要预先加载品种对象以及查询中的狗对象,以便在数据上下文被处理后访问它们,并且(如另一个海报所build议的)在集合上执行一个方法,查询立即执行(ToArray在这种情况下):
public IEnumerable<Dog> GetDogs() { using (var db = new DogDataContext(ConnectString)) { db.LoadOptions.LoadWith<Dog>(i => i.Breed); return db.Dogs.ToArray(); } }
然后访问每只狗的品种是微不足道的:
foreach (var dog in GetDogs()) { Console.WriteLine("Dog's Name: {0}", dog.Name); Console.WriteLine("Dog's Breed: {0}", dog.Breed.Name); }
如果主要想法是使发送到数据库服务器的SQL select语句只有必需的字段,而不是所有的实体字段,那么你可以这样做:
public class Class1 { public IList<Car> getCarsByProjectionOnSmallNumberOfProperties() { try { //Get the SQL Context: CompanyPossessionsDAL.POCOContext.CompanyPossessionsContext dbContext = new CompanyPossessionsDAL.POCOContext.CompanyPossessionsContext(); //Specify the Context of your main entity eg Car: var oDBQuery = dbContext.Set<Car>(); //Project on some of its fields, so the created select statment that is // sent to the database server, will have only the required fields By making a new anonymouse type var queryProjectedOnSmallSetOfProperties = from x in oDBQuery select new { x.carNo, x.eName, x.aName }; //Convert the anonymouse type back to the main entity eg Car var queryConvertAnonymousToOriginal = from x in queryProjectedOnSmallSetOfProperties select new Car { carNo = x.carNo, eName = x.eName, aName = x.aName }; //return the IList<Car> that is wanted var lst = queryConvertAnonymousToOriginal.ToList(); return lst; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.ToString()); throw; } } }
在C#7中,您现在可以使用元组!!这样就不需要创build类来返回结果。
这是一个示例代码:
public List<(string Name, string BreedName)> GetDogsWithBreedNames() { var db = new DogDataContext(ConnectString); var result = from d in db.Dogs join b in db.Breeds on d.BreedId equals b.BreedId select new { Name = d.Name, BreedName = b.BreedName }.ToList(); return result.Select(r => (r.Name, r.BreedName)).ToList(); }
您可能需要安装System.ValueTuple nuget包。