2015-08-17 37 views
5

Özellik değerlerini ilgili tür yerine Nesneler olarak almaya çalışırken bir sorunla karşılaşıyorum. Aşağıdaki kod, bu istisna atar: tarihsaat, Tamsayılar veya null türlerini seçerken bir dize seçerek değil, ne zamanEntity Framework: Nesneyi nesne olarak seç

Unable to cast the type 'System.DateTime' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types. 

Bu kod çalışıyor.

public class Customer 
{ 
    public int Id { get; set; } 

    public string Name { get; set; } 

    public DateTime CreatedOn { get; set; } 
} 

public class Program 
{ 
    public static void Main(string[] args) 
    { 
     using (var ctx = new MyContext()) 
     { 
      // Property selector: select DateTime as Object 
      Expression<Func<Customer, object>> selector = cust => cust.CreatedOn; 

      // Get set to query 
      IQueryable<Customer> customers = ctx.Set<Customer>(); 

      // Apply selector to set. This throws: 
      // 'Unable to cast the type 'System.DateTime' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.' 
      IList<object> customerNames = customers.Select(selector).Distinct().ToList(); 

     } 
    } 
} 

public class MyContext : DbContext 
{ 

} 

Son hedef, bir nesnenin özelliklerinden herhangi birinden farklı değerler seçmek için genel bir filtredir.

+4

Sonunda 'ToList' olarak adlandırıyorsunuz, o zaman neden daha sonra bunlara itiraz etmek için DateTime'ı seçmiyorsunuz? – Hopeless

+1

Seçici üye ifadesi çalışma zamanında oluşturulduğundan, özellik türü derleme zamanında bilinmez. Ben türünü belirlemek için yansıma kullanabilirdim, ama bu iç içe geçmiş özellikler için karmaşık olurdu – user2346738

cevap

3

Expression numaralı bildirimi kullanarak, özelliği uygun bir şekilde (özellik yolunu temsil eden ve Yansıma'yı kullanarak noktayla ayrılmış bir dizeyi ayrıştırma gibi olmaksızın) seçmek için kullanmak istediğinizi anlıyorum. Ancak bunu yapmak, Expression'un açıkça beyan edilmesini gerektirir ve açık tip kullanmamız gerekir. Ne yazık ki, object türünün, İfade'nin dönüş türü olarak kullanılamaz, çünkü daha sonra veritabanında desteklenen türlerden birine dönüştürülemez.

Burada bir çalışma olduğunu düşünüyorum. Buradaki fikir Expression<T,object>'u Expression<T,returnType>'a çevirecektir, burada returnType mülkün gerçek dönüş türüdür (selector tarafından iade edilir). Ancak Select, her zaman tasarım zamanında returnType anlamına gelen Expression<T,returnType> anlamında açık bir tip gerektirir. Yani bu imkansız. Doğrudan Select numaralı telefonu arama imkanımız yok. Bunun yerine, Select'u çağırmak için Yansıma'yı kullanmalıyız. Geri dönüş sonucu, istediğiniz nesnelerin bir listesini almak için ToList()'u arayabileceğiniz bir IEnumerable<object> olarak bekleniyor.

Şimdi IQueryable<T> için bu uzatma yöntemi kullanabilirsiniz:

public static class QExtension 
{ 
    public static IEnumerable<object> Select<T>(this IQueryable<T> source, 
               Expression<Func<T, object>> exp) where T : class 
    { 
     var u = exp.Body as UnaryExpression; 
     if(u == null) throw new ArgumentException("exp Body should be a UnaryExpression.");    
     //convert the Func<T,object> to Func<T, actualReturnType> 
     var funcType = typeof(Func<,>).MakeGenericType(source.ElementType, u.Operand.Type); 
     //except the funcType, the new converted lambda expression 
     //is almost the same with the input lambda expression. 
     var le = Expression.Lambda(funcType, u.Operand, exp.Parameters);    
     //try getting the Select method of the static class Queryable. 
     var sl = Expression.Call(typeof(Queryable), "Select", 
           new[] { source.ElementType, u.Operand.Type }, 
           Expression.Constant(source), le).Method; 
     //finally invoke the Select method and get the result 
     //in which each element type should be the return property type 
     //(returned by selector) 
     return ((IEnumerable)sl.Invoke(null, new object[] { source, le })).Cast<object>(); 
    }   
} 

Kullanımı: Ben exp.Body.Type erişen çalıştı ve bu düşünce İlk başta

Expression<Func<Customer, object>> selector = cust => cust.CreatedOn; 
IQueryable<Customer> customers = ctx.Set<Customer>(); 
IList<object> customerNames = customers.Select(selector).Distinct().ToList(); 

(tam olarak kodu olarak) iç ifadenin gerçek dönüş tipi. Ancak her nasılsa, string özel özel durumu hariç her zaman System.Object (özellik erişiminin dönüş türü string olduğunda). Bu, iç ifadenin gerçek dönüş türüyle ilgili bilgilerin tamamen kaybolduğunu (veya en azından çok dikkatli bir şekilde gizlendiğini) gösterir. Bu tarz bir tasarım oldukça garip ve tamamen kabul edilemez. Neden böyle yaptığını anlamıyorum. İfadenin gerçek dönüş tipi ile ilgili bilgilere kolayca erişilmiş olmalıdır.

0

Öğenin linq noktası, .NET linq komutlarının kullanılmasıyla sql sorgusunun oluşturulmasıdır. Varlık yönergeleri için linq hiç yürütülmeyecek, sadece sql'ye çevrilir. Yani bu linq ifadelerinin içindeki her şeyin sql'ye dönüştürülebilir olması gerekiyor. Ve sql, tarih, dize vb. Için uygun türlere sahiptir. Sınıflar, her özelliğin belirli bir sütun anlamına geldiği tablolar olarak anlaşılır. Ancak, SQL'de olduğu gibi sql'de hiçbir nesne kavramı yoktur ve bu sizin probleminizin kaynağıdır.

Expression<Func<Customer, DateTime>> selector = cust => cust.CreatedOn; 

// Get set to query 
IQueryable<Customer> customers = ctx.Set<Customer>(); 

IList<object> customerNames = customers.Select(selector).Distinct().ToList().Cast<object>().ToList(); 

sql sorguya çevrilmiştir ilk ToList için sorgu varan yazma Her şey, dinlenme: En linq sorguda Uygun verileri döndürmek ve programınızda döküm yapmak için sadece sorgu oluşturmaya odaklanılmalıdır bellekte yürütülür. Bu sayede döküm parçasını hafızaya aldık, bunu yapmak mantıklı.

+2

Bu sorun hakkında kullanıcı2346738 ile çalışıyorum, kıskançlık uğruna bazı bilgiler ihmal ettik. Seçici, bir dize (örneğin 'Country.Name' müşteri => customer.Country.Name olur) bir MemberExpression oluşturarak çalışma zamanında oluşturulur, böylece özellik türü derleme zamanında bilinmez. Türü belirlemek için yansıma kullanabiliriz, ancak iç içe geçmiş üyeler için bu biraz kötüleşir. – chrisv