Skip to content

Instantly share code, notes, and snippets.

@vkhorikov
Last active January 13, 2023 19:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vkhorikov/af1761ef883227eca7c72b8932a04942 to your computer and use it in GitHub Desktop.
Save vkhorikov/af1761ef883227eca7c72b8932a04942 to your computer and use it in GitHub Desktop.
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; }
}
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; }
}
Student student = context.Students.Find(id);
int favoriteCourseId = student.FavoriteCourse.Id;
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;
}
}
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();
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;
}
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();
}
modelBuilder.Entity<Student>(x =>
{
// ...
x.Metadata.FindNavigation("Enrollments").SetPropertyAccessMode(PropertyAccessMode.Field);
});
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();
}
public class StudentMap : ClassMap<Student>
{
public StudentMap()
{
HasMany(x => x.Enrollments).Access.CamelCaseField(Prefix.Underscore);
}
}
int count = Enrollments.Count;
SELECT COUNT(*) FROM dbo.Enrollment WHERE StudentID = @StudentID
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);
}
}
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);
}
}
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);
}
}
public class Student : Entity
{
public void DeleteEnrollment(Enrollment enrollment)
{
int hack = Enrollments.Count; // Force to initialize the backing field
_enrollments.Remove(enrollment);
}
}
modelBuilder.Entity<Student>(x =>
{
// ...
x.HasMany(p => p.Enrollments).WithOne(p => p.Student).OnDelete(DeleteBehavior.Cascade);
});
public class StudentMap : ClassMap<Student>
{
public StudentMap()
{
HasMany(x => x.Enrollments).Cascade.AllDeleteOrphan();
}
}
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;
}
}
modelBuilder.Entity<Student>(x =>
{
// ...
x.Property(p => p.Email)
.HasConversion(p => (string)p, p => Email.Create(p));
});
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);
}
}
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;
}
}
using (SchoolContext context = new SchoolContext(optionsBuilder.Options))
{
var student = new Student("First Name", "Last name");
context.Students.Add(student);
context.SaveChanges();
}
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();
}
public class StudentMap : ClassMap<Student>
{
public StudentMap()
{
// ...
Component(x => x.Address, y =>
{
y.Map(x => x.Street);
y.Map(x => x.City);
});
}
}
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; }
}
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);
});
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;
}
}
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;
}
}
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; }
}
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();
}
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();
}
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!
}
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");
}
}
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