views:

27

answers:

1

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()
+1  A: 

There is a way to create an EntityConnection by pointing at appropriate metadata (CSDL, MSL and SSDL) at runtime.

In fact that is one of the key benefits of dropping down from the Object Layer (CLR classes) to the Entity Client (eSQL and DataReaders etc).

To help you get started this This post shows you how to create an EntityConnection at runtime.

Alex James
Thanks, that helps, but where do the CSDL, MSL, and SSDL files come from at runtime? Is there an API to generate these files when all I have to start with is a connection string and a data provider?
Joel Mueller
I think so, I'm unsure of the details, but what you should investigate is the System.Data.Entity.Design dll.
Alex James