2012-02-05 15 views
5

Solo una pequeña idea con la que estoy jugando, no estoy seguro de si es viable o si tiene mucha utilidad.Creando un EF CodeFirst DbContext usando Roslyn

Estoy tratando de generar una base de datos EF Code First muy básica usando el CTP de Roslyn.

Código:

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

cuando se ejecuta consigo la siguiente excepción

Excepción:

El tipo 'Presentación # 0 + Car' no fue mapeado. Compruebe que el tipo no se haya excluido explícitamente mediante el método Ignore o la anotación de datos NotMappedAttribute. Verifique que el tipo se haya definido como una clase, que no sea primitivo, anidado o genérico, y que no herede de EntityObject.

Mirando la lista de posibles problemas, supongo que Roslyn está haciendo una clase anidada como parte del código gen. Esto tiene sentido de lo contrario el "nuevo contexto();" la llamada debería ser envuelta en una clase/método de algún tipo. Podría emitir un ensamblado, lo que confirmaría lo anterior, pero probablemente no tenga ninguna pista sobre cómo escribirlo correctamente.

También fui por la ruta de Syntax.ClassDeclaration, pero terminé con unos cientos de líneas de código solo para hacer una clase con 1 propiedad y no hay manera obvia de crear una instancia de esa clase.

Pregunta

¿Hay una manera fácil de crear una clase en Roslyn que es accesible al público (por ejemplo, no anidada en otra clase)?

Respuesta

5

Puede utilizar Roslyn para crear la biblioteca DLL real que contiene el tipo basado en el código fuente y luego usar que a partir de la secuencia de comandos:

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

necesaria una ligera modificación, que pasa la ruta completa de EF para el guión engine - typeof (DbContext) .Assembly.Location. De lo contrario, funciona a la perfección. – Betty

+0

¿conoce alguna forma de cargar ese ensamblaje creado en scriptengine sin escribirlo en el disco? ScriptEngine parece arrojar una excepción "Ruta no puede estar vacía" si paso un ensamblado creado por el flujo de memoria -> bytes -> método de ensamblaje. – Betty

+2

Sí, parece que no es posible. Lo intenté con un ensamblaje dinámico, y tampoco funcionó. – svick

Cuestiones relacionadas