2010-02-06 72 views
20

我對Linq to SQL和Lazy加載特性非常感興趣。在我的項目中,我使用AutoMapper將數據庫模型映射到域模型(從DB_RoleInfoDO_RoleInfo)。在我的倉庫代碼如下:AutoMapper是否支持Linq?

public DO_RoleInfo SelectByKey(Guid Key) 
    { 
     return SelectAll().Where(x => x.Id == Key).SingleOrDefault(); 
    } 

    public IQueryable<DO_RoleInfo> SelectAll() 
    { 
     Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>(); 
     return from role in _ctx.DB_RoleInfo 
       select Mapper.Map<DB_RoleInfo, DO_RoleInfo>(role); 
    } 

SelectAll方法運行良好,但是當我打電話SelectByKey,我得到的錯誤:

Method 「RealMVC.Data.DO_RoleInfo MapDB_RoleInfo,DO_RoleInfo」 could not translate to SQL.

難道Automapper不完全支持LINQ的?

相反Automapper的,我想下面的手動映射代碼:

public IQueryable<DO_RoleInfo> SelectAll() 
{ 
    return from role in _ctx.DB_RoleInfo 
    select new DO_RoleInfo 
    { 
     Id = role.id, 
     name = role.name, 
     code = role.code 
    }; 
} 

此方法我想它的方式。

+0

只是除了您的更新我的筆記回答:第二個版本的工作原理是因爲Linq到SQL已經*知道*你已經做了一個不可逆轉的預測; 「SelectByKey」實際上只是將Linq用於對象。如果您檢查正在生成的實際查詢,我認爲您會發現它仍然從數據庫中選擇所有實體,這相當於使用「ToList()」,然後過濾結果列表。 – Aaronaught 2010-02-06 15:33:57

+2

這不是說AutoMapper支持LINQ。這是LINQ to SQL不支持AutoMapper。 LINQ to SQL查詢提供程序查看錶達式樹以確定如何生成SQL查詢。當它到達Mapper.Map部分時,它不知道如何生成SQL。 – 2010-02-07 18:11:15

回答

23

改變你的第二個功能是:

public IEnumerable<DO_RoleInfo> SelectAll() 
{ 
    Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>(); 
    return from role in _ctx.DB_RoleInfo.ToList() 
      select Mapper.Map<DB_RoleInfo, DO_RoleInfo>(role); 
} 

AutoMapper只是罰款的LINQ到SQL,但它不能爲遞延查詢的一部分來執行。在您的Linq查詢的末尾添加ToList()會導致它立即評估結果,而不是嘗試將AutoMapper段作爲查詢的一部分進行翻譯。


澄清

延遲執行「懶加載」)的概念沒有任何意義,一旦你已經改變了結果類型的東西,這不是一個數據實體。考慮這兩個類:

public class DB_RoleInfo 
{ 
    public int ID { get; set; } 
    public string Name { get; set; } 
} 

public class DO_RoleInfo 
{ 
    public Role Role { get; set; } // Enumeration type 
} 

現在考慮下面的映射:

Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo> 
    .ForMember(dest => dest.Role, opt => opt.MapFrom(src => 
     (Role)Enum.Parse(typeof(Role), src.Name))); 

這種映射是完全沒問題的(除非我犯了一個錯誤),但讓我們說你寫的SelectAll方法在你的原職而不是我的修訂之一:

public IQueryable<DO_RoleInfo> SelectAll() 
{ 
    Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>(); 
    return from role in _ctx.DB_RoleInfo 
      select Mapper.Map<DB_RoleInfo, DO_RoleInfo>(role); 
} 

竟是那樣的這個工作,但一個自稱「可查詢」,就在於。如果我嘗試寫下它,會發生什麼情況:

public IEnumerable<DO_RoleInfo> SelectSome() 
{ 
    return from ri in SelectAll() 
      where (ri.Role == Role.Administrator) || 
       (ri.Role == Role.Executive) 
      select ri; 
} 

想一想這件事真的很難。 Linq如何能夠可能能夠成功地將您的where成爲一個實際的數據庫查詢?

Linq對DO_RoleInfo類一無所知。它不知道如何做映射落後 - 在某些情況下,這可能甚至不可能。當然,你可以看看這個代碼,然後去「哦,這很簡單,只需在Name列」「中搜索'管理員'或'執行',但只有你是唯一知道這一點的人。就Linq to SQL而言,查詢是純粹的廢話。

