Core
List<Customer> Customers = new List<Customer>();
string conntionStr = "Data source=localhost; initial catalog=SubwayDB; integrated security=true";
SqlConnection sqlConn = new SqlConnection(connectionStr);
string Query = "Select * from Customers where FirsName=@FN";
SqlCommand sqlComm = new SqlCommand(Query, sqlConn);
sqlComm.CommandType = System.Data.CommandType.Text;
sqlComm.Parameters.AddWithValue("@FN", "test").DbType = System.Data.DbType.Int16;
sqlConn.Open();
SqlDataReader reader = sqlComm.ExecuteReader();
while (reader.Read())
{
Customers.Add(new Customer(){
FirstName = reader["FirstName"].ToString(),
LastName = reader["LastName"].ToString(),
Email = reader["Email"].ToString(),
});
}
reader.Close();
sqlConn.Close();
What are the problems?
•SQL Code
•Vendor dependent
•Not compiled & Runtime errors
•SQL is a string
•Connection/resource management
•Manual conversion to object instances
•Conversion of datatypes
•SQL Types != C# Types
•Labour intensive
•Errors
•Changes in database or C# code
•Database dependent (C#)
course
public class Student
{
public int StudentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Study Study { get; set; }
public List<Degree> Degrees { get; set; }
}SQL Lite has some drawbacks, it's better to choose a real relational database!
public class Student
{
public int StudentId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Study Study { get; set; }
public List<Degree> Degrees { get; set; }
}
public class Study
{
public int StudyId { get; set; }
public string UniversityName { get; set; }
public string Name { get; set; }
public DateTime Completion { get; set; }
}
public class Degree
{
public int DegreeId { get; set; }
public string Name { get; set; }
public DateTime Received { get; set; }
}public class UniversityDbContext : DbContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Study> Studies { get; set; }
public DbSet<Degree> Degrees { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlite("Data Source=University.db");
}
}using (var db = new UniversityDbContext())
{
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
} Model = Domain Model (entities) + DabaseContext
using (var db = new UniversityDbContext())
{
db.ConfigureLogging(s => Console.WriteLine(s), LoggingCategories.SQL);
var cs = new Study() {
Name = "Computer Science"
};
db.Students.Add(new Student() {
FirstName = "Jan",
LastName = "Hendriks",
Study = cs,
Degrees = new List<Degree>() {
new Degree() {
Name = "bachelor computer science",
Received = new DateTime(2011, 6, 1)
}
//... add more data
}
});
//add more data
db.SaveChanges();
}public class Employee
{
public int EmployeeId { get; set; }
[Required, MinLength(3), MaxLength(50)]
public string FirstName { get; set; }
[Required, MinLength(3), MaxLength(50)]
public string LastName { get; set; }
[NotMapped]
public string FullName {
get { return $"{FirstName} {LastName}"; }
}
[Required, EmailAddress]
public string Email { get; set; }
[Phone]
public int Phone { get; set; }
[Range(1, 5)]
public int WorkdayCountInWeek { get; set; }
[Required, Range(0, 5000)]
public int Salary { get; set; }
[Url]
public string IntranetPage { get; set; }
}
Problem is:
public class EmployeeContext : DbContext
{
public DbSet<Employee> Employees { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.ToTable("Emp");
modelBuilder.Entity<Employee>()
.Property(x => x.FirstName)
.IsRequired().HasMaxLength(50);
modelBuilder.Entity<Employee>()
.Property(x => x.Phone)
.HasColumnName("Phone Number");
base.OnModelCreating(modelBuilder);
}
} public class Student
{
public int StudentId { get; set; }
....
public ContactInfo ContactInfo { get; set; }
public int StudyId { get; set; }
public Study Study { get; set; }
...
}
public class ContactInfo
{
public int ContactInfoId { get; set; }
public int StudentId { get; set; }
public Student Student { get; set; }
...
}Principle
Dependent
public class Author
{
public int AuthorId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<Book> Books { get; set; }
}
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public int AuthorId { get; set; }
public Author Author { get; set; }
}
public class CourseStudent
{
public int CourseId { get; set; }
public Course Course { get; set; }
public int StudentId { get; set; }
public Student Student { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<CourseStudent>()
.HasKey(x => new {x.CourseId, x.StudentId});
}using (var db = new UniversityDbContext())
{
db.ConfigureLogging(s => Console.WriteLine(s), LoggingCategories.SQL);
Student student = db.Students.Find(1);
db.Students.Remove(student);
db.SaveChanges();
}using (var db = new UniversityDbContext())
{
db.ConfigureLogging(s => Console.WriteLine(s), LoggingCategories.SQL);
Student student = db.Students
.Include(x => x.Degrees)
.Single(x => x.StudentId == 1);
foreach (var degree in student.Degrees)
{
db.Degrees.Remove(degree);
}
db.Students.Remove(student);
db.SaveChanges();
}
var studentDegrees = db.Students.Where(x => x.StudentId == 1)
.Select(x => new
{
x.LastName, x.FirstName,
x.Degrees,
DegreeBefore2014 = x.Degrees.Where(d => d.Received < new DateTime(2014, 1, 1))
})
.ToList();in EFC 6+
var students = db.Students.Where(x => x.StudentId == 1)
.Include(x => x.CourseStudents)
.ThenInclude(x => x.Course)
.ToList();
foreach (var student in students)
{
Console.WriteLine(new string('-', 20));
Console.WriteLine($"{student.LastName}, {student.FirstName.First()} ");
foreach (var cs in student.CourseStudents)
{
Console.WriteLine(cs.Course.Name);
}
Console.WriteLine(new string('-', 20));
}The prefered way to work (my opinion)!!!!
var students = db.Students;
foreach (Student student in students)
{
if (student.LastName == "Hendriks")
{
db.Entry(student).Reference<Study>(x => x.Study).Load();
if (student.Study.Name == "Computer Science")
{
Console.WriteLine($"{student.LastName}");
db.Entry(student).Collection(x => x.Degrees)
.Query()
.Where(x => x.Received < new DateTime(2014, 1, 1))
.Load();
foreach (var degree in student.Degrees)
{
Console.WriteLine($"{degree.Name}");
}
}
}
}!!!!Be careful, can be expensive!!!! N+1 Query Problem!
SELECT * FROM Students /* return n records */
SELECT * FROM Degrees WHERE StudentId = 1
SELECT * FROM Degrees WHERE StudentId = 2
...
SELECT * FROM Degrees WHERE StudentId = n
var csStudents = db.Students.Where(x => x.Study.Name == "Computer Science");
foreach (var csStudent in csStudents)
{
Console.WriteLine($"Name: {csStudent.LastName}, {csStudent.FirstName.First()}");
}var csStudents2 = db.Students.Where(x => x.Study.Name == "Computer Science").ToList();
var csStudents = db.Students.Where(x => x.Study.Name == "Computer Science")
foreach (var csStudent in csStudents)
{
//Do lots of work
}var csStudents = db.Students.Where(x => x.Study.Name == "Computer Science").ToList();
foreach (var csStudent in csStudents)
{
//Do lots of work
}Student s1 = db.Students.Single(x => x.LastName == "Lops");
//exception if no student exists with Lastname equals "Lops"
Student s2 = db.Students.SingleOrDefault(x => x.LastName == "Lops");
if (s2 == null)
{
//do something
}
var studentNames = studentList.Where(s => s.Age > 18)
.Select(s => s)
.Where(st => st.StandardID > 0)
.Select(s => s.StudentName);var teenStudentsName = from s in studentList
where s.age > 12 && s.age < 20
select new { StudentName = s.StudentName };
public static class ExtensionMethodsExamples
{
public static string UppercaseFirstLetter(this string value)
{
if (value?.Length > 0)
{
return char.ToUpper(value[0]) + value.Substring(1);
}
return value;
}
}
Console.WriteLine("aap noot mies".UppercaseFirstLetter());Take a look at all the different syntaxis!
Lambda: short way to represent an anonymous method
public static IEnumerable<string> UppercaseFirstLetter(this IEnumerable<string> values)
{
foreach (var value in values) {
yield return value.UppercaseFirstLetter();
}
}
"aap noot mies".Split(" ").UppercaseFirstLetter()
.ToList().ForEach(x => Console.WriteLine(x));
public static IEnumerable<TSource>
Filter<TSource>(this IEnumerable<TSource> values, Predicate<TSource> predicate)
{
foreach (var value in values) {
if (predicate(value)) {
yield return value;
}
}
}
"Aap noot Mies".Split(" ")
.Filter(x => x.Length > 0 && char.IsUpper(x[0]))
.ToList()
.ForEach(x => Console.WriteLine(x));
public static IEnumerable<TSource> Where<TSource>
(this IEnumerable<TSource> source, Func<TSource,int,bool> predicate);
int[] numbers = { 0, 30, 20, 15, 90, 85, 40, 75 };
IEnumerable<int> query =
numbers.Where((number, index) => number <= index * 10);
public static IEnumerable<TSource> Where<TSource>
(this IEnumerable<TSource> source, Func<TSource,bool> predicate);
List<string> fruits =
new List<string> { "apple", "passionfruit", "banana", "mango",
"orange", "blueberry", "grape", "strawberry" };
IEnumerable<string> query = fruits.Where(fruit => fruit.Length < 6);using (var db = new UniversityDbContext())
{
db.ConfigureLogging(s => Console.WriteLine(s), LoggingCategories.SQL);
var csStudents = db.Students.Where(x => x.Study.Name == "Computer Science");
foreach (var csStudent in csStudents)
{
Console.WriteLine($"Name: {csStudent.LastName}, {csStudent.FirstName.First()}");
}
}using (var db = new UniversityDbContext())
{
db.ConfigureLogging(s => Console.WriteLine(s), LoggingCategories.SQL);
var csStudents = from s in db.Students
where s.Study.Name == "Computer Science"
select s;
foreach (var csStudent in csStudents)
{
Console.WriteLine($"Name: {csStudent.LastName}, {csStudent.FirstName.First()}");
}
}Microsoft.EntityFrameworkCore.Design
dotnet tool install --global dotnet-ef
dotnet ef migrations add init
dotnet ef migrations add emailToStudent
dotnet ef database update
Add Email
Required!
From database to domain classes (Entities)
public static void ConnectedObjectUpdate()
{
Student studentToUpdate = null;
using (var db = new UniversityDbContext())
{
studentToUpdate = db.Find<Student>(1);
studentToUpdate.FirstName += studentToUpdate.FirstName.First();
db.SaveChanges();
}
}
public static void DisconnectObjectUpdate()
{
Student studentToUpdate = null;
using (var db = new UniversityDbContext())
{
studentToUpdate = db.Find<Student>(1);
}
studentToUpdate.FirstName += studentToUpdate.FirstName.First();
using (var db = new UniversityDbContext())
{
db.Students.Update(studentToUpdate);
db.SaveChanges();
}
}EntityState: Unchanged, Added, Modified, Delete, Detached
private static void DisplayStates(IEnumerable<EntityEntry> entries)
{
foreach (var entry in entries)
{
Console.WriteLine($"Entity: {entry.Entity.GetType().Name},
State: {entry.State.ToString()} ");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().Property<DateTime>("LastModified");
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
modelBuilder.Entity(entityType.Name).Property<DateTime>("LastModified");
}
}Query of shadow properties
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries()
.Where(x => x.State == EntityState.Added || x.State == EntityState.Modified))
{
entry.Property("LastModified").CurrentValue = DateTime.Now;
}
return base.SaveChanges();
}//disable tracking for all operations on context
db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
//no tracking for one query
db.Students.AsNoTracking();[Fact]
public void TestSQLiteOk()
{
//SETUP
//Here create the options using SQLite CreateOptions
var options = SqliteInMemory
.CreateOptions<MyContext>();
//I create an instance of my DbContext
using (var context = new MyContext(options))
{
//You have to create the database
context.Database.EnsureCreated();
context.SeedDatabaseFourBooks();
//ATTEMPT
context.Books.First().Title = "New Title";
context.SaveChanges();
//VERIFY
context.Books.First().Title.ShouldEqual("New Title");
}
}