Trying joining using Linq. What should I use? Left join or right join?
FIRST LIST SECOND LIST
APPLICANT_ID|Applicant_Name| NOTES | |APPLICANT_ID|Applicant_Name | NOTES |
1 | RAY HEAVENS | Note1 | | 2 | BEN TULFO | NoteA
2 | BEN TULFO | Note2 | | 3 | ERNIE BARON | NoteB
3 | ERNIE BARON | Note3 | |
4 | SUPERMAN | Note4 | |
5 | MARK LAPID | Note5 | |
Desired output:
APPLICANT_ID | Applicant_Name | NOTES
-------------+----------------+---------
1 | RAY HEAVENS | Note1
2 | BEN TULFO | NoteA
3 | ERNIE BARON | NoteB
4 | SUPERMAN | Note4
5 | MARK LAPID | Note5
This is my code in my controller:
var applicantList = (from a in db.Profiles
where a.isDeleted == false
select a ).ToList();
var GetNewNotes = (from a in db.ProfilesNotes
where a.isDeleted == false
select a).ToList();
var lst = (from lst1 in applicantList
where !GetNewNotes.Any(x => x.APPLICANT_ID == lst1.APPLICANT_ID )
select lst1).ToList();
ViewBag.updatedNotes = lst;
I hope someone can recommend me what to use or what to do.
Thank you in advance.
This is an odd structure to map. Structurally it looks like a 1-to-0..1, but in concept it looks like it should be 1-to-many. For a 1-to-many I'd be expecting a table structure more like:
Applicant ( ApplicantId | Name )
ApplicantNote ( ApplicantNoteId | ApplicantId | Note )
This would be mapped in EF something like:
public class Applicant
{
public int ApplicantId { get; set; }
public string Name { get; set; }
public virtual ICollection<ApplicantNote> { get; set; } = new List<ApplicantNote>();
}
public class ApplicantNote
{
public int ApplicantNoteId { get; set; }
public virtual Applicant Applicant { get; set; }
}
public class ApplicantConfig : EntityTypeConfiguration<Applicant>
{
public ApplicantConfig()
{
ToTable("Applicant");
HasKey(x => x.ApplicantId);
HasMany(x => x.ApplicantNotes)
.WithRequired(x => x.Applicant)
.Map(x => x.MapKey("ApplicantId"));
}
}
public class ApplicantNoteConfig : EntityTypeConfiguration<ApplicantNote>
{
public ApplicantNoteConfig()
{
ToTable("ApplicantNote");
HasKey(x => x.ApplicantNoteId);
}
}
What you have is more like an Applicant table that contains a note, but then there is an additional table that can hold a single, additional extra note.
Applicant ( ApplicantId | Name | Note )
ExtraApplicantNote ( ApplicantId | Note ) // Name isn't required.
which in a 1-to-0..1 would look something like:
public class Applicant
{
public int ApplicantId { get; set; }
public string Name { get; set; }
public string Note { get; set; }
public ExtraApplicantNote ExtraApplicantNote { get; set; }
}
public class ExtraApplicantNote
{
public int ApplicantId { get; set; }
public string Note { get; set; }
public virtual Applicant Applicant { get; set; }
}
public class ApplicantConfig : EntityTypeConfiguration<Applicant>
{
public ApplicantConfig()
{
ToTable("Applicant");
HasKey(x => x.ApplicantId);
HasOptional(x => x.ExtraApplicantNote)
.WithRequired(x => x.Applicant);
}
}
public class ExtraApplicantNoteConfig : EntityTypeConfiguration<ExtraApplicantNote>
{
public ExtraApplicantNoteConfig()
{
ToTable("ExtraApplicantNote");
HasKey(x => x.ApplicantId);
}
}
This joins this extra applicant note record to the Applicant as an optional associated entity. When selecting as an entity graph:
var applicant = context.Applicants
.Include(x => x.ExtraApplicantNote)
.Single(x => x.ApplicantId == applicantId);
for example... then access the note(s) via applicant.Note and applicant?.ExtraApplicantNote.Note to account for the fact that an extra applicant note is optional.
To produce an output of all notes with their applicant details, a 1-to-many structure is far, far simpler to produce:
var notes = context.ApplicantNotes.Select(x => new
{
x.Applicant.ApplicantId,
x.Applicant.Name,
x.Note
}).ToList();
To do the same thing with a 1-to-0..1 is a fair bit more involved:
var notes = context.Applicants.Select(x => new
{
x.ApplicantId,
x.Name,
x.Note
}).Union(context.ExtraApplicantNotes.Select(x => new
{
x.ApplicantId,
x.Applicant.Name,
x.Note
})).ToList();
This involves first pulling the notes from the first table, then using a union to join the same details from the optional records in the second table.
** Edit ** Sorry, I re-read the question and you want the 2nd table to override the first.
In this case, similar to above:
var notes = context.ExtraApplicantNotes.Select(x => new
{
x.ApplicantId,
x.Applicant.Name,
x.Note
}).Union(context.Applicants
.Where(x => x.ExtraApplicant == null)
.Select(x => new
{
x.ApplicantId,
x.Name,
x.Note
})).ToList();
I would go for an inner join with .Join():
var lst = applicantList.Join(GetNewNotes,
(a) => a.APPLICANT_ID,
(n) => n.APPLICANT_ID,
(a, n) => return new
{
a.APPLICANT_ID,
a.Applicant_Name,
n.Notes
});
/*
lst:
2 | BEN TULFO | NoteA,
3 | ERNIE BARON | NoteB
*/
As a side note, is there any reason your second table contains ApplicantName?
Why not keep this in Applicant table only?
EDIT:
After re-reading the question, I realized that you need the unmatched entries from the left list
too. So, that should be left outer join instead, which you achieve with .GroupJoin() and .SelectMany():
var lst = applicantList.GroupJoin(GetNewNotes,
(a) => a.Id,
(n) => n.Id,
(a, n) => new
{
Id = a.Id,
Name = a.Name,
Notes = a.Notes,
ApplicantNotes = n
})
.SelectMany(
g => g.ApplicantNotes.DefaultIfEmpty(),
(g, applicantNotes) => new
{
Id = g.Id,
Name = g.Name,
Notes = applicantNotes?.Notes ?? g.Notes
});
/*
lst:
1 | RAY HEAVENS | Note1
2 | BEN TULFO | NoteA
3 | ERNIE BARON | NoteB
4 | SUPERMAN | Note4
5 | MARK LAPID | Note5
*/
Related
public class Entity
{
public string Id { get; set; }
public string CreatedBy { get; set; }
public string LastUpdatedBy { get; set; }
[NotMapped]
public string Creator { get; set; }
[NotMapped]
public string Updater { get; set; }
}
public class User
{
public string Name { get; set; }
public string Id { get; set; }
}
I am going to search Entities, sort on Creator/Updater properties(and return UserInfo.Name) with ef core query, any idea?
After hours of researches, refers
How do you perform a left outer join using linq extension methods
Entity Framework Join 3 Tables
There 3 ways in oder(presonal perfer the first than second):
class Program
{
static void Main()
{
using TenantDBContext dbContext = new TenantDBContext("PORT=5432;DATABASE=linqtosql;HOST=xxx.com;PASSWORD=xxx;USER ID=postgres;Pooling=true;Minimum Pool Size=10;Application Name=xxx");
var result = (
from entity in dbContext.Entities
join user in dbContext.Users on entity.CreatedBy equals user.Id into temp1
from ce in temp1.DefaultIfEmpty()
join user1 in dbContext.Users on entity.UpdatedBy equals user1.Id into temp2
from cu in temp2.DefaultIfEmpty()
select new Entity() { Id = entity.Id, CreatedBy = entity.CreatedBy, UpdatedBy = entity.UpdatedBy, Creator = ce.Name, Updater = ce.Name }
).ToList();
Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented));
var result2 = dbContext.Entities
.GroupJoin(dbContext.Users, e => e.CreatedBy, u => u.Id, (e, u) => new { Entity = e, User = u })
.SelectMany(eUser => eUser.User.DefaultIfEmpty(), (e, u) => new Entity() { Id = e.Entity.Id, CreatedBy = e.Entity.CreatedBy, UpdatedBy = e.Entity.UpdatedBy, Creator = u.Name })
.GroupJoin(dbContext.Users, e => e.UpdatedBy, u => u.Id, (e, u) => new { Entity = e, User = u })
.SelectMany(eUser => eUser.User.DefaultIfEmpty(), (e, u) => new Entity() { Id = e.Entity.Id, CreatedBy = e.Entity.CreatedBy, UpdatedBy = e.Entity.UpdatedBy, Creator = e.Entity.Creator, Updater = u.Name }
).ToList();
Console.WriteLine(JsonConvert.SerializeObject(result2, Formatting.Indented));
var result3 = dbContext.Entities
.SelectMany(entity => dbContext.Users.Where(user => entity.CreatedBy == user.Id).DefaultIfEmpty(), (entity, user) => new { Entity = entity, User = user })
.SelectMany(entity => dbContext.Users.Where(user => entity.Entity.UpdatedBy == user.Id).DefaultIfEmpty(), (entity, user) => new Entity { Id = entity.Entity.Id, CreatedBy = entity.Entity.CreatedBy, UpdatedBy = entity.Entity.UpdatedBy, Creator = entity.User.Name, Updater = user.Name })
.ToList();
Console.WriteLine(JsonConvert.SerializeObject(result2, Formatting.Indented));
}
}
I wanted to ask about building the model in EntityFramework Core - v 2.1.
First things first - what I want to achieve is described below:
// I want this entity to be stored in database, with EntityReferences as owned records,
// stored within the same table.
// There will be more properties of type EntityReference.
public partial class CdiEmailEvent
{
public override Guid Id { get; set; }
public EntityReference CdiAccountId
public EntityReference CdiAutomationId
}
// EntityReference is a pointer to record, containing Id of target record and it's type.
// So Id does not point to CdiEmailEntity record, but for example an Account or Contact.
// I want this to be an owned record within CdiEmailEvent.
public sealed class EntityReference : IExtensibleDataObject
{
public Guid Id { get; set; }
public string LogicalName { get; set; }
public string Name { get; set; }
public string RowVersion { get; set; }
}
So the real data could look like:
new CdiEmailEvent {
Id = "18640407-1A44-430A-8267-96BD23CC9EE8",
CdiAccountId = new CdiAccountId {
Id = "FBBB4932-C74C-47CE-8D1B-909C5975D945",
Name = "Account",
LogicalName = "cdi_account"
},
CdiAutomationId = new CdiAccountId {
Id = "5496BC1C-C5FD-4AFB-B6D9-913DD5549C13",
Name = "Automation",
LogicalName = "cdi_automation"
},
...
}
And I'd like databese table to look like:
Id | CdiAccountId | CdiAccountName | CdiAccountLogicalName | CdiAutomationId | CdiAutomationName | CdiAutomationLogicalName
"18640407-1A44-430A-8267-96BD23CC9EE8" | "FBBB4932-C74C-47CE-8D1B-909C5975D945" | "Account" | "cdi_account" | "5496BC1C-C5FD-4AFB-B6D9-913DD5549C13" | Automation | "cdi_automation"
My current model configuration is:
modelBuilder
.Entity<CdiEmailEvent>(entity =>
{
entity.HasKey(e => e.CdiEmailEventId);
entity.OwnsOne(rel => rel.CdiAccountId);
entity.OwnsOne(rel => rel.CdiAutomationId);
...
entity.ToTable("CdiEmailEvents", this._databaseConfig.DatabaseSchema);
});
The issue that I'm encountering is:
System.InvalidOperationException: 'The entity of type 'CdiEmailEvent' is sharing
the table 'replication.CdiEmailEvents' with entities of type
'CdiEmailEvent.CdiAutomationId#EntityReference', but there is no entity of this
type with the same key value '{CdiEmailEventId: 8a99d6ab-cd82-4eb8-b548-50d903d6f26c}'
that has been marked as 'Added'.'
I guess that EF Core is trying to use CdiAutomationId as a reference to CdiEmailEvent. Does anyone know how can I alter such behavior, and enforce EF Core to treat CdiAutomationId simply as an object without any keys? I went through EF Core's documentation, but it's not very descriptive in this matter.
The problem simplified is as follows: in Entity Framework i am doing a join involving 3 tables, and returning the joined result set, which involves (some) fields from the 3 tables.
var query = (
from t1 in dbCtx.TB_Entity1
from t2 in dbCtx.TB_Entity2
.Where(p => p.someCol == t1.someCol && t.IsActive == true)
.DefaultIfEmpty() //LEFT JOIN
from t3 in dbCtx.TB_Entity3
.Where(q => q.someCol == t2.someCol)
where t1.IsLatest == true
&& (t1.istatus == 2
|| t1.istatus == 3
)
select new {
t1.col100,
t1.col101,
t2.col200,
t2.col201,
t3.col300,
t3.col301
}).OrderByDescending(t1 => t1.ID);
var anonObjList = query.ToList();
Thus, at the end of the query I write a projection to select the fields i want.
Finally i run the query with .ToList() and get a list of Anonymous objects.
How do i modify the query to project into a List of MyConcreteClass
i.e. i want to be able to write something similar to
List<MyConcreteClass> myObjList = query.ToList();
You may assume my concrete class looks like
public class MyConcreteClass
{
public string Col100 { get; set; }
public string Col101 { get; set; }
public string Col200 { get; set; }
public string Col201 { get; set; }
public string Col300 { get; set; }
public string Col301 { get; set; }
}
You just use the object initializer syntax:
new MyConcreteClass
{
Col100 = t1.col100,
Col101 = t1.col101,
Col200 = t2.col200,
Col201 = t2.col201,
Col300 = t3.col300,
Col301 = t3.col301
}
How can I model this scenario with Entity Framewrok Code First:
Cats:
| Id | Name |
|----|-------|
| 1| Cat1|
| 2| Cat2|
| 3| Cat3|
Dogs:
| Id | Name |
|----|-------|
| 1 | Dog1|
| 2 | Dog2|
| 3 | Dog3|
Owner:
| Id | Name |TableName |EntityId|
|----|-------|--------------------
| 1 | John| Dog | 1|
| 2 | Pete| Cat | 1|
| 3 |Jessica| Cat | 2|
This is just an example of what I want to accomplish. I need that Owner Table can store any Animal, I meant, I have no control over the animal tables, so plus cats and dogs, there may be mouses, lions, etc.
Entity Framework support this? Can I model this with navigation properties?
I have read about Table Per Hierarchy but don't think that should follow this approach. Also, I want to navigate from Owner table to any animal entity.
I offer to you this solution, that completely compatible with your tables and also if you will add another table with new animal, it is not needed to make any changes at Owner class, that is why it is generic approach as you desired.
MODELS:
public class Animal
{
public int Id { get; set; }
[StringLength(64)]
public string Name { get; set; }
}
public class Dog : Animal
{
}
public class Cat : Animal
{
}
public class Owner
{
public int Id { get; set; }
[StringLength(64)]
public string Name { get; set; }
[StringLength(64)]
public string TableName { get; private set; }
public int? EntityId { get; private set; }
[NotMapped]
public Animal animal {
get {
if (EntityId == null || string.IsNullOrEmpty(TableName))
return null;
using (var context = new Context())
{
var type = typeof(Animal).Assembly.GetTypes().Where(x => x.Name.Contains(TableName)).First();
return (Animal)context.Set(type).Find(EntityId);
}
}
set {
//I assume that animal has already existed on this moment i.e. Id field is valid.
EntityId = value.Id;
TableName = value.GetType().Name;
}
}
}
IMPLEMENTATION:
var cat = context.Cats.Add(new Cat { Name = "Cat1" });
context.SaveChanges();
var owner = context.Owners.Add(new Owner { Name = "Owner", animal = cat });
context.SaveChanges();
var animal = owner.animal;
I have a NoteBrief
public int Id { get; set; }
public string Title { get; set; }
public DateTime Created { get; set; }
public int ParentNoteId { get; set; }
Data looks something like
1 Title1 03/31/1987 1
2 Title1 03/31/1988 1
3 Title3 01/01/2000 3
4 Title4 01/01/2001 4
5 Title4 01/01/2005 4
I want to do:
SELECT t1.*
FROM Notes AS t1 LEFT JOIN Notes AS t2
ON (t1.ParentNoteId = t2.ParentNoteId AND t1.Created < t2.Created)
WHERE t2.Created IS NULL;
Right now i have:
public IQueryable<NoteBrief> GetNotes()
{
return _ctx.Notes.Select(r => new NoteBrief
{
Id = r.Id,
Title = r.Title,
Created = r.Created,
ParentNoteId = r.ParentNoteId,
});
}
I'm happy with this, but really don't need the older revisions of a parentNoteId, just need the one that was created last so i can link to it.
I've read many examples, some of which use FirstOrDefault and some that use max. Everytime i try to implement an example though, it doesn't work for me.
Entity Framework creating IQuerable of the most recent
This is what finally worked for me:
return from e in _ctx.Notes
group e by e.ParentNoteId into g
select g.OrderByDescending(e => e.Created).FirstOrDefault() into r
select new NoteBrief
{
Id = r.Id,
Title = r.Title,
Created = r.Created,
ParentNoteId = r.ParentNoteId,
};
Also edited my original post with correct query i was going for.
Thanks.
Try following
return _ctx.Notes.Select(r => new NoteBrief
{
Id = r.Id,
Title = r.Title,
Created = r.Created,
ParentNoteId = r.ParentNoteId,
}).OrderBy(x=>x.Created).GroupBy(x=>new {Id=x.Id, Title=x.Title}).Select(x=>x.First()).AsQueryable();
}