Connecting from C# to Accumulo - c#

I am new to working with Accumulo. I need to read/write data from a remote Accumulo through C#.
The only code sample/documentation for C#, I have found is -
Accumulo createBatchScanner range not working as expected
I attempted to compile the code in Xamarin Studio, on a Mac.
The issue I am encountering is with this line:
AccumuloProxy.Client client = new AccumuloProxy.Client(protocol);
Error CS0246: The type or namespace name AccumuloProxy' could not be found. Are you missingorg.apache.accumulo.proxy.thrift' using directive? (CS0246) (AccumuloIntegratorPrototype)
Where can I find the DLLs to add to my CSharp project related to AccumuloProxy client?
Is there a way I can generate the same?
Here is a code fragment:
namespace AccumuloIntegratorPrototype
{
class MainClass
{
static byte[] GetBytes(string str)
{
return Encoding.ASCII.GetBytes(str);
}
static string GetString(byte[] bytes)
{
return Encoding.ASCII.GetString(bytes);
}
public static void Main (string[] args)
{
try
{
/** connect **/
TTransport transport = new TSocket("xxx.xx.x.xx", 42424);
transport = new TFramedTransport(transport);
TCompactProtocol protocol = new TCompactProtocol(transport);
transport.Open();
AccumuloProxy.Client client = new AccumuloProxy.Client(protocol);

Thanks to all for the pointers.
Was able to complete my project.
These are my notes.
A. Versions:
Accumulo 1.5
Thrift 0.90
Mono 3.2.5
B. Strategy/option used to connect to Accumulo from C#:
Accumulo Proxy API
C. Accumulo Proxy with C# bindings:
Performed the following actions on a node running Accumulo
1. Installed Mono 3.2.5
2. Installed Thrift 0.90
3. Configured Accumulo proxy service
Modified the file $ACCUMULO_HOME/proxy/proxy.properties;
Specifically updated the instance name, and zookeeper
4. Started the proxy daemon-
${ACCUMULO_HOME}/bin/accumulo proxy -p ${ACCUMULO_HOME}/proxy/proxy.properties
5.Generated the c# bindings, using the proxy.thrift IDL file
thrift --gen csharp $ACCUMULO_HOME/proxy/thrift/proxy.thrift
This resulted in the creation of a directory called gen-csharp under ${ACCUMULO_HOME}/proxy/thrift/
6. The files under gen-csharp are needed in the C# project, in section D, below.
7. Thrift.dll, is also needed.
D. C# project - Accumulo Client:
1. Created a project of type library.
2. Added the files under gen-csharp in step C5, above to the library
3. Added reference to thrift.dll
4. Built the library
E. Connecting to Accumulo from C#
In the C# project that reads/writes to Accumulo,
1. Added reference - thrift.dll
2. Added reference to the library built in section D, above
3. On the Accumulo server, started the proxy (refer step C4, above)
Here is some sample code, to read data, to try this functionality out..
using System;
using System.Text;
using System.Collections.Generic;
using Thrift.Protocol;
using Thrift.Transport;
namespace AccumuloIntegratorPrototype
{
class MainClass
{
static byte[] GetBytes(string str)
{
return Encoding.ASCII.GetBytes(str);
}
static string GetString(byte[] bytes)
{
return Encoding.ASCII.GetString(bytes);
}
public static void Main (string[] args)
{
try
{
String accumuloProxyServerIP = "xxx.xxx.x.xx";//IP
int accumuloProxyServerPort = 42424;//Port Number
TTransport transport = new TSocket(accumuloProxyServerIP, accumuloProxyServerPort);
transport = new TFramedTransport(transport);
TCompactProtocol protocol = new TCompactProtocol(transport);
transport.Open();
String principal = "root";//Application ID
Dictionary<string, string> passwd = new Dictionary<string,string>();
passwd.Add("password", "xxxxx");//Password
AccumuloProxy.Client client = new AccumuloProxy.Client(protocol);
byte[] loginToken = client.login(principal, passwd);//Login token
//{{
//Read a range of rows from Accumulo
var bScanner = new BatchScanOptions();
Range range = new Range();
range.Start = new Key();
range.Start.Row = GetBytes("d001");
//Need the \0 only if you need to get a single row back
//Otherwise, its not needed
range.Stop = new Key();
range.Stop.Row = GetBytes("d001\0");
bScanner.Ranges = new List<Range>();
bScanner.Ranges.Add(range);
String scanId = client.createBatchScanner(loginToken, "departments", bScanner);
var more = true;
while (more)
{
var scan = client.nextK(scanId, 10);
more = scan.More;
foreach (var entry in scan.Results)
{
Console.WriteLine("Row = " + GetString(entry.Key.Row));
Console.WriteLine("{0} {1}:{2} [{3}] {4} {5}", GetString(entry.Key.Row), GetString(entry.Key.ColFamily), GetString(entry.Key.ColQualifier), GetString(entry.Key.ColVisibility), GetString(entry.Value),(long)entry.Key.Timestamp);
}
}
client.closeScanner(scanId);
client.Dispose();
transport.Close();
}catch (Exception e)
{
Console.WriteLine(e);
}
//}}
}
}
}

Adding Thrift to C# project involves two steps:
Add the C# code that has been generated by means of the Thrift compiler
Build Thrift.DLL and add it as a reference to your project. Alternatively, it is possible to link the code into your project, however not recommended.
The C# code for step 1 is generated from a Thrift IDL file, which is typically part of the project. In your case, the IDL files are located under proxy/src/main/thrift in the Accumulo tree.
The Thrift compiler and library can be downloaded from http://thrift.apache.org. Note that some projects are using a older version of Apache Thrift, which is not necessarily the latest stable. As elserj mentioned in the comments, Accumulo 1.4.x depends on Thrift 0.6.1, Accumulo 1.5.x and greater depend on Thrift 0.9.0.

Related

HoloLens cannot connect to OpcUa server

I want to connect to the OpcUa server through the APP on HoloLens and read the node's data. It works on Unity and works fine, but it can't connect to the OpcUa server in HoloLens. I use the OpcUaHelper plugin, which has a simple OpcUa client and supports .Net Core and .Net Standard 2.0.
Unity's settings are:
Scripting Runtime Version: .Net 4.X Equivalent
Scripting Backend: IL2CPP
Api Compatibility Level : .Net 4.X
My code are shown below.
Does anyone know what the reason is?
Or have anyone tried to connect the Opc Ua server in HoloLens?
The version of the software I am using is:
1.Unity 2018 3.11f
2.Mixed Reality Toolkit v2.0.0 RC1
3.Visual Studio 2017
using System;
using UnityEngine;
using UnityEngine.UI;
using Microsoft.MixedReality.Toolkit.Input;
using OpcUaHelper;
using Opc.Ua;
using System.Collections;
using System.Collections.Generic;
public void OpcUaConnector()
{
OpcUaClient m_OpcUaClient = new OpcUaClient();
//m_OpcUaClient.UserIdentity = new UserIdentity("user", "password");
string OpcUa_Test = "opc.tcp://118.24.36.220:62547/DataAccessServer";
m_OpcUaClient.ConnectServer(string.Format("{0}", OpcUa_Test));
try
{
//Read the same type of data from multiple nodes
List<NodeId> nodeIds = new List<NodeId>();
nodeIds.Add(new NodeId("ns=2;s=Machines/Machine A/Name"));
nodeIds.Add(new NodeId("ns=2;s=Machines/Machine B/Name"));
nodeIds.Add(new NodeId("ns=2;s=Machines/Machine C/Name"));
List<DataValue> dataValues = m_OpcUaClient.ReadNodes(nodeIds.ToArray());
AnlagenName_A.text = string.Format("{0}", dataValues[0]);
AnlagenName_B.text = string.Format("{0}", dataValues[1]);
AnlagenName_C.text = string.Format("{0}", dataValues[2]);
//foreach (string tag in Tags)
//{
// string value = m_OpcUaClient.ReadNode<string>(tag);
//}
}
// //Read data from a single node
// //string value = m_OpcUaClient.ReadNode<string>("ns=2;s=Machines/Machine B/Name");
// //Console.WriteLine("{0}", value);
// //Console.ReadKey();
//}
catch
{
Debug.Log("Connected Failed");
}
}
I hope to successfully connect to the OpcUa server and read the node data.

Xamarin with PCLStorage - Where to place SQLite3 file?

I am reading 'Xamarin in Action' by Manning Publications and working on Countr example. I am using below code to retrieve Sqlite3 file as has been mentioned in chapter 7 of book.
var local = FileSystem.Current.LocalStorage.Path;
var datafile = PortablePath.Combine(local, "counters.db3");
connection = new SQLiteAsyncConnection(datafile);
connection.GetConnection();
I wanted to add one more database which has static content that I already prepared. I need to place this database inside app's assets or resources and read it when app loads. I realized that on Windows, above code is looking for database at "C:\Users\\AppData\Local\Microsoft.TestHost.x86\15.6.2\". For testing Model, ViewModel I placed the database file at that location and that helped. However, I wanted to know what is right place to put database file and how to access it so I can test in Android and iPhone emulators?
Just use Environment.GetFolderPath and store the database the location Android and IOS gives your app to store data without further permissions:
string dbPath = Path.Combine (
Environment.GetFolderPath (Environment.SpecialFolder.Personal),
"counters.db3");
You can find more information on this article:
https://developer.xamarin.com/guides/android/data-and-cloud-services/data-access/part-3-using-sqlite-orm/
In Visual Studio, Xamarin Solution by default has multiple projects -
App.Core that has libraries needed for designing Model and ViewModels
App.Droid that has Android SDK libraries to build view for Android devices
App.iOS that has iOS SDK libaries to build View for Apple devices
It is not possible to add below code in App.Core library as that project has no information about App's file system. Only App.Droid and App.iOS projects carry this information.
string dbPath = Path.Combine (
Environment.GetFolderPath (Environment.SpecialFolder.Personal),
"counters.db3");
Since we want to copy a database with pre-existing content to LocalDirectory, Setup.cs in App.Droid and App.iOS must be altered. For example, for App.Droid, below are the steps needed (This is one way and there are of course alternatives)
Place Sqlite DB in App.Droid/Assets folder
Create a class file (let's call it AndroidConfiguration.cs) and add a method which I call copyElementDatabaseToLocalStorage() (Code for this method can be found after these list of steps)
In SetUp.cs, call copyElementDatabaseToLocalStorage() method
Code for copyElementDatabaseToLocalStorage Method:
using System;
using Android.App;
using System.IO;
using PCLStorage;
namespace App.Droid.AndroidSpecificSetters
{
public class AndroidConfiguration
public async void copyElementDatabaseToLocalStorage()
{
string localStorage = FileSystem.Current.LocalStorage.Path;
string databaseLocation = Path.Combine(localStorage, "counters.db3");
try
{
if (!File.Exists(databaseLocation))
{
using (var binaryReader = new BinaryReader(Application.Context.Assets.Open("counters.db3")))
{
using (var binaryWriter = new BinaryWriter(new FileStream(databaseLocation, FileMode.Create)))
{
byte[] buffer = new byte[2048];
int length = 0;
while ((length = binaryReader.Read(buffer, 0, buffer.Length)) > 0)
{
binaryWriter.Write(buffer, 0, length);
}
}
}
}
} catch (Exception e)
{
...
}
}
}
}
Code Inside App.Droid Setup.cs constructor
public Setup(Context applicationContext) : base(applicationContext)
{
AndroidConfiguration androidConfiguration = new AndroidConfiguration();
androidConfiguration.copyElementDatabaseToLocalStorage();
}

How can I open AutoCAD 2015 through the .NET API

I've been browsing for a good hour and have yet to find something that would help with this. I'm working on opening AutoCAD from the .NET API in VS2013 using C#, but for some reason, I can never get AutoCAD to actually launch. I'm using the following code:
using System;
using System.Runtime.InteropServices;
using Autodesk.AutoCAD.Interop;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
namespace IOAutoCADHandler
{
public static class ACADDocumentManagement
{
[CommandMethod("ConnectToAcad")]
public static void ConnectToAcad()
{
AcadApplication acAppComObj = null;
// no version number so it will run with any version
const string strProgId = "AutoCAD.Application";
// Get a running instance of AutoCAD
try
{
acAppComObj = (AcadApplication)Marshal.GetActiveObject(strProgId);
}
catch // An error occurs if no instance is running
{
try
{
// Create a new instance of AutoCAD
acAppComObj = (AcadApplication)Activator.CreateInstance(Type.GetTypeFromProgID(strProgId), true);
}
catch //// STOPS HERE
{
// If an instance of AutoCAD is not created then message and exit
// NOTE: always shows this box and never opens AutoCAD
System.Windows.Forms.MessageBox.Show("Instance of 'AutoCAD.Application'" +
" could not be created.");
return;
}
}
// Display the application and return the name and version
acAppComObj.Visible = true;
System.Windows.Forms.MessageBox.Show("Now running " + acAppComObj.Name +
" version " + acAppComObj.Version);
// Get the active document
AcadDocument acDocComObj;
acDocComObj = acAppComObj.ActiveDocument;
// Optionally, load your assembly and start your command or if your assembly
// is demandloaded, simply start the command of your in-process assembly.
acDocComObj.SendCommand("(command " + (char)34 + "NETLOAD" + (char)34 + " " +
(char)34 + #"C:\Users\Administrator\Documents\All Code\main-libraries\IOAutoCADHandler\bin\Debug\IOAutoCADHandler.dll" + (char)34 + ") ");
acDocComObj.SendCommand("DRAWCOMPONENT");
}
}
Unfortunately, it always stops at the nested catch statement and always displays the popup box without opening AutoCAD. Any suggestions on how to at least make AutoCAD open for me?
EDIT: Error message
The issue is you're coding (correctly) to the AutoCAD interop interface. I recommend against that (due to potential version changes).
The other issue is that the documentation for AutoCAD plugins using the newer .net api is for plugins when AutoCAD is already running.
Final issue could be that the program Id of AutCAD is a mystery. I have resorted to making that a configurable setting, but default to "AutoCAD.Application", which will take the currently registered AutoCAD.Application on the production machine. If there are multiple versions installed on the machine and you want to be specific, then you could append the version number (which you'll need to research) to the ProgID like: "AutoCAD.Application.19", or "AutoCAD.Application.20" for 2015.
For the first issue, one technique is to use dynamics for the autoCad objects, particularly for creating instances. I have used the ObjectARX api for creating my application in a dummy project, and then switching to dynamics when I'm happy with the properties and method names.
In a standalone .Net application that starts AutoCAD you could use something like:
// I comment these out in production
//using Autodesk.AutoCAD.Interop;
//using Autodesk.AutoCAD.Interop.Common;
//...
//private static AcadApplication _application;
private static dynamic _application;
static string _autocadClassId = "AutoCAD.Application";
private static void GetAutoCAD()
{
_application = Marshal.GetActiveObject(_autocadClassId);
}
private static void StartAutoCad()
{
var t = Type.GetTypeFromProgID(_autocadClassId, true);
// Create a new instance Autocad.
var obj = Activator.CreateInstance(t, true);
// No need for casting with dynamics
_application = obj;
}
public static void EnsureAutoCadIsRunning(string classId)
{
if (!string.IsNullOrEmpty(classId) && classId != _autocadClassId)
_autocadClassId = classId;
Log.Activity("Loading Autocad: {0}", _autocadClassId);
if (_application == null)
{
try
{
GetAutoCAD();
}
catch (COMException ex)
{
try
{
StartAutoCad();
}
catch (Exception e2x)
{
Log.Error(e2x);
ThrowComException(ex);
}
}
catch (Exception ex)
{
ThrowComException(ex);
}
}
}
When there are several versions of AutoCAD installed on a computer, creating an instance with the ProgID "AutoCAD.Application" will run the latest version started on this computer by the current user. If the version of the Interop assemblies used does not match the version that is starting, you'll get a System.InvalidCastException with an HRESULT 0x80004002 (E_NOINTERFACE).
In your specific case, the {070AA05D-DFC1-4E64-8379-432269B48B07} IID in your error message is the GUID for the AcadApplicationinterface in R19 64-bit (AutoCAD 2013 & 2014). So there is an AutoCAD 2013 or 2014 that is starting, and you cannot cast this COM object to a 2015 type because 2015 is R20 (not binary compatible).
To avoid that, you can add a specific version to your ProgID (like "AutoCAD.Application.20" for AutoCAD 2015 (R20.0) to 2016 (R20.1)) to start the version matching your Interop assemblies or you can use late binding (eg. remove your references to Autodesk.AutoCAD.Interop* and use the dynamic keyword instead of the AutoCAD types).
In the last case, you will lost autocompletion, but your program will work with all the versions of AutoCAD.
Check also 32-bit vs 64-bit because TypeLib/Interop assemblies are not the same.
I open the application in a much straight-forward way. First, be sure to reference the correct type library. The one I am using is AutoCAD 2014 Type Library, located at:
c:\program files\common files\autodesk shared\acax19enu.tlb
To initialize the application:
using AutoCAD;
namespace test
{
class Program
{
static void Main(string[] args)
{
AutoCAD.AcadApplication app;
app = new AcadApplication();
app.Visible = true;
Console.Read();
}
}
}
Try this:
"sourcefile" is the original file
"newfile" is the new file
[CommandMethod("ModifyAndSaveas", CommandFlags.Redraw | CommandFlags.Session)]
public void ModifyAndSaveAs()
{
Document acDoc = Application.DocumentManager.Open(sourcefile);
Database acDB = acDoc.Database;
Transaction AcTran = acDoc.Database.TransactionManager.StartTransaction();
using (DocumentLock acLckDoc = acDoc.LockDocument())
{
using (AcTran)
{
BlockTable acBLT = (BlockTable)AcTran.GetObject(acDB.BlockTableId, OpenMode.ForRead);
BlockTableRecord acBLTR = (BlockTableRecord)AcTran.GetObject(acBLT[BlockTableRecord.ModelSpace], OpenMode.ForRead);
var editor = acDoc.Editor;
var SelectionSet = editor.SelectAll().Value;
foreach (ObjectId id in SelectionSet.GetObjectIds())
{
Entity ent = AcTran.GetObject(id, OpenMode.ForRead) as Entity;
//modify entities
}
AcTran.Commit();
}
}
acDB.SaveAs(newfile, DwgVersion.AC1021);
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Tekkit
{
class Program
{
static void Main(string[] args)
{
//make sure to add last 2 using statements
ProcessStartInfo start = new ProcessStartInfo("calc.exe");
Process.Start(start);//starts the process
}
}
}

Create solution file(.sln) and project files(.csproj) dynamically

I am creating code generation tool (auto code generation based on table structure) as a Windows forms application in Visual Studio 2012 using .NET Framework 4.0. It's generating the portable object, controller, WCF services and business logic code files.
All code files bundle in the appropriate project and all project bundle in one solution. The solution and projects need to create dynamically through program.
I have tried to create the solution and project using Visual Studio Add-in project. It is working fine in Add-In project (separate solution). The OnConnection method call automatically in Add-in project. Now I want to implements this in my code generation tool. While debugging in Add-In project the application variable shown like COM object.
I am tried to pass the value for OnConnection method from code generation tool, it throws an error (I passed this object for application variable). I really don't know how to call this method from my code generation tool. Anyone help this?
Code
private DTE2 _applicationObject;
private AddIn _addInInstance;
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
_applicationObject = (DTE2)application;
_addInInstance = (AddIn)addInInst;
createProjectsFromTemplates(_applicationObject);
}
public void createProjectsFromTemplates(DTE2 dte)
{
try
{
Solution2 soln = (Solution2)dte.Solution;
string csTemplatePath;
string csPrjPath = "SamplePath\\TestCreateProject";
csTemplatePath = soln.GetProjectTemplate("WpfApplication.zip", "CSharp");
System.Windows.Forms.MessageBox.Show("C# template path: " + csTemplatePath);
soln.AddFromTemplate(csTemplatePath, csPrjPath, "NewWCFCSharpAutoGeneratorProject", false);
Project prj;
ProjectItem prjItem;
String itemPath;
// Point to the first project (the Visual Basic project).
prj = soln.Projects.Item(1);
prjItem = prj.ProjectItems.AddFromFileCopy("SampelCSharp.cs");
}
catch (System.Exception ex)
{
System.Windows.Forms.MessageBox.Show("ERROR: " + ex.Message);
}
}
You can instantiate a VS from the host application and generate the files. Hope that will work. The below code works well for me.
Use the following namespaces to get work the below given code.
Namespaces:
using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.CommandBars;
using System.Resources;
using System.Reflection;
Code:
EnvDTE80.DTE2 dte2;
dte2 = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.11.0");
Connect objConnect = new Connect();
Array objArray = null;
objConnect.OnConnection(dte2, ext_ConnectMode.ext_cm_UISetup, null, ref objArray);
I got this reference it is really useful.
http://rcos.rpi.edu/projects/unmake/commit/programmatically-launch-devenv-generate-a-solution-and-save-it/
You can use this. This is for .cs project files and framewwork above .NET 2.0 versions. VB project sources are not compatible.
protected void Build(string project)
{
Engine engine = new Engine();
BuildPropertyGroup properties = new BuildPropertyGroup();
properties.SetProperty(#"Configuration", #"Debug");
// Point to the path that contains the .NET Framework 2.0 CLR and tools
engine.BinPath = #"c:\windows\microsoft.net\framework\v3.5";
// Instantiate a new FileLogger to generate build log
FileLogger logger = new FileLogger();
// Set the logfile parameter to indicate the log destination
string str = #"logfile=D:\temp";
str += project.Substring(project.LastIndexOf("\\"), project.LastIndexOf(".") - project.LastIndexOf("\\")) + ".log";
logger.Parameters = str;
// Register the logger with the engine
engine.RegisterLogger(logger);
// Build a project file
bool success = engine.BuildProjectFile(project, new string[] { "build" }, properties);
//Unregister all loggers to close the log file
engine.UnregisterAllLoggers();
using (BinaryWriter writer = new BinaryWriter(File.Open(#"D:\temp\Prj.log", FileMode.Append)))
{
if (success)
{
writer.Write("\nBuild Success :" + project.Substring(project.LastIndexOf("\\")));
}
else
{
writer.Write("\nBuild Fail :" + project.Substring(project.LastIndexOf("\\")));
}
}
}

Using Microsoft.Build.Evaluation to publish a database project (.sqlproj)

I need to be able to publish an SSDT project programmatically. I am looking at using Microsoft.Build to do so but can not find any documentation. It seems pretty simple to create the .dacpac, but how would I either publish to an existing database or at the very least to a .sql file. The idea is to have it do what it does when I right click on the project and select publish. It should compare with a selected database and generate an upgrade script.
This is what I have so far to create the .dacpac:
partial class DBDeploy
{
Project project;
internal void publishChanges()
{
Console.WriteLine("Building project " + ProjectPath);
Stopwatch sw = new Stopwatch();
sw.Start();
project = ProjectCollection.GlobalProjectCollection.LoadProject(ProjectPath);
project.Build();
//at this point the .dacpac is built and put in the debug folder for the project
sw.Stop();
Console.WriteLine("Project build Complete. Total time: {0}", sw.Elapsed.ToString());
}
}
Essentially I am trying to do what this MSBuild Example shows but in code.
Sorry that this is all I have. The doecumentation on the Build classes is very poor. Any help would be appreciated.
Thanks.
I had to do something similar to this because VSDBCMD which we previously used does not deploy to SQL Server 2012 and we needed to support it. What I found was the Microsoft.SqlServer.Dac assembly which seems to come as part of the SQL Server data tools (http://msdn.microsoft.com/en-us/data/tools.aspx)
When you run this on the client machine you will need the full version of the .NET 4 framework and the SQL CLR types and SQL T-SQL ScriptDOM pack found here: http://www.microsoft.com/en-us/download/details.aspx?id=29065
Code below is from a mockup I made for testing the new deployment method and deploys a given .dacpac file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SqlServer.Dac;
using System.IO;
namespace ConsoleApplication3
{
class Program
{
private static TextWriter output = new StreamWriter("output.txt", false);
static void Main(string[] args)
{
Console.Write("Connection String:");
//Class responsible for the deployment. (Connection string supplied by console input for now)
DacServices dbServices = new DacServices(Console.ReadLine());
//Wire up events for Deploy messages and for task progress (For less verbose output, don't subscribe to Message Event (handy for debugging perhaps?)
dbServices.Message += new EventHandler<DacMessageEventArgs>(dbServices_Message);
dbServices.ProgressChanged += new EventHandler<DacProgressEventArgs>(dbServices_ProgressChanged);
//This Snapshot should be created by our build process using MSDeploy
Console.WriteLine("Snapshot Path:");
DacPackage dbPackage = DacPackage.Load(Console.ReadLine());
DacDeployOptions dbDeployOptions = new DacDeployOptions();
//Cut out a lot of options here for configuring deployment, but are all part of DacDeployOptions
dbDeployOptions.SqlCommandVariableValues.Add("debug", "false");
dbServices.Deploy(dbPackage, "trunk", true, dbDeployOptions);
output.Close();
}
static void dbServices_Message(object sender, DacMessageEventArgs e)
{
output.WriteLine("DAC Message: {0}", e.Message);
}
static void dbServices_ProgressChanged(object sender, DacProgressEventArgs e)
{
output.WriteLine(e.Status + ": " + e.Message);
}
}
}
This seems to work on all versions of SQL Server from 2005 and up. There is a similar set of objects available in Microsoft.SqlServer.Management.Dac, however I believe this is in the previous version of DACFx and is not included in the latest version. So use the latest version if you can.
We need a way tell msbuild how and where to publish. Open your project in Visual Studio and begin to Publish it. Enter all needed info in the dialog, including your DB connection info and any custom SQLCMD variable values. Save Profile As... to a file, e.g. Northwind.publish.xml. (You may then Cancel.) Now we can use this and the project file to build and publish:
// Create a logger.
FileLogger logger = new FileLogger();
logger.Parameters = #"logfile=Northwind.msbuild.log";
// Set up properties.
var projects = ProjectCollection.GlobalProjectCollection;
projects.SetGlobalProperty("Configuration", "Debug");
projects.SetGlobalProperty("SqlPublishProfilePath", #"Northwind.publish.xml");
// Load and build project.
var dbProject = ProjectCollection.GlobalProjectCollection.LoadProject(#"Northwind.sqlproj");
dbProject.Build(new[]{"Build", "Publish"}, new[]{logger});
This can take awhile and may appear to get stuck. Be patient. :)
You should use SqlPackage.exe to publish your dacpac.
SqlPackage.exe
/Action:Publish
/SourceFile:C:/file.dacpac
/TargetConnectionString:[Connection string]
Also instead of passing too many parameters you could save your settings into DAC Publish Profile (this can be done from visual studio)
I wanted to build and publish a database based on a sqlproj file and log helpful information to console. Here's what I arrived at:
using Microsoft.Build.Framework;
using Microsoft.Build.Execution;
public void UpdateSchema() {
var props = new Dictionary<string, string> {
{ "UpdateDatabase", "True" },
{ "PublishScriptFileName", "schema-update.sql" },
{ "SqlPublishProfilePath", "path/to/publish.xml") }
};
var projPath = "path/to/database.sqlproj";
var result = BuildManager.DefaultBuildManager.Build(
new BuildParameters { Loggers = new[] { new ConsoleLogger() } },
new BuildRequestData(new ProjectInstance(projPath, props, null), new[] { "Publish" }));
if (result.OverallResult == BuildResultCode.Success) {
Console.WriteLine("Schema update succeeded!");
}
else {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Schema update failed!");
Console.ResetColor();
}
}
private class ConsoleLogger : ILogger
{
public void Initialize(IEventSource eventSource) {
eventSource.ErrorRaised += (sender, e) => {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(e.Message);
Console.ResetColor();
};
eventSource.MessageRaised += (sender, e) => {
if (e.Importance != MessageImportance.Low)
Console.WriteLine(e.Message);
};
}
public void Shutdown() { }
public LoggerVerbosity Verbosity { get; set; }
public string Parameters { get; set; }
}
This is for .NET 4 and above. Be sure and include assembly references to Microsoft.Build and Microsoft.Build.Framework.

Categories