想象一下,有人給你這些指令:

Go to the supermarket and bring back the ingredients for making Morton Thompson Turkey.

除非你之前已經做到了,而且大部分人都沒有,你到指令響應是最有可能將是:

  • 到底是什麼?

你可以去市場,你可以通過名字來取得具體成分,但你不能評價我已經給你而你那邊的條件。我必須「取消」標準第一個。我必須告訴你,這裏是我們需要這個食譜的成分 - 現在去拿他們。


總之,這是不是LINQ的之間的一些簡單不相容到SQL和AutoMapper。這兩個圖書館都不是唯一的。不要緊實際操作中做映射到非實體類型 - 你可以很容易地做手工的映射,你仍然得到同樣的錯誤,因爲你現在給LINQ to SQL的一組不能再理解的指令,處理不具有任何特定實體類型的內在映射的神祕類。

這個問題到O/R映射的概念,並推遲執行查詢的基礎。投影單向操作。一旦你的項目,你再也不能回到查詢引擎,並說哦對了,這裏爲大家介紹一些多個條件。太晚了。你能做的最好的就是把它已經給你的東西和你自己評估額外的條件。


最後但並非最不重要的,我會給你一個解決方法。如果您希望能夠從你的映射做唯一就是過濾行,你可以這樣寫:

public IEnumerable<DO_RoleInfo> SelectRoles(Func<DB_RoleInfo, bool> selector) 
{ 
    Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>(); 
    return _ctx.DB_RoleInfo 
     .Where(selector) 
     .Select(dbr => Mapper.Map<DB_RoleInfo, DO_RoleInfo>(dbr)); 
} 

這是處理映射爲您和接受過濾器的實用方法在實體,而不是映射的實體。如果您有許多不同類型的過濾器,但始終需要執行相同的映射,則此功能可能會很有用。個人而言,我認爲只要正確寫出查詢,首先確定需要從數據庫中檢索,然後進行任何預測/映射,然後最後如果需要做進一步過濾(你不應該),然後物化結果與ToList()ToArray()並寫入更多的條件對本地列表。

不要試圖使用AutoMapper或任何其他工具來將Linq公開的實體隱藏到SQL中。領域模型是您的公共接口。您編寫的查詢是您的私有執行的一個方面。理解差異並保持良好的關注點很重要。

+0

感謝您的回答! 但是,按照您提供的方式,SelectAll將從數據庫中檢索所有記錄。我想享受延遲加載的好處,它意味着應用程序將從SelectByKey中的數據庫中選擇一條記錄,但不是SelectAll中的所有記錄。 – 2010-02-06 07:42:07

+2

格特阿諾德的答案在下面是解決方案。作爲參考,這裏是相關的API https://github.com/AutoMapper/AutoMapper/blob/master/src/AutoMapper/QueryableExtensions.cs – 2013-02-08 01:06:33

54

@Aaronaught的回答在撰寫本文時是正確的,因爲世界經常發生變化,AutoMapper也隨之改變。與此同時,QueryableExtensions被添加到代碼庫中,該代碼庫增加了對翻譯成表達式的預測的支持,最後還支持SQL。

核心擴展方法是ProjectTo 。這是你的代碼可能看起來像:

using AutoMapper.QueryableExtensions; 

public IQueryable<DO_RoleInfo> SelectAll() 
{ 
    Mapper.CreateMap<DB_RoleInfo, DO_RoleInfo>(); 
    return _ctx.DB_RoleInfo.ProjectTo<DO_RoleInfo>(); 
} 

它會像手動映射行爲。 (CreateMap聲明是出於演示目的。通常,您應該在應用程序啓動時定義映射一次)。

因此,只查詢映射所需的列,結果是IQueryable仍然具有原始查詢提供程序(linq-to-sql,linq-to-entities,無論)。所以它仍然是組合的,這將轉化爲WHERE子句中的SQL:

SelectAll().Where(x => x.Id == Key).SingleOrDefault(); 
v之前

Project().To<T>() 4.1.0

+2

這個答案應該成爲新的答案。 – 2013-02-08 01:07:35

+3

只需使用AutoMapper.QueryableExtensions添加; – Colin 2013-03-22 14:40:47

+0

我收到以下錯誤「The method'Where'不能按照方法'選擇'或不支持'。請參閱此處的問題(http://stackoverflow.com/q/16174351/1133338)。 – 2013-04-23 17:01:13