0

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.

Community
  • 1
  • 1
Konrud
  • 1,114
  • 9
  • 19
  • 1
    Why doesn't Code First work for you? – Robert Harvey Sep 03 '14 at 19:20
  • Yes you can, but why? – jamesSampica Sep 03 '14 at 19:20
  • Yes it can be done, but the real question is WHY? Database-First allows EF to make sure everything works properly. Code-First requires you to intimately know your object graph and create everything accordingly. A hybrid is simply not a good idea and one reason is because a change will actually require *much more work* then either of the defaults. Most likely this is an [XY Problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem), that is you *think* that this solution which you can't get to work will solve a root problem, but you haven't described the root problem. – Erik Philips Sep 03 '14 at 19:22
  • I want to simplify it as much as possible, also i want to have full control over the database. – Konrud Sep 03 '14 at 19:24
  • What you are asking for is not simple. You can have full control over the database with Database-First and Code-First. [What if My Database Changes - Code First: If your database schema changes you can either manually edit the classes or perform another reverse engineer to overwrite the classes.](http://msdn.microsoft.com/en-us/data/jj200620.aspx) – Erik Philips Sep 03 '14 at 19:27
  • But with Database-First i have a lot of auto-generated code, which i don't want. – Konrud Sep 03 '14 at 19:29
  • The whole point of generating that code is so that you don't have to write it yourself. – Robert Harvey Sep 03 '14 at 19:38
  • Your code answers your own question. The real question is why you don't receive expected data. You should show the code where you try that. – Gert Arnold Sep 03 '14 at 22:50
  • Y'all need to realize that in the real world (TM) Code-First is not always a good idea, especially in cases where you're dealing with Enterprise data systems that have existed long before your application and aren't going to change just because you came along. That said, this is a legitimate question and Database-First is a legitimate strategy. The problem here, though, is probably not related to Code- or Database-First, but to data access and setup. Can we see the access code you're using? – Steve G Sep 04 '14 at 01:26
  • @GertArnold sorry for late answer here is my code where i try to receive the data i.e. StudentToCourses (The courses and the grades for the specific student): `var listOfGrades = _context.Students.Where(s => s.Name.StartsWith("J")).FirstOrDefault().StudentToCourses;` But i get an empty collection. – Konrud Sep 04 '14 at 15:27

2 Answers2

1

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.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Thanks for your answer but it doesn't work for me , i still get an empty collection of ` public ICollection StudentToCourses`. I think there is some problem with Intermediate Class\Table for some reason EF doesn't like it. – Konrud Sep 04 '14 at 15:36
1

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;
Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
  • Thanks for your answer, But... First of all as i know Include method gets string as its incoming parameter. Second I actually defined StudentToCourses as virtual (as you proposed), now i get an exception "Invalid object name 'dbo.StudentToCourses'" here is the code: `var studentGrades = _context.Students.Include("StudentToCourses").Where(s => s.Name == "John").FirstOrDefault().StudentToCourses;` – Konrud Sep 05 '14 at 08:07
  • The include parameter depends on the EF version you use, or whether you have `using System.Data.Entity.DbExtensions`. The table name in your DDL script is `StudentsToCourses` (Students with an "s"). – Gert Arnold Sep 05 '14 at 09:08
  • I'm using EF 6.0 , regarding System.Data.Entity.DbExtensions, nice catch , thanks i forgot this, you're right . I Tried StudentsToCourses also, it throws me the same exception . – Konrud Sep 05 '14 at 09:40