Using NHibernate to execute DDL statements - nhibernate

How can I execute a DDL statement via NHibernate?
To be clear, I don't want to auto-generate my schema from my mapping files. My DDL is stored in plain text files along the lines of:
CREATE TABLE Foo (Bar VARCHAR(10))
GO
CREATE TABLE Hello (World INTEGER)
GO
I want to cycle through these in order and execute each of them. I could just open a SqlConnection and execute via a SqlCommand but I'd like to go through NHibernate if there is a nice way to do this. This is mainly because I want to remain as database agnostic as possible: I have a SQL db now but I might need to implement Oracle or DB2 later...
I'm using .Net v3.51 and NHibernate v2.1. I looked at the NHibernate SchemaExport class but couldn't see a way to use this for this purpose.

I've used session.Connection.CreateCommand and session.Transaction.EnlistCommand before with success to run raw SQL.
Here's a snippet of something similar that I've done:
using (var command = _session.Connection.CreateCommand())
{
_session.Transaction.Enlist(command);
command.CommandText = "select foo from bar where id = #id";
var versionIdParameter = command.CreateParameter();
versionIdParameter.ParameterName = "id";
versionIdParameter.Value = id;
command.Parameters.Add(versionIdParameter);
using(var reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
{
while (reader.Read())
// ...
}
}

You can get an IDbConnection from an ISession's Connection property but you'll need to do this with SqlCommand. Executing DDL is outside of NHibernate's scope.

Related

Create table Routine definition

I've been using this table INFORMATION_SCHEMA.ROUTINES which has the routine definition for procs and functions, i was wondering if there is a similar table for create table and create view routines.
I would just skip using INFORMATION_SCHEMA.ROUTINES entirely. Instead look at sys.sql_modules. It has the ddl for everything you are looking for except tables. But my question is why do you need to find the ddl for all these things?
You can use sys.sql_modules to find the definition of views. For tables, one option is SMO objects. The C# example below returns the DDL for a table in the listed database. This will require references to the Microsoft.SqlServer.Management.Sdk.Sfc, Microsoft.SqlServer.Smo, and Microsoft.SqlServer.ConnectionInfo namespaces. System.Collections.Specialized is also used, but only for StringCollection in this example. This can be filtered using the Name property of the Table class as noted below.
//set source server and database using SMO objects
Server srv = new Server(#"YourServer");
//for Windows Authentication
srv.ConnectionContext.LoginSecure = true;
srv.ConnectionContext.StatementTimeout = 600;
Database db = srv.Databases["YourDatabase"];
//configure Scripter for DDL
Scripter script = new Scripter(srv);
ScriptingOptions scriptOpt = new ScriptingOptions();
//this can changed to views, stored procedures, or other objects
foreach (Table t in db.Tables)
{
//check for system objects
//use t.Name to check table name if needed
if (!t.IsSystemObject)
{
StringCollection sc = t.Script(scriptOpt);
foreach (string s in sc)
{
//DDL is here, it can accessed/used as needed
Console.WriteLine(s);
}
}
}

TSQL | Create Database

I have two databases on my local. I wish to use tsql to script out one of the two databases in its entirety (schema only) and save it in one .sql script. Is this possible for SQL 2012? And if so, how may I go about doing it? I am using GUI to do this right now but want to use tsql query if possible. I can't use any 3rd party tools.
Thank you
UPDATE: I am using the RIGHT CLICK > GENERATE script method right now. I want to avoid that and find a way to generate the database generation tsql script by some way other than using SSMS GUI. Also, I want to script the entire database and not just tables.
You can do it in the following 2 ways.
Use Powershell.
Use the SMO classes, the Scripter class in particular. The GUI Tools are wrappers around this class.
Here's the solution for #1.
Using Powershell to Generate Table-Creation Scripts. By Robert Sheldon on simple-talk.com
Here's a solution for #2.
See this MSDN Example scripting all tables in a database with SMO.
Relevant code below. Change the database name and other details as appropriate.
//Connect to the local, default instance of SQL Server.
{
Server srv = default(Server);
srv = new Server();
//Reference the AdventureWorks database.
Database db = default(Database);
db = srv.Databases("AdventureWorks");
//Define a Scripter object and set the required scripting options.
Scripter scrp = default(Scripter);
scrp = new Scripter(srv);
scrp.Options.ScriptDrops = false;
scrp.Options.WithDependencies = true;
//Iterate through the tables in database and script each one. Display the script.
//Note that the StringCollection type needs the System.Collections.Specialized namespace to be included.
Table tb = default(Table);
Urn[] smoObjects = new Urn[2];
foreach ( tb in db.Tables) {
smoObjects = new Urn[1];
smoObjects(0) = tb.Urn;
if (tb.IsSystemObject == false) {
StringCollection sc = default(StringCollection);
sc = scrp.Script(smoObjects);
string st = null;
foreach ( st in sc) {
Console.WriteLine(st);
}
}
}
}
If you want to script out ALL the DB objects, and not just tables, take a look at the powershell script in this page where it says "Full Script". It takes care of table and relationship dependencies also.

Entity Framework 4.1 Raw SQL

I am developing an ASP.Net MVC 3 application using Entity Framework 4.1. For a particular complex query that I need to execute I have decided to write a raw SQL query and pass it to the built in dbSet.SqlQuery method.
I have a Service method like below where I assign the SQL query to a string variable called query. As the query is passed two parameters, I have parameterized these to prevent SQL Injection.
public IList<User> GetAvailableLocums(int shiftID, int shiftDateID)
{
var query ="Select .... where t1 = #p0 and t2 = #p1";
ObjectParameter _shiftID = new ObjectParameter("p0", shiftID);
ObjectParameter _shiftDateID = new ObjectParameter("p1", shiftDateID);
object[] parameters = new object[] { _shiftID, _shiftDateID };
return _UoW.User.GetWithRawSql(query, parameters).ToList();
}
I then pass the query and the parameters to a method in my repository which executes the query for me.
public IEnumerable<TEntity> GetWithRawSql(string query, params object[] parameters)
{
return dbSet.SqlQuery(query, parameters).ToList();
}
I know the query is correct as I have tested it in SQL Server Management Studio, however, I currently get the following error when I try to run this code
No mapping exists from object type System.Data.Objects.ObjectParameter
to a known managed provider native type
Does anyone have any suggestions as to how I can fix this?
Thanks for your help.
Folks
The problem was that I was using ObjectParameter to create my Parameters. I instead changed this to SqlParameter and it worked fine. See below.
Change from this
ObjectParameter _shiftID = new ObjectParameter("p0", shiftID);
To this
SqlParameter _shiftID = new SqlParameter("p0", shiftID);
And it worked. Hope this helps someone else.
From a quick Google search, it looks like your close. I think you are missing setting the return type for your SQL query:
return dbSet.SqlQuery<TEntity>(query, parameters).ToList();
This just tells Entity Framework how to map it.

Libraries for ADO.NET to rapidly bulk insert data into a database from a .csv file?

I'd like to know if you can recommend any advanced ADO.NET libraries for working with databases.
I've discovered that LINQ-to-Entities is great for pulling data out of databases, but not at all useful for inserting data into databases. Its missing functionality like fast bulk insert, culling of duplicates, and most of the advanced functionality you can achieve with pure SQL.
So: can you recommend some ADO.NET libraries that offer the sorts of advanced functionality that LINQ-to-Entities is missing?
The ADO.net SqlBulkCopy class enables quick, mass upload of records into a table:
DataTable dt = s_EmptyUploadTable.Copy();
foreach (var itm in yourList) {
DataRow row = dt.NewRow();
row["Field1"] = itm.Field1;
row["Field2"] = itm.Field2;
dt.Rows.Add(row);
}
using (SqlConnection cn = new SqlConnection(yourConnectionString)) {
cn.Open();
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(cn)) {
bulkCopy.DestinationTableName = "dbo.YourActualSQLServerTableName";
bulkCopy.WriteToServer(dt);
}
cn.Close();
}
You can use LINQ Entity Data Reader to write an IEnumerable list to a database using SQL Bulk Copy behind the scenes. You can use this library to bulk upload the results of a LINQ query straight into the database, because the results of a LINQ query are IEnumerable.
As there are LINQ-to-everything adapters, you can do tricks like use the LINQ to CSV library to grab the data out of a .csv file using a LINQ query, then the LINQ Entity Data Reader to bulk write this data directly into the database.
Case study:
Problem: read a .csv file quickly into a database. The connection to the SQL database is via LINQ-to-Entitys from C#.
Solution 1: Use LINQ to CSV library, construct a LINQ query to pull out the data you want, then write it in using the standard LINQ-to-Entity calls (ctx.AddObject(), ctx.SaveChanges(), etc). Time taken: 30 seconds for 20,000 records, as LINQ ends up generating a query for every single record (slooooow!!!!!).
Solution 2: Use LINQ to CSV library, construct a LINQ query to pull out the data you want into an IEnumerable, use LINQ Entity Data Reader to bulk write this data directly into the target data table. Time taken: 3 seconds for 20,000 records.
Solution 3: Use a a stored procedure with SQL "bulk copy". Time taken: 2 seconds for 20,000 records. However, this solution is quite brittle as it relies on a stored procedure, and SQL bulk copy is just not compatible with some .csv file formats. This method also requires that you use a staging table between the actual target table and the .csv file, to deal with file formatting issues and to help with normalization.
And, here is the source code for solution #2:
static void WriteCSVtoSQLtable()
{
// Step 1: Read .csv file into IEnumerable using LINQ-to-CSV class.
// This section requires "LINQtoCSV" class as described at http://www.codeproject.com/KB/linq/LINQtoCSV.asp
string inputFilePath = #"T:\x.csv";
CsvFileDescription inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
IEnumerable<MyCustomColumnMappingClass> csvChains = cc.Read<MyCustomColumnMappingClass>(inputFilePath, inputFileDescription);
// Step 2: Now write into the target table on SQL Server.
// This section requires "EntityDataReader" class described at http://archive.msdn.microsoft.com/LinqEntityDataReader.
public static string dbSqlConnectionString = #";Data Source=(local);Initial Catalog=PhiEngine;Integrated Security=True;MultipleActiveResultSets=True";
SqlConnection dbSql(dbSqlConnectionString);
using (var tran = dbSql.BeginTransaction())
{
var csvFile = from p in csvChains
select p;
SqlBulkCopy bc = new SqlBulkCopy(dbSql,
SqlBulkCopyOptions.CheckConstraints |
SqlBulkCopyOptions.FireTriggers |
SqlBulkCopyOptions.KeepNulls, tran)
{
BatchSize = 1000,
DestinationTableName = "TStagingTable" // Temporary staging table in database.
};
bc.WriteToServer(csvFile.AsDataReader()); // Extension method .AsDataReader depends on adding the EntityDataReader class to your C# project (see above).
tran.Commit();
}
}
// This class is used by LINQ to CSV to query the .csv file, see "LINQtoCSV" website.
public class MyCustomColumnMappingClass
{
[CsvColumn(Name = "symbol", FieldIndex = 1)]
public string Symbol { get; set; }
[CsvColumn(Name = "date", FieldIndex = 3, OutputFormat = #"MM/dd/yyyy")]
public DateTime Date { get; set; }
}

Best way to delete all rows in a table using NHibernate?

To keep my integration tests independent I remove all old data and insert new test data before each test. Is there a better way of doing this than simply querying for all entities and deleting them one by one?
I have considered writing a stored proc that runs "delete from tablename;" for each table that is to be cleared. That ought to quite a bit faster, but it would be nice to do it without doing SQL queries or calling SPs via NH.
I'm using vanilla NHibernate and Linq to NHibernate. I beleive Castle Active Record has something like Foo.DeleteAll(), but I don't want to use Active Record for this project.
Any ideas?
Thanks /Erik
UPDATE:
Since this question was asked and answered, progress has been made by the NHibernate team. As Ayende explains in this blog post, you can now execute DML queries directly, without NHibernate having to fetch any entities.
To delete all Foo objects you could do like this:
using (ISession session = ...)
using (ITransaction transaction = session.BeginTransaction())
{
session.CreateQuery("delete Foo f").ExecuteUpdate();
transaction.Commit();
}
This query would generate the following SQL:
delete from Foo
which aught to be significantly faster than fetching the entities first and then deleting them. Be careful though, since queries like these do not affect the level 1 cache.
In the TearDown of my UnitTests, I mostly do this:
using( ISession s = ... )
{
s.Delete ("from Object o");
s.Flush();
}
This should delete all entities.
If you want to delete all instances of one specific entity, you can do this:
using( ISession s = .... )
{
s.Delete ("from MyEntityName e");
s.Flush();
}
Offcourse, there's a drawback with this method, and that is that NHibernate will first fetch the entities before deleting them.
I use Fluent Nhibernate attributes so I modify code a little in order not to hardcore table names
private static void CleanUpTable<T>(ISessionFactory sessionFactory)
{
var metadata = sessionFactory.GetClassMetadata(typeof(T)) as NHibernate.Persister.Entity.AbstractEntityPersister;
string table = metadata.TableName;
using (ISession session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
string deleteAll = string.Format("DELETE FROM \"{0}\"", table);
session.CreateSQLQuery(deleteAll).ExecuteUpdate();
transaction.Commit();
}
}
}
usage
CleanUpTable<Person>(sessionFactory);
With NHibernate 5.0 you can now simply do:
session.Query<Foo>().Delete();
Documentation:
//
// Summary:
// Delete all entities selected by the specified query. The delete operation is
// performed in the database without reading the entities out of it.
//
// Parameters:
// source:
// The query matching the entities to delete.
//
// Type parameters:
// TSource:
// The type of the elements of source.
//
// Returns:
// The number of deleted entities.
public static int Delete<TSource>(this IQueryable<TSource> source);