我正在尝试创建一个CLR过程来将SQL数据导出到Excel,其中包含的功能比其他选项(如小计和突出显示)更多。
这要求我引用Microsoft.Office.Interop.Excel
dll,但我不确定在编译代码时如何实际包含程序集。
如何在我的CLR程序中包含Excel程序集?
using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
public class ExportToExcel
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void ExportQueryResults(string queryText, string worksheetName, string fileName)
{
using (SqlConnection cnn = new SqlConnection("context connection=true"))
{
//the temp list to hold the results in
List<object[]> results = new List<object[]>();
cnn.Open();
//create the sql command
SqlCommand cmd = new SqlCommand(queryText, cnn);
using (SqlDataReader reader = cmd.ExecuteReader())
{
int fieldCount = reader.FieldCount;
object[] headers = new object[fieldCount];
for(int i = 0; i < fieldCount; i++)
{
headers[i] = reader.GetName(i);
}
//read the results
while (reader.Read())
{
object[] values = new object[fieldCount];
for (int i = 0; i < fieldCount; i++)
{
values[i] = reader[i];
}
results.Add(values);
}
//convert the results into a 2-d array to export into Excel
object[,] exportVals = new object[results.Count, fieldCount];
for (int row = 0; row < results.Count; row++)
{
for (int col = 0; col < fieldCount; col++)
{
exportVals[row, col] = results[row][col];
}
}
Excel.Application _app = new Excel.Application();
Excel.Workbook _book = _app.Workbooks.Add(Missing.Value);
Excel.Worksheet _sheet = (Excel.Worksheet)_book.ActiveSheet;
Excel.Range _range = (Excel.Range)_sheet.Cells[1, 1];
_range = _sheet.get_Range(_sheet.Cells[1, 1], _sheet.Cells[results.Count, fieldCount]);
_range.Value2 = exportVals;
_sheet.Name = worksheetName;
//remove any extra worksheets
foreach(Excel.Worksheet sht in _book.Worksheets)
{
if (sht.Name != worksheetName)
sht.Delete();
}
_book.SaveAs(fileName
, Excel.XlFileFormat.xlWorkbookDefault
, Missing.Value
, Missing.Value
, false
, false
, Excel.XlSaveAsAccessMode.xlNoChange
, Missing.Value
, Missing.Value
, Missing.Value
, Missing.Value
, Missing.Value);
}
}
}
}
答案 0 :(得分:3)
在SQL Server中无法使用任意程序集。您只需引用框架的subset并执行一些basic discipline。我怀疑具有Excel历史层的怪物应用程序是否会加载到此环境中。
您可能需要查看符合此条件的更简单的substitute functionality。
如果您需要更多,请考虑在客户端使用Excel。
答案 1 :(得分:1)
经过一天的摸索,几乎每种类型的错误都可能发生,我想出了解决问题的方法。
我感谢所给出的答案/评论,虽然我同意可能有更有效/更安全的方法来实施解决方案,但使用互操作程序集是完成此项目的最快和最熟悉的。
在我对煤炭进行倾斜之前,请注意此项目绝对需要在更传统的SQL Server导出功能范围之外进行自动过滤和其他格式化。
<强>解决方案强>
我创建了一个名为SqlProcedures
的类库输出类型visual studio项目,创建了一个名为ExportToExcel
的新类,并在我的引用中添加了Microsoft.Office.Interop.Excel
。
以下是我的ExportToExcel.cs
文件中的代码:
using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
using System.Runtime.InteropServices;
public class ExportToExcel
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void ExportQueryResults(string queryText, string worksheetName, string fileName)
{
using (SqlConnection cnn = new SqlConnection("context connection=true"))
{
//the temp list to hold the results in
List<object[]> results = new List<object[]>();
cnn.Open();
//create the sql command
SqlCommand cmd = new SqlCommand(queryText, cnn);
using (SqlDataReader reader = cmd.ExecuteReader())
{
int fieldCount = reader.FieldCount;
object[] headers = new object[fieldCount];
for (int i = 0; i < fieldCount; i++)
{
headers[i] = reader.GetName(i);
}
//read the results
while (reader.Read())
{
object[] values = new object[fieldCount];
for (int i = 0; i < fieldCount; i++)
{
values[i] = reader[i];
}
results.Add(values);
}
//convert the results into a 2-d array to export into Excel
object[,] exportVals = new object[results.Count, fieldCount];
for (int row = 0; row < results.Count; row++)
{
for (int col = 0; col < fieldCount; col++)
{
exportVals[row, col] = results[row][col];
}
}
Excel.Application _app = new Excel.Application();
Excel.Workbook _book = _app.Workbooks.Add(Missing.Value);
Excel.Worksheet _sheet = (Excel.Worksheet)_book.ActiveSheet;
Excel.Range _range = (Excel.Range)_sheet.Cells[1, 1];
_app.DisplayAlerts = false;
//set the headers and freeze the panes
_range = _sheet.get_Range(_sheet.Cells[1, 1], _sheet.Cells[1, fieldCount]);
_range.NumberFormat = "@";
_range.HorizontalAlignment = Excel.XlHAlign.xlHAlignLeft;
_range.Value2 = headers;
_range.Font.Bold = true;
_range = _sheet.get_Range(_sheet.Cells[2, 1], _sheet.Cells[2, 1]);
_range.EntireRow.Select();
_range.Application.ActiveWindow.FreezePanes = true;
_range = _sheet.get_Range(_sheet.Cells[2, 1], _sheet.Cells[results.Count, fieldCount]);
_range.Value2 = exportVals;
_range = _sheet.get_Range(_sheet.Cells[1, 1], _sheet.Cells[exportVals.Length, fieldCount]);
_range.AutoFilter(1, Type.Missing, Excel.XlAutoFilterOperator.xlAnd, Type.Missing, true);
_sheet.Cells.Columns.AutoFit();
_sheet.Range["A1"].Select();
_sheet.Name = worksheetName;
//remove any extra worksheets
foreach (Excel.Worksheet sht in _book.Worksheets)
{
if (sht.Name != worksheetName)
sht.Delete();
}
_book.SaveAs(fileName
, Excel.XlFileFormat.xlExcel5
, Missing.Value
, Missing.Value
, false
, false
, Excel.XlSaveAsAccessMode.xlNoChange
, Missing.Value
, Missing.Value
, Missing.Value
, Missing.Value
, Missing.Value);
//_book.Close(Missing.Value, Missing.Value, Missing.Value);
_app.Application.Quit();
GC.Collect();
GC.WaitForPendingFinalizers();
Marshal.ReleaseComObject(_range);
Marshal.ReleaseComObject(_sheet);
Marshal.ReleaseComObject(_book);
Marshal.ReleaseComObject(_app);
_range = null;
_sheet = null;
_book = null;
_app = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}
}
在成功构建DLL之后,我将其复制到SQL服务器上的本地目录中。
为了运行过程ExportQueryResults
,我需要在Microsoft.Office.Interop.Excel.dll
依赖的SQL服务器中添加几个程序集。
这是我的SQL代码:
ALTER DATABASE main SET TRUSTWORTHY ON;
create assembly [stdole] from
'C:\Program Files\Microsoft.NET\Primary Interop Assemblies\stdole.dll'
WITH PERMISSION_SET = unsafe
create assembly [Office] from
'C:\WINDOWS\assembly\GAC\office\12.0.0.0__71e9bce111e9429c\OFFICE.DLL'
WITH PERMISSION_SET = unsafe
create assembly [Vbe]
FROM 'C:\WINDOWS\assembly\GAC\Microsoft.Vbe.Interop\12.0.0.0__71e9bce111e9429c\Microsoft.Vbe.Interop.dll'
WITH PERMISSION_SET = unsafe
create assembly [Microsoft.Office.Interop.Excel.dll]
from 'C:\WINDOWS\assembly\GAC\Microsoft.Office.Interop.Excel\12.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Excel.dll'
WITH PERMISSION_SET = unsafe
create assembly SqlProcedures from 'c:\sql_data_reporting\SqlProcedures.dll'
WITH PERMISSION_SET = unsafe
go
create procedure ExportToExcel @queryText nvarchar(4000), @worksheetName nvarchar(32), @fileName nvarchar(250)
as external name SqlProcedures.ExportToExcel.ExportQueryResults
go
现在我知道使用with permission_set = unsafe
是一个nono,但这是一个“现在就完成”项目,这是我能想到的最快的解决方案。
希望此解决方案可以为需要实现类似功能的其他人节省一些时间。
答案 2 :(得分:0)
我相信您可以像添加核心程序集一样添加引用的程序集。问题是interop程序集是Excel COM对象周围的薄包装器。这意味着除非您还在SQL Server上安装Microsoft Excel,否则互操作将毫无价值。我甚至不确定这是否可行,但这听起来真是一个非常糟糕的主意。