c# Linq duplicates initialising of member - c#

I am trying to query two tables and need to pull related records from both the tables. I am using enityframeworkcore 3 One is system versioned table and the other is history table. My resultset is containing data only from history table
and not system-versioned table. Could somebody tell me what is wrong with my statement . I am ensured that the personid in the systemversion table matches the history table.
If I try to add the following to the select list I am getting duplicates initialising of member
select new PersonNote
{
AuthorId = pn.AuthorId,
Created = pn.Created,
Note = pn.Note,
AuthorName = p.FirstName + " " + p.LastName,
AuthorId = pnh.AuthorId,
Created = pnh.Created,
Note = pnh.Note,
AuthorName = p.FirstName + " " + p.LastName,
Original query
public IEnumerable<PersonNote> GetPersonNotes(int personId)
{
var personNotes = (from pn in _context.PersonNotes
join pnh in _context.PersonNotesHistory on pn.PersonId equals pnh.PersonId
join p in _context.Person on pn.PersonId equals p.Id
select new PersonNote
{
AuthorId = pn.AuthorId,
Created = pn.Created,
Note = pn.Note,
AuthorName = p.FirstName + " " + p.LastName,
}
);
return personNotes;
}
Data

Change the table order for joins. Start from Person, join it with PersonNotes on personId, and then with PersonNotesHistory (pn.PersonId equals pnh.PersonId).
public IEnumerable<PersonNote> GetPersonNotes(int personId)
{
var personNotes = (from p in _context.Person
join pn in _context.PersonNotes on pn.PersonId equals p.Id
join pnh in _context.PersonNotesHistory on pn.PersonId equals pnh.PersonId
select new PersonNote
{
AuthorId = pn.AuthorId,
Created = pn.Created,
Note = pn.Note,
AuthorName = p.FirstName + " " + p.LastName,
}
);
return personNotes;
}
This projection wont give you the data from pnh because nothing is selected.

Related

LINQ: Having trouble navigating across multiple jeft join

What I'm trying to do in SQL looks like this:
SELECT
....
ct.FirstName + ' ' + ct.LastName
....
FROM
Leads l
LEFT JOIN LeadContacts lc ON lc.LeadID = l.LeadID
LEFT JOIN Contacts ct on ct.ContactID = lc.ContactID
In the Lead model:
public class Lead
{
....
public virtual ICollection<LeadContact> LeadContacts { get; set; }
....
}
And in the LeadContact model:
public class LeadContact
{
....
[ForeignKey(nameof(LeadID))]
public virtual Lead Lead { get; set; }
[ForeignKey(nameof(ContactID))]
public virtual Contact Contact { get; set; }
....
}
And now, I'm trying to build an object from an instance of:
leads = IQueryable<Lead>...
And for the life of me, I can't figure out how to navigate to the Contacts table.
var results = leads.Select(l => new QuoteSearchItem
{
....
SomeProperty = l.SomeProperty,
LeadSales = l.LeadContacts. ?????
SomeOtherProperty = l.SomeOtherProperty
....
});
QuoteSearchItem.LeadSales is a string. It needs to be:
Contacts.FirstName + " " + Contacts.LastName
Because of the relationship type, l.LeadContacts.Contacts is not an option.
What do I need to do so that this is possible?
You can do that easily if you use comprehension syntax (which use SelectMany when there are multiple froms):
var query = from l in Leads
from lc in l.LeadContacts.DefaultIfEmpty()
from ct in lc.Contacts.DefaultIfEmpty()
select new
{
//....
ContactName = ct.FirstName + ' ' + ct.LastName
//....
};
And if you wish, running this in LinqPad, you can get the lambda version + if that is Linq To SQL the SQL itself.
EDIT: Your class is implying that there is a single Contact per LeadContact, then you can shorten this:
var query = from l in Leads
from lc in l.LeadContacts.DefaultIfEmpty()
select new
{
//....
ContactName = lc.Contact.FirstName + ' ' + lc.Contact.LastName
//....
};
It almost maps to this sample which use the Northwind sample database:
var data = from c in Customers
from o in c.Orders.DefaultIfEmpty()
select new {
CustomerId = c.CustomerID,
OrderId = (int?)o.OrderID,
Employee = o.Employee.FirstName + ' ' + o.Employee.LastName
};
which yields this SQL:
-- Region Parameters
DECLARE #p0 NChar(1) = ' '
-- EndRegion
SELECT [t0].[CustomerID] AS [CustomerId], [t1].[OrderID] AS [OrderId], ([t2].[FirstName] + #p0) + [t2].[LastName] AS [Employee]
FROM [Customers] AS [t0]
LEFT OUTER JOIN [Orders] AS [t1] ON [t1].[CustomerID] = [t0].[CustomerID]
LEFT OUTER JOIN [Employees] AS [t2] ON [t2].[EmployeeID] = [t1].[EmployeeID]
EDIT: What I was saying, instead of:
var results = leads.Select(l => new QuoteSearchItem
{
....
LeadSales = l.LeadContacts. ?????
....
});
Do it like:
var results = from l in leads
from lc in l.LeadContacts.DefaultIfEmpty()
select new QuoteSearchItem
{
....
LeadSales = lc.Contact.FirstName + " " + lc.Contact.LastName
....
};
Either in (method) lambda form or comprehension syntax, end result is same. When selectMany is needed I find comprehension syntax to be easier. And as I said, if you are dying for method syntax, try running it in LinqPad and it will give you lambda counterpart, something like:
var result = leads
.SelectMany (
l => l.LeadContacts.DefaultIfEmpty (),
(l, lc) =>
new QuoteSearchItem
{
//...
LeadSales = lc.Contact.FirstName + " " + lc.Contact.LastName
}
);
First of all, you need a Foreign Key in your LeadContact table. I will assume your Lead table has a LeadId field:
//Foreign key for Standard
public int LeadId { get; set; }
[ForeignKey("LeadId")]
public Lead Lead { get; set; }
That represents the relationship between Lead and Lead Contact and it will limit the records to the ones you want. LeadContact is a cross reference so I am sure it already has a "LeadId" field (or some other name) that maps to the Lead Id field in the Lead table so indicate that relationship as shown above.
Assuming LeadSales is of type IEnumerable<string>, you can then do:
var results = leads.Select(l => new QuoteSearchItem
{
....
LeadSales = l.LeadContacts.Select(lc => lc.Contact.FirstName + " " + lc.Contact.LastName);
....
});
You could do something like:
l.LeadContacts.SelectMany(x => x.Contacts)
But, your Contact property is not a list inside your LeadContact class. So either make it a list or you could access it like:
l.LeadContacts.Select(x => x.Contact)
Are you using contexts ?
var result = await context.SearchLead.AsNoTracking()
.Where(l => l.LeadContacts.Any(lc => lc.Contact == QuoteSearchItem))
.ToListAsync();

LINQ Query not pulling all records needed

I am having some trouble with a LINQ query.
This query creates a list of new objects based on entries from a repository.
Here is the original query:
var accounts = (from a in entityRepository.Queryable<Account>()
from l in a.ExternalLogins
select new
{
a.ID,
FullName = a.FirstName + " " + a.LastName,
Status = a.Status == AccountStatus.Closed ? Enums.Status.Inactive : Enums.Status.Active,
Login = new
{
ConnectionID = l.Connection.ID,
l.Connection.ConnectionType,
l.Identity
},
a.AdminAccess,
a.Username,
a.Email
}).ToList();
My issue is that not all a have an a.ExternalLogins. The query is not pulling those accounts because of the additional from statement from l in a.ExternalLogins. I tried modifying the query to:
var accounts = (from a in entityRepository.Queryable<Account>()
select new
{
a.ID,
FullName = a.FirstName + " " + a.LastName,
Status = a.Status == AccountStatus.Closed ? Enums.Status.Inactive : Enums.Status.Active,
Login = (from l in a.ExternalLogins
select new
{
ConnectionID = l.Connection.ID,
l.Connection.ConnectionType,
l.Identity
}),
a.AdminAccess,
a.Username,
a.Email
}).ToList();
But I am getting a 'System.Reflection.AmbiguousMatchException' exception.
From looking up that exception, I am guessing that the reason is because both Account and Connection have the field ID.
Am I headed in the right direction with this? Do I chase down this exception, or is my query not correct?
I apologize if this is trivial; I'm new to LINQ queries, and my google skills have failed me at this point!
To do a left outer join in Linq, add a DefaultIfEmpty() call and check for null in the results:
var accounts = (from a in entityRepository.Queryable<Account>()
from l in a.ExternalLogins.DefaultIfEmpty()
select new
{
a.ID,
FullName = a.FirstName + " " + a.LastName,
Status = a.Status == AccountStatus.Closed ? Enums.Status.Inactive : Enums.Status.Active,
Login = (l == null) ? null : new
{
ConnectionID = l.Connection.ID,
l.Connection.ConnectionType,
l.Identity
},
a.AdminAccess,
a.Username,
a.Email
}).ToList();

LINQ - Limit result based on count returned by custom function

I have the following code and want to return a limited subset of this query in LINQ. The limited subset will take u.ID as an argument to the function and count the number of records associated with u.ID from another table.
So far, this is what I have.
var res = from u in db.Users
where id == u.WorkGroupID && jobCount(u.ID) > 0
select
new
{
ArtistID = u.ID,
ArtistName = u.FirstName + " " + u.LastName
};
How can I modify this query to limit the number of returned records based on a count value associated with each u.ID?
EDIT:
New Query Below. Last line returns to caller a list from the last LINQ query.
var res = from u in db.Users
where id == u.WorkGroupID
select
new
{
// SELF
ArtistID = u.ID,
ArtistName = u.FirstName + " " + u.LastName
};
var res2 = res.ToList<dynamic>();
var res3 = from row in res2.AsEnumerable()
where jobCount(row.ArtistID) > 0
select new
{
row.ArtistName,
row.ArtistID
};
return res3.ToList<dynamic>();
Use a group join:
from u in db.Users
join o in db.Other on u.ID equals o.UserID into grp
where grp.Any()
select new
{
ArtistID = u.ID,
ArtistName = u.FirstName + " " + u.LastName
};

Multiple Left Join LINQ-to-entities

I have 3 tables:
Dealerships
------------
ID, Name, Website
Locations
------------
ID, DealershipID, Address, Ect.
Contacts
------------
ID, LocationID, Name, Ect.
So the relationship shows that we have dealerships who have multiple locations (Example: Weed Chevrolet of PA, Weed Chevrolet of NJ) and then each location has its own contacts (Example: Managers of PA location, Managers of NJ location). I need to join the 3 tables together. This is what I have:
var results = from d in entities.dealerships
join l in entities.locations on d.ID equals l.DealershipID
join c in entities.contacts on l.ID equals c.LocationID
select new
{
Name = d.Name,
Website = d.Website,
Address = l.Address + ", " + l.City + ", " + l.State + " " + l.Zip,
Contact = c.FirstName + " " + c.LastName,
WorkPhone = c.WorkPhone,
CellPhone = c.CellPhone,
HomePhone = c.HomePhone,
Email = c.Email,
AltEmail = c.AltEmail,
Sells = l.Sells
}
When I attempt to bind results to a BindingSource and then to a DataGridView I receive the following error:
Unable to cast the type 'System.Nullable`1' to type 'System.Object'.
LINQ to Entities only supports casting Entity Data Model primitive types.
What can it be? I am new to JOIN statements in LINQ so I am sure I am doing something wrong.
EDIT: There is data in the database so the results shouldn't be null, just to clarify
You were close but I discovered that you have to convert it from LINQ-To-Entities to LINQ-To-Objects. First I had to cast the entities using AsEnumerable() then use ToList(). This made it so I could use functions like ToString() and String.Format(). Thanks for leading me in the right direction. Here is the final code:
var query = from d in entities.dealerships
from l in entities.locations.Where(loc => loc.DealershipID == d.ID).DefaultIfEmpty()
from c in entities.contacts.Where(cont => cont.LocationID == l.ID).DefaultIfEmpty()
where d.Keywords.Contains(keywords) || l.Keywords.Contains(keywords) || l.Sells.Contains(keywords) || c.Keywords.Contains(keywords)
select new
{
Dealership = d,
Location = l,
Contact = c
};
var results = (from r in query.AsEnumerable()
select new
{
Name = r.Dealership.Name,
Website = r.Dealership.Website,
Contact = r.Contact.FirstName + " " + r.Contact.LastName,
Address = r.Location.Address + ", " + r.Location.City + ", " + r.Location.State + " " + r.Location.Zip,
WorkPhone = r.Contact.WorkPhone,
CellPhone = r.Contact.CellPhone,
Fax = r.Contact.Fax,
Email = r.Contact.Email,
AltEmail = r.Contact.AltEmail,
Sells = r.Location.Sells
}).ToList();
bindingSource.DataSource = results;
Since your results is IQueryable, EF will try to cast on the data store side and it won't work because cast only works with scalar types. You should call ToList() on the results like this:
var results = (from d in entities.dealerships
join l in entities.locations on d.ID equals l.DealershipID
join c in entities.contacts on l.ID equals c.LocationID
select new
{
Name = d.Name,
Website = d.Website,
Address = l.Address + ", " + l.City + ", " + l.State + " " + l.Zip,
Contact = c.FirstName + " " + c.LastName,
WorkPhone = c.WorkPhone,
CellPhone = c.CellPhone,
HomePhone = c.HomePhone,
Email = c.Email,
AltEmail = c.AltEmail,
Sells = l.Sells
}).ToList();
var EmplistDriver = (from a in data
join b in db.DesignationDetails on a.DesignationID equals b.DesignationDetailID into EmployeeBonus
from b in dataBonus.DefaultIfEmpty()
join x in db.EmployeeCommission on a.EmployeeDetailID equals x.EmployeeDetailID into EmployeeCommission
from x in dataComm.DefaultIfEmpty()
join c in db.EmployeeAdvance on a.EmployeeDetailID equals c.FKEAEmployeeID
join d in db.EmployeeAllowance on a.EmployeeAllowanceID equals d.EmployeeAllowanceID
join e in dataAtt on a.EmployeeDetailID equals e.EmployeeDetailID
join f in dataDri on a.EmployeeDetailID equals f.EmployeeDetailID
join h in db.ProjectAllocation on f.FKAllocationID equals h.PKAllocationID
join i in db.ProjectDetails on h.FKProjectDetailID equals i.ProjectDetailID
where a.IsActive == true && c.EAIsActive == true && d.IsActive == true && e.EAIsActive == true && h.IsActivity == true
select new
{
c.BalanceAmount,
c.BalanceDue,
d.FoodAllowance,
i.DriverBasicSalary,
d.OtherAllowance,
d.AccommodationAllowance,
e.EABasicWorktime,
BonusAmount = (b.BonusAmount == null ? 0 : b.BonusAmount),
CommissionAmount = (x.CommissionAmount == null ? 0 : x.CommissionAmount),
TotalOverTime,
TotalHr
}).FirstOrDefault();

LINQ sorting anonymous types?

How do I do sorting when generating anonymous types in linq to sql?
Ex:
from e in linq0
order by User descending /* ??? */
select new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = string.Format("{0:d}", e.Date)
}
If you're using LINQ to Objects, I'd do this:
var query = from e in linq0
select new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = e.Date.ToString("d")
} into anon
orderby anon.User descending
select anon;
That way the string concatenation only has to be done once.
I don't know what that would do in LINQ to SQL though...
If I've understood your question correctly, you want to do this:
from e in linq0
order by (e.User.FirstName + " " + e.User.LastName).Trim()) descending
select new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = string.Format("{0:d}", e.Date)
}
Would this work, as a way of avoiding Jon's select...into?
from e in linq0
let comment = new
{
Id = e.Id,
CommentText = e.CommentText,
UserId = e.UserId,
User = (e.User.FirstName + " " + e.User.LastName).Trim()),
Date = string.Format("{0:d}", e.Date)
}
orderby comment.User descending
select comment
I'm going to get a necromancer badge for this answer, but I still think it's worth showing this snippet.
var records = await (from s in db.S
join l in db.L on s.LId equals l.Id
where (...)
select new { S = s, Type = l.MyType }
).ToListAsync();
//Data is retrieved from database by now.
//OrderBy below is LINQ to Objects, not LINQ to SQL
if (sortbyABC)
{
//Sort A->B->C
records.OrderBy(sl => sl.Type, new ABC());
}
else
{
//Sort B->A->C
records.OrderBy(sl => sl.Type, new BAC());
}
ABC and BAC implement IComparer<MyType>.

Categories

Resources