使用Roslyn创建EF CodeFirst DbContext

时间:2012-02-05 01:06:41

标签: ef-code-first roslyn

我正在玩一个小小的想法,不确定它是否可行或有多大用处。

我正在尝试使用Roslyn CTP生成一个非常基本的EF Code First数据库。

代码:

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();

执行时我得到以下异常

例外:

  

未提供“提交#0 + Car”类型。使用Ignore方法或NotMappedAttribute数据批注检查未明确排除类型。验证类型是否已定义为类,不是原始类,嵌套类还是通用类,并且不从EntityObject继承。

查看可能出现的问题列表,我猜测Roslyn正在将嵌套类作为代码生成的一部分。这有意义,否则“新的Context();”调用需要包装到某种类/方法中。我可以发出一个程序集,它可以确认上述内容,但可能没有任何关于如何正确编写它的线索。

我也走了Syntax.ClassDeclaration的路线,但最终得到了几百行代码,只是为了创建一个具有1个属性的类,并没有明显的方法来实例化该类。

问题

是否有一种简单的方法可以在Roslyn中创建一个可公开访问的类(例如,没有嵌套在另一个类中)?

1 个答案:

答案 0 :(得分:5)

您可以使用Roslyn根据源代码创建包含您的类型的实际DLL库,然后使用脚本中的那个:

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);