我有下面的SQL可以正常工作:
SELECT Message, CreateDate, AccountId, AlertTypeId
FROM dbo.Alerts
UNION
SELECT TOP (100) PERCENT Status, CreateDate, AccountId,
(SELECT 10 AS Expr1) AS AlertTypeId
FROM dbo.StatusUpdates
WHERE AccountId = PassedInParameter
ORDER BY CreateDate DESC
我正在尝试将其转换为LINQ,这不能正常工作:)显然,这里有很多错误 - 这只是一个粗略的开始。它没有考虑上面的临时列或order by
条件,并且泛型/返回类型歧义是我尝试理解两种不同的返回类型:
public List<T> GetSomething<T>(Int32 accountId)
{
List<T> result;
using (DataContext dc = _conn.GetContext())
{
IEnumerable<Alert> alerts = (from a in dc.Alerts
where a.AccountId == accountId
select a);
IEnumerable<StatusUpdate> updates = (from s in dc.StatusUpdates
where s.AccountId == accountId
select s);
IEnumerable<T> obj = alerts.Union(updates);
result = obj.ToList();
}
return result;
}
我遇到的问题是:
1)我在我的选择中处理两种不同的类型(警报和状态更新) 我不确定如何组合它们(或返回什么类型)。我猜这可能 用泛型来解决?
2)在我的SQL中,我有这样的代码:(SELECT 10 AS Expr1) AS AlertTypeId
,它将值10添加到临时列AlertTypeId(允许联合将其与Alert的实际列AlertTypeId匹配)。如何在LINQ中完成这样的临时列/我该怎么做?
感谢您的帮助。
修改--------------------------------- EDIT --------- ---------------------------------修改
好的,我还有点进一步。以下是我目前的情况。您会注意到我添加了一些逻辑来返回朋友关系的更新。我还将此作为类型IList
的通用方法,因为警报和更新必须是通用的才能达成一致。我在调用方法中传递StatusUpdate(下面进一步说明)。
public IList GetUpdatesByAccountId<T>(Int32 accountId)
{
List<Friend> friends = _friendRepository.GetFriendsByAccountId(accountId);
using (DataContext dc = _conn.GetContext())
{
// Get all the account ids related to this user
var friendAccountIds =
friends.Select(friend => friend.MyFriendsAccountId).Distinct();
friendAccountIds = friendAccountIds.Concat(new[] { accountId });
var updates =
dc.StatusUpdates.Where(s => s.AccountId.HasValue && friendAccountIds.Contains(s.AccountId.Value)).Select(
s => new { Alert = (Alert)null, StatusUpdate = s});
var alerts =
dc.Alerts.Where(a => a.AccountId == accountId).Select(
a => new {Alert = a, StatusUpdate = (StatusUpdate) null});
var obj = updates.Union(alerts).Take(100);
return obj.OrderByDescending(su => su.StatusUpdate.CreateDate).ToList();
}
}
而且,调用方法:
protected void LoadStatus()
{
repStatusUpdates.DataSource = _statusRepository
.GetUpdatesByAccountId<StatusUpdate>(_userSession.CurrentUser.AccountId);
repStatusUpdates.DataBind();
}
这里是我用来通过LINQ访问我的Alert和StatusUpdate表的存储库的接口:
public interface IAlertRepository
{
List<Alert> GetAlertsByAccountId(Int32 accountId);
void SaveAlert(Alert alert);
void DeleteAlert(Alert alert);
}
public interface IStatusUpdateRepository
{
StatusUpdate GetStatusUpdateById(Int32 statusUpdateId);
List<StatusUpdate> GetStatusUpdatesByAccountId(Int32 accountId);
List<StatusUpdate> GetFriendStatusUpdatesByAccountId(Int32 accountId, Boolean addPassedInAccount);
void SaveStatusUpdate(StatusUpdate statusUpdate);
List<StatusUpdate> GetTopNStatusUpdatesByAccountId(Int32 accountId, Int32 number);
List<StatusUpdate> GetTopNFriendStatusUpdatesByAccountId(Int32 accountId, Int32 number, Boolean addPassedInAccount);
}
当前问题:
1)当我编译这段代码时,我得到了这个奇怪的错误:
无法投射类型的对象 'System.Data.Linq.SqlClient.SqlNew'到 类型 'System.Data.Linq.SqlClient.SqlValue'。
我能找到的唯一读物是this link,虽然那里没有明确的解决方案(至少我可以说)。但是,如果上面的LINQ代码看起来不太好,也许你建议的任何内容都会导致此错误消失。
2)上面的代码仍未考虑原始SQL中的这一行:
(SELECT 10 AS Expr1) AS AlertTypeId
但这很小。
再次感谢您的帮助。
答案 0 :(得分:1)
试试这个(我将StatusUpdate转换为警报,如果这是不可接受的,您将不得不将警报转换为StatusUpdate,或者创建一个新类):
var alerts = (from a in dc.Alerts
where a.AccountId == accountId
select a);
var updates = (from s in dc.StatusUpdates
where s.AccountId == accountId
select s)
.OrderByDescending( x => x.CreateDate)
.Take(100)
.Select( x => new Alert
{
Message = x.Percent.ToString(),
CreateDate = x.CreateDate,
AccountId = x.AccountId,
AlertTypeId = 10 // Is this right?
}
);
var obj = alerts.Union(updates);
result = obj.ToList();
我选择最后一次的原因是您不必为您未使用的所有结果构建新警报。
这将为您提供警报列表。
在这种情况下使用泛型有点难以实现。例如,你不能这样做:
IQueryable alerts =(来自_alerts 其中a.AccountId == accountId 选择a);
因为它隐式地将a转换为类型T.即使您尝试限制T实现或继承的内容:
public List<T> GetSomething<T>(Int32 accountId) where T : IAlert// Interface that both StatusUpdates and IAlert implement
public List<T> GetSomething<T>(Int32 accountId) where T : Alert
public List<T> GetSomething<T>(Int32 accountId) where T : AlertBase // Base class for both Status and Alert
您仍会遇到问题,因为无法静态地确切知道T的类型,因此您无法知道它是否可以从Alert和StatusUpdate转换。
另一种方法是明确使用IAlert作为返回类型:
public List<IAlert> GetSomething(Int32 accountId)
使用IAlert:
public interface IAlert
{
int AccountId { get; set; }
int AlertTypeId { get; set; }
DateTime CreateDate { get; set; }
string Message { get; set; }
}
如果您同时使用Alert和StatusUpdate实现IAlert,则可以将其重写为:
IQueryable<IAlert> alerts = (from a in dc.Alerts
where a.AccountId == accountId
select a);
IQueryable<IAlert> updates = (from s in dc.StatusUpdates
where s.AccountId == accountId
select s)
.OrderByDescending( x => x.CreateDate)
.Take(100);
var obj = alerts.Union(updates);
result = obj.ToList();
这是我将采用的路径,而不是传递一些未知类型并尝试限制它实现或继承的内容,因为转换为该类型可能仍然无效。
答案 1 :(得分:0)
你只能接受相同类型序列的联合。您需要将警报和更新转换为常见类型的序列,然后进行联合。您可以使用匿名类型执行此操作。如果类型没有任何共同点,则特别有用。
//this is a hack and probably not what you would want to use.
var alerts =
from a in dc.Alerts
where a.AccountId == accountId
select new { Alert = a, StatusUpdate = (StatusUpdate)null };
var updates =
from s in dc.StatusUpdates
where s.AccountId == accountId
select new { Alert = (Alert)null, StatusUpdate = s };
//both are sequences of anonymous type with properties:
// Alert (of type Alert)
// StatusUpdate (of type StatusUpdate)
var obj = alerts.Union(updates);
如果您有共同的字段,除了包含已知字段外,您仍然使用匿名类型。
var alerts =
from a in dc.Alerts
where a.AccountId == accountId
select new
{
a.Message, //assuming is a string
Status = (string)null,
a.CreateDate,
a.AccountId,
a.AlertTypeId //assuming is an int
};
var updates =
(from s in dc.StatusUpdates
where s.AccountId == accountId
select new
{
Message = (string)null,
s.Status, //assuming is a string
s.CreateDate,
s.AccountId,
AlertTypeId = 10 //this should handle the "10 AS AlertTypeId" part
}).OrderByDescending(s => s.CreateDate);
var obj = alerts.Union(updates);
关键是两个匿名类型具有相同确切类型的完全相同的属性。然后你可以把他们两者结合起来。