Konrud
Konrud

Reputation: 1114

Database-first approach in Entity Framework without auto generated code

I wonder, if there is any way , to use Database-first approach with manually generated classes (models) in advance(just like Code-first approach), but without using auto-generated code which Entity Framework creates using Database-first approach? I have 3 Classes(first two of them Student and Courses have many to many relationship), which represents models: First one is Student:

public class Student
{
    public int StudentID  { get; set;}

    public string Name  { get; set;}

    public DateTime BirthDate { get; set;}

    public ICollection<StudentToCourse> StudentToCourses { get; set; }

    public Student()
    {
        StudentToCourses = new List<StudentToCourse>();
    }
}

Then Course:

    public class Course
{
    public int CourseID { get; set; }

    public string CourseName { get; set; }

    public ICollection<StudentToCourse> StudentToCourses { get; set; }


    public Course()
    {
        StudentToCourses = new List<StudentToCourse>();
    }

}

And Relation/Intermediate Class with additional properties StudentToCourse:

    public class StudentToCourse
{
    [Key, Column(Order = 0)]
    public int StudentID { get; set; }
    [Key, Column(Order = 1)]
    public int CourseID { get; set; }
    [Key, Column(Order = 2)]
    public DateTime Date { get; set; }

    public virtual Student Student { get; set; }

    public virtual Course Course { get; set; }

    //public ICollection<Student> Students { get; set; }

    //public ICollection<Course> Courses { get; set; }

    public int Grade { get; set; }

}

Also, i created Database, using LocalDb feature in VS 2013

I have 3 Tables: Courses:

CREATE TABLE [dbo].[Courses]
(
[CourseID] INT NOT NULL PRIMARY KEY IDENTITY,
[CourseName] NVARCHAR(100) NOT NULL,
)

Students:

CREATE TABLE [dbo].[Students]
(
[StudentID] INT NOT NULL PRIMARY KEY IDENTITY,
[Name] NVARCHAR(50) NOT NULL,
[BirthDate] DATETIME NOT NULL,
)

Relation Table StudentsToCourses:

CREATE TABLE [dbo].[StudentsToCourses]
(
[StudentID] INT REFERENCES Students(StudentID) NOT NULL,
[CourseID] INT REFERENCES Courses(CourseID) NOT NULL,
[Date] DATETIME NOT NULL,
[Grade] INT NOT NULL,
PRIMARY KEY (StudentID, CourseID, Date) 
)

Unfortunately, i have no luck with this approach, i do get students' data but i don't receive data from relational table and i can't receive all related grades per student.

I searched for related topics in google and in stackoverflow , but all those topics weren't helpful for me, although the example above i found in this topic.

Upvotes: 0

Views: 2349

Answers (2)

Gert Arnold
Gert Arnold

Reputation: 109109

As I suspected, the problem is not whether or not you can have a database and a class model independently. Of course you can! All these generation tools and migration stuff only serve one goal: making life easier, help you keeping both models in sync. But you can do that job yourself just as well. The end result is always: two models that – at runtime – don't interact with each other whatsoever. (Don't interact? No, not as such. There must be a middleman, an ORM, to connect both worlds.)

The reason why you don't get data is because lazy loading does not occur. Your statement is

var listOfGrades = _context.Students.Where(s => s.Name.StartsWith("J"))
                   .FirstOrDefault().StudentToCourses;

This requires lazy loading, because the FirstOrDefault() statement executes the first part of the query. It renders a Student of which subsequently the StudentToCourses are accessed. But these don't load because the collection is not virtual. It should be

public virtual ICollection<StudentToCourse> StudentToCourses { get; set; }

This enables EF to override the collection in a dynamic proxy object that is capable of lazy loading.

But of course is is more efficient to get the collection in one statement, for example:

var listOfGrades = _context.Students.Include(s => s.StudentToCourses)
                   .Where(s => s.Name.StartsWith("J"))
                   .FirstOrDefault().StudentToCourses;

Upvotes: 1

Chris Pratt
Chris Pratt

Reputation: 239290

Yes, you can. You just need a context with no initialization strategy (so it doesn't try to create or migrate your existing database):

public class ExistingDatabaseContext : DbContext
{
    public ExistingDatabaseContext()
        : base("ExistingDatabaseConnectionStringName")
    {
        Database.SetInitializer<ExistingDatabaseContext>(null);
    }

    // DbSets here for your "code-first" classes that represent existing database tables
}

Just bear in mind that this context will not be capable of doing migrations or any other form of initialization, so if you have actual true code-first tables in there as well, you'll need a separate context to manage those.

Upvotes: 1

Related Questions