33

I have a table as follows:

PersonalDetails

Columns are:

Name  
BankName
BranchName
AccountNo
Address

I have another list that contains 'Name' and 'AccountNo'. I have to find all the records from table that whose respective 'Name' and 'AccountNo' are present in given list.

Any suggestion will be helpful.

I have done following but not of much use:

var duplicationhecklist = dataAccessdup.MST_FarmerProfile
                          .Join(lstFarmerProfiles, 
                                t => new { t.Name,t.AccountNo}, 
                                t1 => new { t1.Name, t1.AccountNo}, 
                                (t, t1) => new { t, t1 })
                           .Select(x => new {
                                               x.t1.Name,
                                               x.t1.BankName,
                                               x.t1.BranchName,
                                               x.t1.AccountNo
                                             }).ToList();

where lstFarmerProfiles is a list.

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
Saket Kumar
  • 4,363
  • 4
  • 32
  • 55

5 Answers5

31

You probably found out that you can't join an Entity Framework LINQ query with a local list of entity objects, because it can't be translated into SQL. I would preselect the database data on the account numbers only and then join in memory.

var accountNumbers = lstFarmerProfiles.Select(x => x.AccountNo).ToArray();

var duplicationChecklist = 
        from profile in dataAccessdup.MST_FarmerProfile
                                     .Where(p => accountNumbers
                                                    .Contains(p.AccountNo))
                                     .AsEnumerable() // Continue in memory
        join param in lstFarmerProfiles on 
            new { profile.Name, profile.AccountNo} equals 
            new { param.Name, param.AccountNo}
        select profile

So you will never pull the bulk data into memory but the smallest selection you can probably get to proceed with.

If accountNumbers contains thousands of items, you may consider using a better scalable chunky Contains method.

Community
  • 1
  • 1
Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
  • I gave some more details [here](http://stackoverflow.com/questions/26198860/entityframework-contains-query-of-composite-key/26201371#26201371). – Gert Arnold Oct 05 '14 at 21:43
  • just a theoretical question, but why can't a local list be translated into SQL? When the query actually runs, LINQ should be able to create a temp table or table variable with the data from the local list and then join on that. This is a feature that should absolutely be included in the framework. – jtate Jul 05 '18 at 13:07
  • @jtate There are some [private efforts](https://codereview.stackexchange.com/a/190364/7251) in that direction. To me these only show that it's not really trivial. – Gert Arnold Jul 05 '18 at 16:46
  • @GertArnold this is exactly what I was imagining. With fairly small in-memory collections, I think this would work great. – jtate Jul 10 '18 at 13:02
3

Since you have the lists in .net of values you want to find, try to use the Contains method, for sample:

List<string> names = /* list of names */;
List<string> accounts = /* list of account */;

var result = db.PersonalDetails.Where(x => names.Contains(x.Name) && accounts.Contains(x.AccountNo))
                               .ToList();
Felipe Oriani
  • 37,948
  • 19
  • 131
  • 194
3

If MST_FarmerProfile is not super large I think you best option is to bring it into memory using AsEnumerable() and do the joining there.

var duplicationhecklist = 
             (from x in dataAccessdup.MST_FarmerProfile
                        .Select(z => new {
                                            z.Name, 
                                            z.BankName, 
                                            z.BranchName,
                                            z.AccountNo
                                          }).AsEnumerable()
              join y in lstFarmerProfiles
                 on new { x.Name, x.AccountNo} equals new { y.Name, y.AccountNo}
              select x).ToList();
Magnus
  • 45,362
  • 8
  • 80
  • 118
2

Since data is usually located on different machines or in separate processes at least: DB - is one and your in-memory list is your app, there is just 2 ways to do it.

  1. Download as small data part from DB to local as possible and join locally (usually using AsEnumerable() or basically ToList()). You got many good thoughts on this in other answers.
  2. Another one is different - upload your local data to server somehow and perform query on DB side. Uploading can be done differently: using some temp tables OR using VALUES. Fortunately there is a small extension for EF now (for both EF6 and EF Core) which you could try. It is EntityFrameworkCore.MemoryJoin (name might be confusing, but it supports both EF6 and EF Core). As stated in author's article it modifies SQL query passed to server and injects VALUES construction with data from your local list. And query is executed on DB server.
Tony
  • 7,345
  • 3
  • 26
  • 34
  • 1
    We just added this library and it is AWESOME!!!! This solves most situations where linq to entities stops you. (We were struggling with providing IDs and keeping the sort order) – TChadwick Sep 14 '20 at 16:22
0

If accountNo identifies the record then you could use:

var duplicationCheck = from farmerProfile in dataAccessdup.MST_FarmerProfile
                       join farmerFromList in lstFarmerProfiles
                       on farmerProfile.AccountNo equals farmerFromList.AccountNo
                       select new { 
                                      farmerProfile.Name, 
                                      farmerProfile.BankName, 
                                      farmerProfile.BranchName, 
                                      farmerProfile.AccountNo 
                                  };

If you need to join on name and account then this should work:

 var duplicationCheck = from farmerProfile in dataAccessdup.MST_FarmerProfile
                        join farmerFromList in lstFarmerProfiles
                        on new
                        {
                            accountNo = farmerProfile.AccountNo,
                            name = farmerProfile.Name
                        }
                        equals new
                        {
                            accountNo = farmerFromList.AccountNo,
                            name = farmerFromList.Name
                        }
                        select new
                        {
                            farmerProfile.Name,
                            farmerProfile.BankName,
                            farmerProfile.BranchName,
                            farmerProfile.AccountNo
                        };

If you are only going to go through duplicateChecklist once then leaving .ToList() out will be better for performance.

Bvrce
  • 2,170
  • 2
  • 27
  • 45