Hello,
I have a need to generate SQL queries against a variety of database providers, for database schemas that are not known at compile time. I see that Entity Framework has already done the hard work of providing a SQL dialect called Entity SQL that is translated to native SQL before execution, and I was hoping to take advantage of that in some way.
Ideally, I'd like to just generate ESQL, run it, and get an IDataReader
, while Entity Framework worries about the provider-specific details. However, there doesn't seem to be a way to create an EntityConnection
without providing metadata in the form of SSDL, CSDL, and MSL files, and I won't find out about the database schema until runtime.
My question is, is there any way to take advantage of ESQL without any information about the database schema at compile time? If necessary (and possible), I would be open to programmatically generating the metadata from the database and caching it. I would also be open to any .NET tools that might be a better fit for my needs than Entity Framework.
Thanks for your time.
Update
Thanks to Alex's suggestion, I've been able to work out how to generate the metadata needed for an EntityConnection
on the fly, without writing out any files. As a result, I've been able to do exactly what I was hoping. Now all I need to do is figure out how to extract information about available tables/views for my own use from the generated metadata.
Here's my test code:
#r "System.Data.Entity"
#r "System.Data.Entity.Design"
#r "System.Transactions"
open System
open System.IO
open System.Data
open System.Data.EntityClient
open System.Data.Entity.Design
open System.Data.Mapping
open System.Data.Metadata.Edm
open System.Data.SqlClient
open System.Text
open System.Xml
let dbName = "Northwind"
let cnstr = sprintf "Server=.;Database=%s;Integrated Security=SSPI" dbName
let provider = "System.Data.SqlClient"
let mslText = StringBuilder()
let mslWriter = XmlWriter.Create(mslText)
let schemaGen = EntityStoreSchemaGenerator(provider, cnstr, dbName)
schemaGen.GenerateStoreMetadata() |> ignore
let modelGen = EntityModelSchemaGenerator(schemaGen.EntityContainer)
modelGen.GenerateMetadata() |> ignore
modelGen.WriteStorageMapping(mslWriter)
mslWriter.Close()
let mslReader = XmlReader.Create(new StringReader(mslText.ToString()))
let ssdlCollection = schemaGen.StoreItemCollection
let csdlCollection = modelGen.EdmItemCollection
let mslCollection = StorageMappingItemCollection(csdlCollection, ssdlCollection, [| mslReader |])
let mdw = MetadataWorkspace()
mdw.RegisterItemCollection(csdlCollection)
mdw.RegisterItemCollection(ssdlCollection)
mdw.RegisterItemCollection(mslCollection)
let sqlCn = new SqlConnection(cnstr)
let cn = new EntityConnection(mdw, sqlCn)
let cmd = cn.CreateCommand()
cmd.CommandText <- sprintf "SELECT p.ProductName FROM %sContext.Products AS p" dbName
cn.Open()
let reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)
while reader.Read() do
printfn "%A" reader.["ProductName"]
reader.Close()
cn.Close()
sqlCn.Close()