iRepository Select - can I concat?

Topics: Troubleshooting, Writing modules
Aug 25, 2011 at 12:56 AM

I have a model named Client that is made up of 2 strings: ClientId and ClientName

I'm doing an iRepository query from a table and trying to populate the results into a IEnumerable<Client>. The Select line looks like the following:

select new Client { ClientId = client.GroupId, ClientName = client.GroupId + "-" + client.GroupName };

which nHibernate is throwing an error on when I debug because client.GroupId + "-" _ client.GroupName is not a valid way to do this. If I make ClientName = client.GroupName, everything works fine, so the rest of the process is okay.

I'm assuming this is not the correct way to concat 2 strings together and assign it to a property with nHibernate? Does anyone know how I would do this so that ClientName is "GroupId-GroupName"?

Thanks for any help!

Coordinator
Aug 25, 2011 at 12:59 AM

mmh, not sure why nHibernate would care, unless there is something else in the query that would cause this. In any case, you can probably do the nHib query without the computed column, get the list and then do a select on that, which would then not involve nHib at all. I think.

Aug 25, 2011 at 2:39 AM

ya...not sure why it's throwing an error on that part. I was able to do it with this...

        public IEnumerable<Client> GetClients(string text)
        {
            IEnumerable<SalesHierarchyRecord> clientlist = from client in _saleshierarchyRepository.Table
                                                           where client.GroupName.Contains(text) || client.GroupId.Contains(text)
                                                           select client;
            List<Client> clients = new List<Client>();
            foreach (var c in clientlist.Select(p => new { p.GroupId, p.GroupName }).Distinct())
            {
                Client client = new Client
                {
                    ClientId = c.GroupId,
                    ClientName = c.GroupId + "-" + c.GroupName
                };
                clients.Add(client);
            }
            return clients.AsQueryable();
        }
It's a bit more code than I was hoping for something this simple, but it works. Thanks for the help!

Aug 25, 2011 at 6:11 PM

I havent tried this but I thought I'd ask. Have you tried this:

select new Client { ClientId = client.GroupId, ClientName = (client.GroupId + "-" + client.GroupName) };

or this :

select new Client { ClientId = client.GroupId, ClientName = (client.GroupId + "-" + client.GroupName).ToString() };

or this :

select new Client { ClientId = client.GroupId, ClientName = String.Concat(client.GroupId + "-" + client.GroupName) };

I often find that when things like this happen, in any ORM, that making the ORM only see the resultant value is key, so you want to force it to do the work it doesn't like before it generates it's query.

And yes I know calling string.ToString() seems completely ridiculous, but in some cases it's what needs to be done.

Coordinator
Aug 25, 2011 at 7:13 PM

@psenechal: you don't have to do this. You should be able to do something like this:

public IEnumerable GetClients(string text) {
  IEnumerable clientlist =
    (from client in _saleshierarchyRepository.Table
     where client.GroupName.Contains(text) || client.GroupId.Contains(text)
     select client).ToList();
  List clients =
    clientlist.Select(r => 
      new { r.GroupId, r.GroupId + "-" + r.GroupName }).Distinct();
}

Depends how big the table is. @reverand's suggestions may work as well.

Aug 25, 2011 at 7:46 PM

Thanks for the suggestions guys...

@reverand: none of those worked...I got the same error.

@bertrand: thanks for the code suggestion. I have it down to this now:

            List<SalesHierarchyRecord> clientlist = (from client in _saleshierarchyRepository.Table
                                                     where client.GroupName.Contains(text) || client.GroupId.Contains(text)
                                                     select client).ToList();
            IEnumerable<Client> clients = clientlist.Select(x => new Client { ClientId = x.GroupId, ClientName = x.GroupId + "-" + x.GroupName }).Distinct();
            return clients;

The only problem I'm having is that the Distinct() isn't working as I'm still seeing duplicates. I'll keep playing with it's placement.

The structure of SalesHierarchyRecord looks like this btw:

GroupId, GroupName, EmployeeId1, EmployeeId2, EmployeeId3

1234, Company, 1, 1, 1

1234, Company, 2, 2, 2

So for this example, I'm still getting 2 results instead of just 1. Almost there though.

Coordinator
Aug 25, 2011 at 7:52 PM

ah, yes, because the Client instances are distinct objects of course, unless you redefined equality. Yes, you'd have to use an intermediate anonymous object I think, so you'd have a second select with anonymous object, then distinct, then a third select to map to the Client type.

Aug 25, 2011 at 8:17 PM
Edited Aug 25, 2011 at 8:19 PM

ahhh...gotcha. I added in another layer to handle the Distinct() before mapping it to Client

 

            IEnumerable<SalesHierarchyRecord> clientlist = from client in _saleshierarchyRepository.Table
                                                           where client.GroupName.Contains(text) || client.GroupId.Contains(text)
                                                           select client;
            var distinctclients = clientlist.Select(x => new { ClientId = x.GroupId, ClientName = x.GroupId + "-" + x.GroupName }).Distinct();
            IEnumerable<Client> clients = distinctclients.Select(x => new Client { ClientId = x.ClientId, ClientName = x.ClientName });
            return clients.Distinct();

This seems to work good. Thanks for the help...this definitely seems more compact and efficient than what I had originally.

Aug 25, 2011 at 8:31 PM

I always prefer to redefine equality :) :

public class FuncComparer<T> : IEqualityComparer<T>
    {
        public Func<T, T, bool> Comparer { get; set; }

        public FuncComparer(Func<T, T, bool> comparer)
        {
            Comparer = comparer;
        }
        public FuncComparer()
        {
            Comparer = DefaultComparer;
        }

        public bool Equals(T x, T y)
        {
            return Comparer(x, y);
        }

        public int GetHashCode(T obj)
        {
            return obj.GetHashCode();
        }

        public static Func<T, T, bool> DefaultComparer
        {
            get
            {
                return (x, y) => x.Equals(y);
            }
        }
    }

 

Create an instance of that with a defined Func for equality and pass it to Distinct, if I am reading your code right it would be:

(x,y) => x.ClientId == y.ClientId
Aug 25, 2011 at 8:35 PM

Thanks reverand...I've never redefined equality before. I'll have to play around with that to see how it works. Always love learning something new!

Coordinator
Aug 25, 2011 at 8:38 PM

But you 're doing it wrong :) http://weblogs.asp.net/bleroy/archive/2004/12/15/316601.aspx Your hash code in particular should be consistent and return the same value for two objects that are equal. It doesn't if I'm not mistaken.

Aug 25, 2011 at 8:48 PM

Damn, I am doing it wrong. :) And your right, if we just hand off the objects hash code nothing has changed, crap fooey. 

Truth be told I have never used this in a scenario where 2 object really being "equal" actually mattered, only where we want to know if they are "the same", by our definition. (big difference there)

I actually think I see based on your article (THANKS!) how I could fix this pretty easily and make it better.

I love better!

Coordinator
Aug 25, 2011 at 8:50 PM

Glad I could help :)