Last active
January 13, 2023 19:52
-
-
Save vkhorikov/af1761ef883227eca7c72b8932a04942 to your computer and use it in GitHub Desktop.
EF Core 2.1 vs NHibernate 5.1: https://enterprisecraftsmanship.com/2018/06/13/ef-core-vs-nhibernate-ddd-perspective/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
public string FirstName { get; private set; } | |
public string LastName { get; private set; } | |
public int FavoriteCourseId { get; private set; } // Foreign key | |
} | |
public class Course : Entity | |
{ | |
public string Title { get; private set; } | |
public int Credits { get; private set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
public string FirstName { get; private set; } | |
public string LastName { get; private set; } | |
public virtual Course FavoriteCourse { get; private set; } | |
} | |
public class Course : Entity | |
{ | |
public string Title { get; private set; } | |
public int Credits { get; private set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Student student = context.Students.Find(id); | |
int favoriteCourseId = student.FavoriteCourse.Id; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Course course = context.Courses.First(); | |
var student = new Student("First Name", "Last name", course); | |
context.Students.Add(student); // EF6 tries to insert course along with student | |
context.SaveChanges(); | |
// Student.cs | |
public class Student : Entity | |
{ | |
public string FirstName { get; private set; } | |
public string LastName { get; private set; } | |
public virtual Course FavoriteCourse { get; set; } | |
public Student(string firstName, string lastName, Course favoriteCourse) | |
{ | |
FirstName = firstName; | |
LastName = lastName; | |
FavoriteCourse = favoriteCourse; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Course course = context.Courses.First(); | |
var student = new Student("First Name", "Last name", course); | |
context.Entry(course).State = EntityState.Unchanged; // or EntityState.Modified | |
context.Students.Add(student); | |
context.SaveChanges(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Course courseCached; | |
using (SchoolContext context = new SchoolContext(optionsBuilder.Options)) | |
{ | |
courseCached = context.Courses.Last(); | |
} | |
using (SchoolContext context = new SchoolContext(optionsBuilder.Options)) | |
{ | |
Student student = context.Students.Find(id); | |
student.FavoriteCourse = courseCached; | |
context.SaveChanges(); // Throws. Must use context.Entry(courseCached).State = EntityState.Detached; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
private readonly List<Enrollment> _enrollments = new List<Enrollment>(); | |
public virtual IReadOnlyList<Enrollment> Enrollments => _enrollments.ToList(); | |
public void AddEnrollment(Course course, Grade grade) | |
{ | |
var enrollment = new Enrollment | |
{ | |
Course = course, | |
Student = this, | |
Grade = grade | |
}; | |
_enrollments.Add(enrollment); | |
} | |
} | |
// Usage example | |
using (SchoolContext context = new SchoolContext(optionsBuilder.Options)) | |
{ | |
Student student = context.Students.Find(1); | |
student.AddEnrollment(context.Courses.First(), Grade.B); | |
context.SaveChanges(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
modelBuilder.Entity<Student>(x => | |
{ | |
// ... | |
x.Metadata.FindNavigation("Enrollments").SetPropertyAccessMode(PropertyAccessMode.Field); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
// The field's type is ICollection, not List | |
private readonly ICollection<Enrollment> _enrollments = new List<Enrollment>(); | |
public virtual IReadOnlyList<Enrollment> Enrollments => _enrollments.ToList(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class StudentMap : ClassMap<Student> | |
{ | |
public StudentMap() | |
{ | |
HasMany(x => x.Enrollments).Access.CamelCaseField(Prefix.Underscore); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
int count = Enrollments.Count; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SELECT COUNT(*) FROM dbo.Enrollment WHERE StudentID = @StudentID |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
private readonly List<Enrollment> _enrollments = new List<Enrollment>(); | |
public virtual IReadOnlyList<Enrollment> Enrollments => _enrollments.ToList(); | |
public void AddEnrollment(Course course, Grade grade) | |
{ | |
if (_enrollments.Count >= 5) // Invariant protection | |
throw new InvalidOperationException(); | |
var enrollment = new Enrollment | |
{ | |
Course = course, | |
Student = this, | |
Grade = grade | |
}; | |
_enrollments.Add(enrollment); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
public void AddEnrollment(Course course, Grade grade) | |
{ | |
if (Enrollments.Count >= 5) // Call to the navigation property instead of the backing field | |
throw new InvalidOperationException(); | |
// ... | |
_enrollments.Add(enrollment); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
private readonly List<Enrollment> _enrollments = new List<Enrollment>(); | |
public virtual IReadOnlyList<Enrollment> Enrollments => _enrollments.ToList(); | |
public void DeleteEnrollment(Enrollment enrollment) | |
{ | |
_enrollments.Remove(enrollment); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
public void DeleteEnrollment(Enrollment enrollment) | |
{ | |
int hack = Enrollments.Count; // Force to initialize the backing field | |
_enrollments.Remove(enrollment); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
modelBuilder.Entity<Student>(x => | |
{ | |
// ... | |
x.HasMany(p => p.Enrollments).WithOne(p => p.Student).OnDelete(DeleteBehavior.Cascade); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class StudentMap : ClassMap<Student> | |
{ | |
public StudentMap() | |
{ | |
HasMany(x => x.Enrollments).Cascade.AllDeleteOrphan(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
public Email Email { get; private set; } | |
} | |
public class Email : ValueObject | |
{ | |
private readonly string _value; | |
private Email(string value) | |
{ | |
_value = value; | |
} | |
public static Email Create(string email) | |
{ | |
if (string.IsNullOrWhiteSpace(email)) | |
throw new InvalidOperationException(); | |
if (!Regex.IsMatch(email, @"^(.+)@(.+)$")) | |
throw new InvalidOperationException(); | |
return new Email(email); | |
} | |
public static implicit operator string(Email email) | |
{ | |
return email._value; | |
} | |
protected override IEnumerable<object> GetEqualityComponents() | |
{ | |
yield return _value; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
modelBuilder.Entity<Student>(x => | |
{ | |
// ... | |
x.Property(p => p.Email) | |
.HasConversion(p => (string)p, p => Email.Create(p)); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
private string _email; | |
public Email Email | |
{ | |
get => Email.Create(_email); | |
protected set => _email = value; | |
} | |
} | |
public class StudentMap : ClassMap<Student> | |
{ | |
public StudentMap() | |
{ | |
// ... | |
Map(x => x.Email).CustomType<string>().Access.CamelCaseField(Prefix.Underscore); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
public string FirstName { get; private set; } | |
public string LastName { get; private set; } | |
public virtual Address Address { get; private set; } // Can be null | |
protected Student() | |
{ | |
} | |
public Student(string firstName, string lastName, Address address = null) | |
{ | |
FirstName = firstName; | |
LastName = lastName; | |
Address = address; | |
} | |
} | |
public class Address : ValueObject | |
{ | |
public string City { get; } | |
public string Street { get; } | |
public Address(string city, string street) | |
{ | |
City = city; | |
Street = street; | |
} | |
protected override IEnumerable<object> GetEqualityComponents() | |
{ | |
yield return City; | |
yield return Street; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using (SchoolContext context = new SchoolContext(optionsBuilder.Options)) | |
{ | |
var student = new Student("First Name", "Last name"); | |
context.Students.Add(student); | |
context.SaveChanges(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using (SchoolContext context = new SchoolContext(optionsBuilder.Options)) | |
{ | |
var emptyAddress = new Address(null, null); | |
var student = new Student("First Name", "Last name", emptyAddress); | |
context.Students.Add(student); | |
context.SaveChanges(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class StudentMap : ClassMap<Student> | |
{ | |
public StudentMap() | |
{ | |
// ... | |
Component(x => x.Address, y => | |
{ | |
y.Map(x => x.Street); | |
y.Map(x => x.City); | |
}); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Person : Entity | |
{ | |
public string FirstName { get; private set; } | |
public string LastName { get; private set; } | |
} | |
public class Student : Person | |
{ | |
public virtual Address Address { get; private set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Address : ValueObject | |
{ | |
public string City { get; } | |
public string Street { get; } | |
public Address(string city, string street) // Must have this ... | |
{ | |
City = city; | |
Street = street; | |
} | |
protected override IEnumerable<object> GetEqualityComponents() | |
{ | |
yield return City; | |
yield return Street; | |
} | |
} | |
modelBuilder.Entity<Address>(x => | |
{ | |
x.Property(p => p.City).UsePropertyAccessMode(PropertyAccessMode.Field); // ... and these | |
x.Property(p => p.Street).UsePropertyAccessMode(PropertyAccessMode.Field); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Address : ValueObject | |
{ | |
public string City { get; set; } | |
public string Street { get; set; } | |
public Country Country { get; set; } | |
protected override IEnumerable<object> GetEqualityComponents() | |
{ | |
yield return City; | |
yield return Street; | |
yield return Country; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Address : ValueObject | |
{ | |
public string City { get; } | |
public string Street { get; } | |
public Country Country { get; } | |
public Address(string city, string street, Country country) | |
{ | |
City = city; | |
Street = street; | |
Country = country; | |
} | |
protected override IEnumerable<object> GetEqualityComponents() | |
{ | |
yield return City; | |
yield return Street; | |
yield return Country; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student2Instructor | |
{ | |
public int StudentId { get; set; } // Must have this | |
public virtual Student Student { get; set; } | |
public int InstructorId { get; set; } // And this | |
public virtual Instructor Instructor { get; set; } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
public string FirstName { get; private set; } | |
public string LastName { get; private set; } | |
private readonly List<Student2Instructor> _student2Instructors = new List<Student2Instructor>(); | |
public virtual IReadOnlyList<Instructor> Instructors => _student2Instructors.Select(x => x.Instructor).ToList(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
public string FirstName { get; private set; } | |
public string LastName { get; private set; } | |
private readonly List<Student2Instructor> _student2Instructors = new List<Student2Instructor>(); | |
public virtual IReadOnlyList<Student2Instructor> Student2Instructors => _student2Instructors.ToList(); | |
public virtual IReadOnlyList<Instructor> Instructors => Student2Instructors.Select(x => x.Instructor).ToList(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using (SchoolContext context = new SchoolContext(optionsBuilder.Options)) | |
{ | |
Student student = context.Students.Find(1); // Triggers a load - OK | |
IReadOnlyList<Instructor> instructors = student.Instructors; // Triggers a load - OK | |
Instructor instructor1 = instructors[0]; // Triggers a load - !NOT OK! | |
Instructor instructor2 = instructors[1]; // Triggers a load - !NOT OK! | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Student : Entity | |
{ | |
public virtual string FirstName { get; private set; } | |
public virtual string LastName { get; private set; } | |
private readonly IList<Instructor> _instructors = new List<Instructor>(); | |
public virtual IReadOnlyList<Instructor> Instructors => _instructors.ToList(); | |
} | |
public class StudentMap : ClassMap<Student> | |
{ | |
public StudentMap() | |
{ | |
HasManyToMany<Instructor>(Reveal.Member<Student>("_instructors")) | |
.Access.Field() | |
.Table("Student2Instructor"); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using (SchoolContext context = new SchoolContext(optionsBuilder.Options)) | |
{ | |
// Do something here | |
List<IDomainEvent> domainEvents = context.ChangeTracker.Entries().Where(x => x.Entity is Entity).SelectMany(x => ((Entity)x.Entity).DomainEvents).ToList(); | |
context.SaveChanges(); | |
foreach (IDomainEvent domainEvent in domainEvents) | |
{ | |
DomainEvents.Dispatch(domainEvent); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment