hakan648
جمعه 10 آذر 1391, 20:03 عصر
سلام
در حال تهیه یک فروم با MVC هستم و قسمت DAL رو با Entity Framework انجام میدم.
حالا برای لیست تاپیک ها و آخرین پست هر تاپیک به مشکل خوردم و موفق به نوشتن یک کوئری صحیح و کم حجم نشدم.
بهترین کوئری که نوشتم ، 170 خط کد T-Sql شد که شامل Join های فراوان و تو در تو بود!
این فروم از مدل های Category , Topic , Post , User تشکیل شده و برای نمایش لیست تاپیک ها هم چند ViewModel نوشتم.
و نکته ای که هست اینه که هر تاپیک شامل اطلاعات تاپیک مثل IsLocked و ارجاعی به User, Category , Posts دارد و متن و عنوان تاپیک و بقیه ی موارد در مدل Post دخیره میشه پس هر تاپیک حداق یک پست دارد که خود تاپیک است.
حالا مدل هایی که نوشتم رو قرار میدم ، امیدوارم با کمک شما یک کوئری مناسب بنویسم.
namespace Forum.DomainClasses
{
public class Category
{
public int CategoryId { get; set; }
// ...
public virtual IList<Topic> Topics { get; set; }
}
public class Topic
{
public int TopicId { get; set; }
public int CategoryId { get; set; }
public bool IsSolved { get; set; }
public bool IsLocked { get; set; }
public virtual User User { get; set; }
public virtual Category Category { get; set; }
public virtual IList<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime CreatedOn { get; set; }
// ...
public virtual User User { get; set; }
public virtual Topic Topic { get; set; }
}
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
public string DisplayName { get; set; }
// ...
public virtual IList<Topic> Topics { get; set; }
public virtual IList<Post> Posts { get; set; }
}
}
namespace Forum.Web.ViewModels
{
public class Topic
{
public int TopicId { get; set; }
public bool IsSolved { get; set; }
public bool IsLocked { get; set; }
public int PostCount { get; set; }
public ShortPost TopicPost { get; set; }
public ShortPost LastPost { get; set; }
}
public class ShortPost
{
public int? PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime? CreatedOn { get; set; }
public ShortUser User { get; set; }
}
public class ShortUser
{
public int UserId { get; set; }
public string UserName { get; set; }
public string DisplayName { get; set; }
}
}
=========
ویرایش1 :
کدی که خودم برای بازیابی تونستم بنویسم این بود:
List<Web.ViewModels.Topic> topics = db.Topics
.Where(x => x.CategoryId == categoryId)
.Select(x => new Topic
{
TopicId = x.TopicId,
IsSolved = x.IsSolved,
IsLocked = x.IsLocked,
PostCount = x.Posts.Count,
TopicPost = new ShortPost
{
Title = x.Posts.FirstOrDefault().Title,
Content = x.Posts.FirstOrDefault().Content,
CreatedOn = x.Posts.FirstOrDefault().CreatedOn,
User = new ShortUser
{
UserId = x.Posts.FirstOrDefault().User.UserId,
DisplayName = x.Posts.FirstOrDefault().User.DisplayName,
UserName = x.Posts.FirstOrDefault().User.UserName,
}
},
//LastPost = new ShortPost
//{
// PostId = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().PostId,
// Title = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().Title,
// Content = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().Content,
// CreatedOn = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().CreatedOn,
// User = new ShortUser
// {
// UserId = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().User.UserId,
// DisplayName = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().User.DisplayName,
// UserName = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().User.UserName,
// }
//},
})
.ToList();
خیلی کوئری کثیفی شده! فکر میکنم مشکلم سر اون FirstOrDefault ها هم باشه.
قسمت هایی که کامنت کردم ، باعث این خطا میشدند ( البته صرفا جهت اطلاع وگرنه که کلا کوئری باطله! :) )
The type 'Forum.Web.ViewModels.ShortPost' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.
و در نهایت کد T-Sql که از اجرای کوئری فوق بدست اومد ، کد زیر هست:
SELECT [Project1].[TopicId] AS [TopicId],
[Project1].[IsSolved] AS [IsSolved],
[Project1].[IsLocked] AS [IsLocked],
[Project1].[C1] AS [C1],
[Project1].[Title] AS [Title],
[Project1].[Content] AS [Content],
CAST([Project1].[CreatedOn] AS datetime2) AS [C2],
[Project1].[User_UserId] AS [User_UserId],
[Project1].[DisplayName] AS [DisplayName],
[Project1].[UserName] AS [UserName]
FROM (SELECT [Filter1].[TopicId] AS [TopicId],
[Filter1].[IsSolved] AS [IsSolved],
[Filter1].[IsLocked] AS [IsLocked],
[Limit1].[Title] AS [Title],
[Limit2].[Content] AS [Content],
[Limit3].[CreatedOn] AS [CreatedOn],
[Limit4].[User_UserId] AS [User_UserId],
[Extent7].[DisplayName] AS [DisplayName],
[Extent9].[UserName] AS [UserName],
(SELECT COUNT(1) AS [A1]
FROM [dbo].[Posts] AS [Extent10]
WHERE [Filter1].[TopicId] = [Extent10].[Topic_TopicId]) AS [C1]
FROM (SELECT [Extent1].[TopicId] AS [TopicId],
[Extent1].[IsSolved] AS [IsSolved],
[Extent1].[IsLocked] AS [IsLocked]
FROM [dbo].[Topics] AS [Extent1]
WHERE 3 = [Extent1].[CategoryId]) AS [Filter1]
OUTER APPLY (SELECT TOP (1) [Extent2].[Title] AS [Title]
FROM [dbo].[Posts] AS [Extent2]
WHERE [Filter1].[TopicId] = [Extent2].[Topic_TopicId]) AS [Limit1]
OUTER APPLY (SELECT TOP (1) [Extent3].[Content] AS [Content]
FROM [dbo].[Posts] AS [Extent3]
WHERE [Filter1].[TopicId] = [Extent3].[Topic_TopicId]) AS [Limit2]
OUTER APPLY (SELECT TOP (1) [Extent4].[CreatedOn] AS [CreatedOn]
FROM [dbo].[Posts] AS [Extent4]
WHERE [Filter1].[TopicId] = [Extent4].[Topic_TopicId]) AS [Limit3]
OUTER APPLY (SELECT TOP (1) [Extent5].[User_UserId] AS [User_UserId]
FROM [dbo].[Posts] AS [Extent5]
WHERE [Filter1].[TopicId] = [Extent5].[Topic_TopicId]) AS [Limit4]
OUTER APPLY (SELECT TOP (1) [Extent6].[User_UserId] AS [User_UserId]
FROM [dbo].[Posts] AS [Extent6]
WHERE [Filter1].[TopicId] = [Extent6].[Topic_TopicId]) AS [Limit5]
LEFT OUTER JOIN [dbo].[Users] AS [Extent7]
ON [Limit5].[User_UserId] = [Extent7].[UserId]
OUTER APPLY (SELECT TOP (1) [Extent8].[User_UserId] AS [User_UserId]
FROM [dbo].[Posts] AS [Extent8]
WHERE [Filter1].[TopicId] = [Extent8].[Topic_TopicId]) AS [Limit6]
LEFT OUTER JOIN [dbo].[Users] AS [Extent9]
ON [Limit6].[User_UserId] = [Extent9].[UserId]) AS [Project1]
========
باید در این کوئری ، اولین و آخرین پست تاپیک و اطلاعات کاربر هر یک از پست ها و هر چیزی که در مدل ViewModels.Topic تعریف شده، دریافت بشه!
در ضمن میدونم که حتما مشکلاتی در تعاریف مدل هام و ... وجود داره، اما در این تاپیک تنها به دنبال کوئری هستم.
واقعا نوشتن این کوئری برام شده مثل آرزو، واقعا ممنون میشم اگه کسی بتونه راهنمایی کنه.
پیشاپیش ممنون!
در حال تهیه یک فروم با MVC هستم و قسمت DAL رو با Entity Framework انجام میدم.
حالا برای لیست تاپیک ها و آخرین پست هر تاپیک به مشکل خوردم و موفق به نوشتن یک کوئری صحیح و کم حجم نشدم.
بهترین کوئری که نوشتم ، 170 خط کد T-Sql شد که شامل Join های فراوان و تو در تو بود!
این فروم از مدل های Category , Topic , Post , User تشکیل شده و برای نمایش لیست تاپیک ها هم چند ViewModel نوشتم.
و نکته ای که هست اینه که هر تاپیک شامل اطلاعات تاپیک مثل IsLocked و ارجاعی به User, Category , Posts دارد و متن و عنوان تاپیک و بقیه ی موارد در مدل Post دخیره میشه پس هر تاپیک حداق یک پست دارد که خود تاپیک است.
حالا مدل هایی که نوشتم رو قرار میدم ، امیدوارم با کمک شما یک کوئری مناسب بنویسم.
namespace Forum.DomainClasses
{
public class Category
{
public int CategoryId { get; set; }
// ...
public virtual IList<Topic> Topics { get; set; }
}
public class Topic
{
public int TopicId { get; set; }
public int CategoryId { get; set; }
public bool IsSolved { get; set; }
public bool IsLocked { get; set; }
public virtual User User { get; set; }
public virtual Category Category { get; set; }
public virtual IList<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime CreatedOn { get; set; }
// ...
public virtual User User { get; set; }
public virtual Topic Topic { get; set; }
}
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
public string DisplayName { get; set; }
// ...
public virtual IList<Topic> Topics { get; set; }
public virtual IList<Post> Posts { get; set; }
}
}
namespace Forum.Web.ViewModels
{
public class Topic
{
public int TopicId { get; set; }
public bool IsSolved { get; set; }
public bool IsLocked { get; set; }
public int PostCount { get; set; }
public ShortPost TopicPost { get; set; }
public ShortPost LastPost { get; set; }
}
public class ShortPost
{
public int? PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime? CreatedOn { get; set; }
public ShortUser User { get; set; }
}
public class ShortUser
{
public int UserId { get; set; }
public string UserName { get; set; }
public string DisplayName { get; set; }
}
}
=========
ویرایش1 :
کدی که خودم برای بازیابی تونستم بنویسم این بود:
List<Web.ViewModels.Topic> topics = db.Topics
.Where(x => x.CategoryId == categoryId)
.Select(x => new Topic
{
TopicId = x.TopicId,
IsSolved = x.IsSolved,
IsLocked = x.IsLocked,
PostCount = x.Posts.Count,
TopicPost = new ShortPost
{
Title = x.Posts.FirstOrDefault().Title,
Content = x.Posts.FirstOrDefault().Content,
CreatedOn = x.Posts.FirstOrDefault().CreatedOn,
User = new ShortUser
{
UserId = x.Posts.FirstOrDefault().User.UserId,
DisplayName = x.Posts.FirstOrDefault().User.DisplayName,
UserName = x.Posts.FirstOrDefault().User.UserName,
}
},
//LastPost = new ShortPost
//{
// PostId = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().PostId,
// Title = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().Title,
// Content = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().Content,
// CreatedOn = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().CreatedOn,
// User = new ShortUser
// {
// UserId = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().User.UserId,
// DisplayName = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().User.DisplayName,
// UserName = x.Posts.Where(p => x.Posts.Count > 1).OrderByDescending(p => p.CreatedOn).FirstOrDefault().User.UserName,
// }
//},
})
.ToList();
خیلی کوئری کثیفی شده! فکر میکنم مشکلم سر اون FirstOrDefault ها هم باشه.
قسمت هایی که کامنت کردم ، باعث این خطا میشدند ( البته صرفا جهت اطلاع وگرنه که کلا کوئری باطله! :) )
The type 'Forum.Web.ViewModels.ShortPost' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.
و در نهایت کد T-Sql که از اجرای کوئری فوق بدست اومد ، کد زیر هست:
SELECT [Project1].[TopicId] AS [TopicId],
[Project1].[IsSolved] AS [IsSolved],
[Project1].[IsLocked] AS [IsLocked],
[Project1].[C1] AS [C1],
[Project1].[Title] AS [Title],
[Project1].[Content] AS [Content],
CAST([Project1].[CreatedOn] AS datetime2) AS [C2],
[Project1].[User_UserId] AS [User_UserId],
[Project1].[DisplayName] AS [DisplayName],
[Project1].[UserName] AS [UserName]
FROM (SELECT [Filter1].[TopicId] AS [TopicId],
[Filter1].[IsSolved] AS [IsSolved],
[Filter1].[IsLocked] AS [IsLocked],
[Limit1].[Title] AS [Title],
[Limit2].[Content] AS [Content],
[Limit3].[CreatedOn] AS [CreatedOn],
[Limit4].[User_UserId] AS [User_UserId],
[Extent7].[DisplayName] AS [DisplayName],
[Extent9].[UserName] AS [UserName],
(SELECT COUNT(1) AS [A1]
FROM [dbo].[Posts] AS [Extent10]
WHERE [Filter1].[TopicId] = [Extent10].[Topic_TopicId]) AS [C1]
FROM (SELECT [Extent1].[TopicId] AS [TopicId],
[Extent1].[IsSolved] AS [IsSolved],
[Extent1].[IsLocked] AS [IsLocked]
FROM [dbo].[Topics] AS [Extent1]
WHERE 3 = [Extent1].[CategoryId]) AS [Filter1]
OUTER APPLY (SELECT TOP (1) [Extent2].[Title] AS [Title]
FROM [dbo].[Posts] AS [Extent2]
WHERE [Filter1].[TopicId] = [Extent2].[Topic_TopicId]) AS [Limit1]
OUTER APPLY (SELECT TOP (1) [Extent3].[Content] AS [Content]
FROM [dbo].[Posts] AS [Extent3]
WHERE [Filter1].[TopicId] = [Extent3].[Topic_TopicId]) AS [Limit2]
OUTER APPLY (SELECT TOP (1) [Extent4].[CreatedOn] AS [CreatedOn]
FROM [dbo].[Posts] AS [Extent4]
WHERE [Filter1].[TopicId] = [Extent4].[Topic_TopicId]) AS [Limit3]
OUTER APPLY (SELECT TOP (1) [Extent5].[User_UserId] AS [User_UserId]
FROM [dbo].[Posts] AS [Extent5]
WHERE [Filter1].[TopicId] = [Extent5].[Topic_TopicId]) AS [Limit4]
OUTER APPLY (SELECT TOP (1) [Extent6].[User_UserId] AS [User_UserId]
FROM [dbo].[Posts] AS [Extent6]
WHERE [Filter1].[TopicId] = [Extent6].[Topic_TopicId]) AS [Limit5]
LEFT OUTER JOIN [dbo].[Users] AS [Extent7]
ON [Limit5].[User_UserId] = [Extent7].[UserId]
OUTER APPLY (SELECT TOP (1) [Extent8].[User_UserId] AS [User_UserId]
FROM [dbo].[Posts] AS [Extent8]
WHERE [Filter1].[TopicId] = [Extent8].[Topic_TopicId]) AS [Limit6]
LEFT OUTER JOIN [dbo].[Users] AS [Extent9]
ON [Limit6].[User_UserId] = [Extent9].[UserId]) AS [Project1]
========
باید در این کوئری ، اولین و آخرین پست تاپیک و اطلاعات کاربر هر یک از پست ها و هر چیزی که در مدل ViewModels.Topic تعریف شده، دریافت بشه!
در ضمن میدونم که حتما مشکلاتی در تعاریف مدل هام و ... وجود داره، اما در این تاپیک تنها به دنبال کوئری هستم.
واقعا نوشتن این کوئری برام شده مثل آرزو، واقعا ممنون میشم اگه کسی بتونه راهنمایی کنه.
پیشاپیش ممنون!