3

I need this linq query to work but linq is complaining about customercontact and phone being int64s and I also need to concat the second column but I'm afraid that isn't working for the same reason. If I add a tostring() it just says linq doesn't recognize it.

base {System.SystemException} = {"Unable to cast the type 'System.Int64' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types."}

var tempCustomers =
                    from c in db.Customers
                    let cc = db.CustomerContacts.FirstOrDefault(x => x.CustomerID == c.CustomerID)
                    select new{cc.CustomerContactID, CustomerValue = c.CustomerName + " 	 	 " + cc.Phone};
danludwig
  • 46,965
  • 25
  • 159
  • 237
Preston
  • 1,300
  • 1
  • 17
  • 32
  • Maybe try `cc.Phone.ToString()` ? – Matthew Watson Apr 06 '13 at 23:19
  • base {System.SystemException} = {"LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression."} – Preston Apr 06 '13 at 23:25
  • 3
    why isn't cc.Phone a string? It certainly isn't a int64....[What's the right way to represent phone numbers?](http://stackoverflow.com/a/3483166/1808494) – Aron Apr 06 '13 at 23:41
  • The only reason to store as a string instead of a long is for parens and dashes which I do on the front end, I see no reason to be storing those. I find it easier to validate them as a long where I don't need a mile long regex. Really it's just personal preference but at this point in the project I'd rather not make a change like that. – Preston Apr 06 '13 at 23:44
  • 1
    Have you tried `Convert.ToString(c.CustomerName)`? – educampver Apr 06 '13 at 23:52
  • Yup, I have. base {System.SystemException} = {"LINQ to Entities does not recognize the method 'System.String ToString(Int64)' method, and this method cannot be translated into a store expression."} – Preston Apr 06 '13 at 23:55
  • 2
    @Preston, that is emphatically not true. As a `long`, a number such as `0014155551212` is equivalent to `14155551212`. Phone numbers are strings. Those two *strings* do not contain identical information. Using numerics to capture phone numbers is wrong. – Kirk Woll Apr 07 '13 at 00:02
  • http://stackoverflow.com/questions/1066760/problem-with-converting-int-to-string-in-linq-to-entities – dugas Apr 07 '13 at 00:05

2 Answers2

1

This error is coming from LINQ to entities. Here is one solution:

var tempCustomers =
    from c in db.Customers.ToArray()
    let cc = db.CustomerContacts
       .FirstOrDefault(x => x.CustomerID == c.CustomerID)
    select new
    {
        cc.CustomerContactID,
        CustomerValue = string.Format("{0} 	 	 {0}",
            c.CustomerName, cc.Phone)
    };

The above will hit the database before it tries to do the string concatenation. If that is not acceptable, please note so in your question.

Why it's not working

LINQ to Entities uses deferred SQL execution, meaning that your LINQ query will not hit the database until you iterate over the IQueryable using a foreach, or call a method like ToList or ToArray on the IQueryable. You can use any code you want inside a LINQ predicate expression, but it will fail at runtime if LINQ to Entities can't figure out how to translate it into SQL. Your code is failing because LINQ to Entities can't figure out how to concatenate CustomerName, your custom string, and the PhoneNumber while running the SQL query. The above works because it gets the data from the database first and then does the string concatenation in memory.

Update

To expand on the better solution which @JeffMercado beat me to, you really should be using a navigation property to join Customer and CustomerContacts. That would eliminate the need for the let clause and the First or FirstOrDefault call:

public class Customer
{
    public long CustomerID { get; set; }
    public string CustomerName { get; set; }
    public virtual ICollection<CustomerContact> Contacts { get; set; }
}

public class CustomerContact
{
    public long CustomerContactID { get; set; }
    public long CustomerID { get; set; }
    public virtual Customer Owner { get; set; }
    public long Phone { get; set; } // I agree this should be a string
}

You should then be able to query out data like this:

var query = db.CustomerContacts
    .Include(x => x.Owner) // eager load to avoid multiple separate SQL queries
    .Select(x => new {
        CustomerContactID = x.CustomerContactID,
        CustomerName = x.Owner.CustomerName,
        Phone = x.Phone,
});

From here, you can use AsEnumerable, ToArray, or ToList to execute the query and format your special CustomerValue property.

var results = query
    .ToArray() // or .AsEnumerable(), or .ToList(), all will execute the SQL query
    .Select(x => new {
        CustomerContactId = x.CustomerContactID,
        CustomerValue = string.Format("{0} &#09;&emsp;&#09; {1}",
            x.CustomerName, x.Phone)
});
danludwig
  • 46,965
  • 25
  • 159
  • 237
0

The kinds of operations you can perform within a query are limited in EF, conversions are one of them. You need to move that part out of the query just getting the data then use AsEnumerable() so you're working with LINQ to Objects. Then you can do whatever you want with the data.

var query=
    from c in db.Customers
    let cc = c.CustomerContacts.FirstOrDefault() // better to use the navigation properties instead
    select new // select the fields you need
    {
        cc.CustomerContactId,
        c.CustomerName,
        Phone = (long?)cc.Phone, // cc could potentially be null this long must also be nullable
    };
var tempCustomers =
    from x in query.AsEnumerable() // LINQ to Objects
    select new // now you can do what you want to the data
    {
        x.CustomerContactId,
        CustomerValue = x.CustomerName + " &#09;&emsp;&#09; " + x.Phone,
    };

I broke in out into separate statements for readability but you could combine them if you'd like.

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • When I debug and try to expand the results of var query all I get is base {System.SystemException} = {"The cast to value type 'Int64' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type."} – Preston Apr 07 '13 at 00:33
  • You need to keep in mind that by using `FirstOrDefault()`, `cc` could potentially be `null` so in effect, all value types you want to select must be declared nullable. – Jeff Mercado Apr 07 '13 at 00:49
  • As much as I wish that was the problem, it isn't, I just double checked to be sure, they are non-nullable in both places and there are no records in the DB without a phone. – Preston Apr 07 '13 at 00:52