Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating an EF CodeFirst DbContext using Roslyn

Just a little idea I'm playing with, not sure if it's viable or has much of a use.

I'm trying to generate a very basic EF Code First database using the Roslyn CTP.

Code:

var scriptEngine = new ScriptEngine(new[] { "System", "System.Core", typeof(DbContext).Assembly.Location });
var session = Roslyn.Scripting.Session.Create();

var t = scriptEngine.CompileSubmission<DbContext>(@"
  using System.Data.Entity;         
  public class Car  {
    public int Id {get; set;}
    public string Name {get;  set; }
  }

  public class Context : DbContext {
    public DbSet<Car> Cars {get; set; }
  }

  new Context();
", session);

t.Execute();

When executed I get the following exception

Exception:

The type 'Submission#0+Car' was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive, nested or generic, and does not inherit from EntityObject.

Looking through the list of possible issues, I'm guessing that Roslyn is making a nested class as part of the code gen. This makes sense otherwise the "new Context();" call would need to be wrapped into a class/method of some sort. I could emit an assembly, which would confirm the above but likely wouldn't have any clues on how to write it correctly.

I also went down the route of Syntax.ClassDeclaration, but ended up with a few hundred lines of code just to make a class with 1 property and no obvious way how to instantiate that class.

Question

Is there an easy way to create a class in Roslyn that is publicly accessible (eg not nested in another class)?

like image 485
Betty Avatar asked Feb 05 '12 01:02

Betty


People also ask

How do you change the state of entity using DbContext?

This can be achieved in several ways: setting the EntityState for the entity explicitly; using the DbContext. Update method (which is new in EF Core); using the DbContext. Attach method and then "walking the object graph" to set the state of individual properties within the graph explicitly.

What is the difference between DbSet and DbContext?

Intuitively, a DbContext corresponds to your database (or a collection of tables and views in your database) whereas a DbSet corresponds to a table or view in your database.


1 Answers

You can use Roslyn to create actual DLL library that contains your type based on your source code and then use that from your script:

var classCode = @"
using System.Data.Entity;   

public class Car  {
    public int Id { get; set; }
    public string Name { get;  set; }
}

public class Context : DbContext {
    public DbSet<Car> Cars { get; set; }
}";

var syntaxTree = SyntaxTree.ParseCompilationUnit(classCode);

var compilation = Compilation.Create(
    "car",
    new CompilationOptions(assemblyKind: AssemblyKind.DynamicallyLinkedLibrary))
    .AddReferences(
        new AssemblyFileReference(typeof(object).Assembly.Location), // mscorlib
        new AssemblyFileReference(typeof(Uri).Assembly.Location), // System
        new AssemblyFileReference(typeof(IOrderedQueryable<>).Assembly.Location), // System.Data
        new AssemblyFileReference(typeof(DbContext).Assembly.Location) // EntityFramework
    )
    .AddSyntaxTrees(syntaxTree);

var dllPath = "car.dll";
using (var stream = File.OpenWrite(dllPath))
{
    compilation.Emit(stream);
}

var code = @"new Context();";
var scriptEngine = new ScriptEngine(new[] { new FileInfo(dllPath).FullName, "EntityFramework" });

var context = scriptEngine.Execute<DbContext>(code);
like image 195
svick Avatar answered Sep 28 '22 09:09

svick