How to iterate through database objects in C# using SMO - c#

I am trying to backup my database objects' scripts to disk. I have been able to put together the below code that does this, but I have to specify each object type (eg Tables, Functions, Stored Procedures).
Is there a way to loop through all these objects without specifying each collection?
using System;
using System.Data;
using Microsoft.SqlServer.Management.Smo;
using Microsoft.SqlServer.Management.Common;
using System.IO;
using System.Text;
public static void BackupDB(Server srv, Database db, string savePath)
{
string objPath;
string objFile;
string objTxt;
foreach (StoredProcedure obj in db.StoredProcedures)
{
if (obj.IsSystemObject == false)
{
objPath = savePath + "Stored Procedures\\";
Directory.CreateDirectory(objPath);
objFile = objPath + obj.Schema + "." + obj.Name + ".sql";
objTxt = GetScriptString(srv, obj);
File.WriteAllText(objFile, objTxt);
}
}
foreach (Table obj in db.Tables)
{
if (obj.IsSystemObject == false)
{
objPath = savePath + "Tables\\";
Directory.CreateDirectory(objPath);
objFile = objPath + obj.Schema + "." + obj.Name + ".txt";
objTxt = GetScriptString(srv, obj);
File.WriteAllText(objFile, objTxt);
}
}
static private string GetScriptString(Server server, SqlSmoObject obj)
{
StringBuilder output = new StringBuilder();
Scripter scr = new Scripter(server);
var script = scr.EnumScript(new SqlSmoObject[] { obj });
foreach (var line in script)
{
output.AppendLine(line);
}
return output.ToString();
}
EDIT:
Using Grant Winney's answer, I was able to put together the below method to get iterate through all the objects without repeating lines:
public static void BackupDB(Server srv, Database db, string savePath)
{
string objPath;
string objFile;
string objTxt;
Dictionary<string, IEnumerable<ScriptSchemaObjectBase>> dboDict =
new Dictionary<string, IEnumerable<ScriptSchemaObjectBase>>();
dboDict.Add("Stored Procedures", db.StoredProcedures.Cast<StoredProcedure>().Where(x => !x.IsSystemObject));
dboDict.Add("Functions", db.UserDefinedFunctions.Cast<UserDefinedFunction>().Where(x => !x.IsSystemObject));
dboDict.Add("Tables", db.Tables.Cast<Table>().Where(x => !x.IsSystemObject));
dboDict.Add("Views", db.Views.Cast<View>().Where(x => !x.IsSystemObject));
foreach (KeyValuePair<string, IEnumerable<ScriptSchemaObjectBase>> dict in dboDict)
{
IEnumerable<ScriptSchemaObjectBase> dboObjects = dict.Value;
objPath = savePath + dict.Key + "\\";
Directory.CreateDirectory(objPath);
foreach (ScriptSchemaObjectBase obj in dboObjects)
{
objFile = objPath + obj.Schema + "." + obj.Name + ".sql";
objTxt = GetScriptString(srv, obj);
File.WriteAllText(objFile, objTxt);
}
}
}

Move the similar code into a separate method, and take advantage of the fact that each of those classes implements the same base class, which is where Schema and Name are located.
You can use LINQ to filter the items first, then pass the filtered list to the other method.
public static void BackupDB(Server srv, Database db, string savePath)
{
string objPath;
string objFile;
string objTxt;
BackupObjects(db.StoredProcedures.Cast<StoredProcedure>().Where(x => !x.IsSystemObject));
BackupObjects(db.Tables.Cast<Table>().Where(x => !x.IsSystemObject));
}
private static void BackupObjects(IEnumerable<ScriptSchemaObjectBase> objects)
{
foreach (ScriptSchemaObjectBase obj in objects)
{
objPath = savePath + "Stored Procedures\\";
Directory.CreateDirectory(objPath);
objFile = objPath + obj.Schema + "." + obj.Name + ".sql";
objTxt = GetScriptString(srv, obj);
File.WriteAllText(objFile, objTxt);
}
}

Related

how to pass an additional parameter to an OnClick event, asp.net web form?

I have a function called "CreateReportForEachCompany" as following:
protected Dictionary<int, List<ReportObject>> CreateReportForEachCompany()
{
try
{
Dictionary<int, List<ReportObject>> dict = new Dictionary<int, List<ReportObject>>();
EnvironmentVariable.setEnvId((int)environmentEnum.mll);
//bakashot_tarif is a VIEW not a TABLE
DataTable dt = GeneralDbExecuterService.executeSqlSelectDataScript("select * from bakashot_tarif");
foreach (DataRow dr in dt.Rows)
{
var reportRow = FillObjectWithValues(dr);
int company_Id = dr.Field<int>("company_Id");
CreateDictionaryPairIfNotExist(dict, company_Id);
dict[company_Id].Add(reportRow);
}
return dict;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
}
now lets say I invoked it that way and got myself a dictionary with keys/values as needed.
var dictionary = CreateReportForEachCompany();
now.. I have another function named:
CreateCsvForEachCompany(Dictionary<int, List<ReportObject>> dict)
that TAKES the dictionary as parameter and creates few csvs based on the dictionary keys..
now those two functions above are executed on button click as following:
protected void ExecuteProgram(object sender, EventArgs e)
{
var dictionary = CreateReportForEachCompany();
CreateCsvForEachCompany(dictionary);
}
after that, I have a function named TransferSftpOnClick which meant to transfer file via SFTP (secure file transfer protocol ) into a designated location in the server:
void TransferSftpOnClick(Dictionary<int, List<ReportObject>> dict)
{
EnvironmentVariable.SetSubject(EnvironmentVariable.appSubjectEnum.btl);
string systemId = "MLL";
string fileType = "HIYUV-CHEVRA";
string chargeMonth = DateTime.Now.ToString("yyyyMM");
var fileCreationDate = DateTime.Now.ToString("yyyyMMdd");
string fileCreationTime = DateTime.Now.ToString("HHmmss");
string subject = EnvironmentVariable.appSubjectEnum.btl.ToString().ToUpper();
string location = EnvironmentVariable.getLocalFilePath() + #"\";
foreach (KeyValuePair<int, List<ReportObject>> entry in dict)
{
FtpUtil ftp = new FtpUtil();
int key = entry.Key;
string fileName = systemId + "-" + fileType + "-" + String.Format("{0:00000}", key) + "-" + chargeMonth + "-" + fileCreationDate + "-" + fileCreationTime + ".CSV";
**ftp.saveFileToFtp(location, key.ToString(), fileName, subject);**
}
}
I want my function to recognize the dictionary that is executed in function "ExecuteProgram()"
And I want to trigger onClick on TransferSftpOnClick that will just transfer me the files to the designated location.
how can I achieve that? is it even worth doing?
or should I just do this:
FtpUtil ftp = new FtpUtil();
ftp.saveFileToFtp(location, key.ToString(), fileName, subject);
under the CreateCsvPerCompany to avoid that mess?
hope I was clear, thanks

What is the best way to create multiple xml files and export it as one zip file

My Project is in ASP.NET MVC, Right now I am using Razor Engine Service (RazorEngineService.RunCompile) to create multiple XML files and making it as a single Zip file and exporting it.
But the problem is that when we pass the model object each time to process the template and return it as separate XML files and completing the whole operation it takes more time to complete (Almost ~40 Seconds for 10 objects) for whole content to export.
Is there anything wrong with my current approach or am I doing it correctly right now? Please guide me If I am doing any mistakes in this approach.
private FileInfo Export(List<Model> modelList)
{
string timeStr = Datetime.Now.ToString();
string archiveFileName = "Main.zip";
string archivePath = Path.Combine(_reportFolderPath, archiveFileName);
using (ZipArchive archive = ZipFile.Open(archivePath, ZipArchiveMode.Create))
{
foreach (var list in modelList)
{
string fileName = model.name + model.Id;
string filePath = GetModelExport(list, fileName, timeStr);
archive.CreateEntryFromFile(filePath, fileName + ".xml");
}
archive.Dispose();
}
return new FileInfo(archivePath);
}
private string GetModelExport(Model model, string fileName, string timeStr)
{
var processedTemplate = ProcessTemplate(model, TemplateName, TemplateKey);
string reportFilelName = fileName + "_" + timeStr + ".xml";
string filePath = Path.Combine(_reportFolderPath, reportFilelName);
using (var file = new StreamWriter(filePath))
{
file.Write(processedTemplate);
}
return filePath;
}
private string ProcessTemplate(Model model, string templateName, string templateKey)
{
var templateFilePath = Path.Combine(_reportTemplateFolder, templateName);
return ReportUtils.ProcessTemplate(templateFilePath, templateKey, model);
}
public static string ProcessTemplate(string templatePath, string templateKey, object model = null)
{
var templateService = RazorEngineService.Create();
var result = templateService.RunCompile(File.ReadAllText(templatePath), templateKey, null, model);
return result;
}
some of your code is missing so i cant see the whole picture, this is what i would start with..... gd luck.
public class HolderTempName
{
private TemplateService _templateService;
private Dictionary<string, string> _templateContainer;
public HolderTempName()
{
//this will save creating this everytime
_templateService = RazorEngineService.Create();
//this will hold the template so it does not have to fetch on each loop,
//if the same template is used.
_templateContainer = new Dictionary<string, string>();
}
//you will need to tweeek this to get the type out
private string GetTemplate(string templateName, templatePath)
{
if(!_templateContainer.Conatains(templateName))
{
var text = File.ReadAllText(templatePath);
_templateContainer[templateName] = text;
}
return _templateContainer[templateName];
}
private FileInfo Export(List<Model> modelList)
{
string timeStr = Datetime.Now;
string archiveFileName = "Main.zip";
string archivePath = Path.Combine(_reportFolderPath, archiveFileName);
using (ZipArchive archive = ZipFile.Open(archivePath, ZipArchiveMode.Create))
{
foreach (var item in modelList)
{
var templateFilePath = Path.Combine(_reportTemplateFolder, TemplateName); //<--TemplateName seems like a local private
//these should come from where cant see where...
var template = GetTemplate( TemplateName, templateFilePath)
string modelResponse = ProcessModel(item,template,TemplateKey ); //<-- why is not passing in the template
//step 2;
//making this above done in parrell and then add aync, but before all that measure what is taking time
string pathname = MakeFileName(_reportFolderPath, reportFilelName, timeStr);
SaveToDisk(pathname, modelResponse);
string fileName = model.name + model.Id;
archive.CreateEntryFromFile(filePath, fileName + ".xml");
}
archive.Dispose();
}
return new FileInfo(archivePath);
}
private string MakeFileName(string path ,string filename, string tStamp)
{
string reportFilelName = fileName + "_" + timeStr + ".xml";
string filePath = Path.Combine(_reportFolderPath, reportFilelName);
return filePath;
}
private void SaveToDisk(string filePath, string content)
{
using (var file = new StreamWriter(filePath))
{
file.Write(processedTemplate);
}
}
public static string ProcessTemplate(object model, string template, templateKey)
{
var result = templateService.RunCompile(template, templateKey, null, model);
return result;
}
}

Getting a System.StackOverflowException' error

I am Getting this error An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll I know you are not supposed to have an infinite loop but its not an infinate loop because it just has too go till it gets a file number that has not been made yet. How can i go about this a better way?
private int x = 0;
public string clients = #"F:\Internal Jobs\Therm-Air Files\Program\P-1-2.0\Clients\";
public string tdate = DateTime.Today.ToString("MM-dd-yy");
public void saveloop()
{
string path = LoadPO.Text.Substring(0, LoadPO.Text.LastIndexOf("\\"));
string name = Path.GetFileName(path);
string t = Convert.ToString(x);
if (!File.Exists(path + #"\" + name + ".xlsx")) // This Line throws error
{
oSheet.SaveAs(path + #"\" + name + "-" + t + ".xlsx");
string prop = /* snipped for space reasons, just string concats */
string Combine = string.Empty;
int b = 0;
int c = cf.cPanel.Controls.Count;
string[] items = new string[c];
foreach (WProduct ewp in cf.cPanel.Controls)
{
string item = /* snipped for space reasons, just string concats */
items[b] = item;
b += 1;
}
Combine = prop + "^<";
foreach (var strings in items)
{
Combine += strings + "<";
}
File.WriteAllText(path + #"\" + name + ".txt", Combine);
}
else
{
x += 1;
saveloop();
}
The reason the code above is failing is because you do not use i in the name of the file so you can increment all you want it does not change the name.
You need to abstract the creation of the name of the file from the code that does the writing. Think of it as writing code in blocks of functionality.
public static string GetFileName(string path, string name)
{
var fileName = $#"{path}\{name}.xlsx";
int i = 0;
while (System.IO.File.Exists(fileName))
{
i++;
fileName = $#"{path}\{name}({i}).xlsx";
}
return fileName;
}
public void saveloop()
{
var fileName = GetFileName(path, name);
// use fileName from this point on
}

Entity to XML (EF5.0)

I'm building and XML file representing and Entity. After many hours this seems to be working, but is there a better way ?
var entityContents = (from p in context.people select p).ToListAsEnumerable();
var XmlString = CollectMemebersNameValue("people" , entityContents);
public static string CollectMemebersNameValue( string entityName, IEnumerable entityQuery)
{
var xmlText = new StringBuilder();
xmlText.AppendLine("<" + entityName + ">");
foreach (var item in entityQuery)
{
xmlText.AppendLine("<Row>");
foreach (var prop in item.GetType().GetProperties())
{
if ( ! prop.PropertyType.Name.Contains("ICollection"))
{
var nname = prop.Name;
var nvalue = prop.GetValue(item, null);
xmlText.AppendLine("<" + nname + ">" + nvalue + "</" + nname + ">");
}
}
}
xmlText.AppendLine("</" + entityName + ">");
return xmlText.ToString();
}
Yes, you can use the XmlSerializer, e.g.
XmlSerializer xs = new XmlSerializer(typeof(YourObjectType));
MemoryStream ms = new MemoryStream();
xs.Serialize(ms, yourActualObject);
string sampleXml = Encoding.UTF8.GetString(ms.ToArray());
Whether you're serializing your entity or a view of your entity, it works the same way. Just ensure whatever object you are serializing is serializable.

How to Serialize a list of objects

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.Xml.Serialization;
namespace AppPrueba
{
public partial class Form1 : Form
{
ArrayList listaFilas = new ArrayList();
public Form1()
{
InitializeComponent();
}
List<Empleados> emp = new List<Empleados>();
List<Agenda> agen = new List<Agenda>();
static public void SerializeToXML(List<Agenda> agen)
{
XmlSerializer serializer = new XmlSerializer(typeof(List<Agenda>));
TextWriter textWriter = new StreamWriter(#"C:\agenda.xml");
serializer.Serialize(textWriter, agen);
textWriter.Close();
}
private void button1_Click(object sender, EventArgs e)
{
openFileDialog1.ShowDialog(this);
string strfilename = openFileDialog1.FileName;
txtPath.Text = strfilename;
if ((strfilename.Trim().Length > 0) && (File.Exists(strfilename)))
{
string[] readText = File.ReadAllLines(strfilename);
if (strfilename.EndsWith("Agenda.txt"))
{
lstLinesBeforeChange.Items.Clear();
foreach (string s in readText)
{
lstLinesBeforeChange.Items.Add(s);
}
}
else
{
lstLinesBeforeChange.Items.Clear();
foreach (string s in readText)
{
lstLinesBeforeChange.Items.Add(s);
}
}
}
}
private void btnModify_Click(object sender, EventArgs e)
{
string strfilename = txtPath.Text;
string[] readText = File.ReadAllLines(strfilename);
if (strfilename.EndsWith("Agenda.txt"))
{
lstLinesAfterChange.Items.Clear();
foreach (string s in readText)
{
int nroEmp = Convert.ToInt32(s.Substring(0, 4));
string nombre = s.Substring(4, 15);
string telef = s.Substring(19, 10);
string ciudad = s.Substring(29);
Agenda unAgenda = new Agenda();
unAgenda.NroEmp = nroEmp;
unAgenda.Nombre = nombre.TrimStart().TrimEnd();
unAgenda.Telefono = telef;
unAgenda.Localidad = ciudad;
agen.Add(unAgenda);
agen.Sort(delegate(Agenda a1, Agenda a2)
{
return a1.NroEmp.CompareTo(a2.NroEmp);
});
}
foreach (Agenda a in agen)
{
string agenOrd = a.NroEmp.ToString() + "\t" + a.Nombre + "\t" + a.Telefono + "\t" + a.Localidad;
lstLinesAfterChange.Items.Add(agenOrd);
}
}
else
{
lstLinesAfterChange.Items.Clear();
foreach (string s in readText)
{
string[] sSinBarra = s.Split('|');
int nroEmp = Convert.ToInt32(sSinBarra[0]);
string nombre = sSinBarra[1];
string posicion = sSinBarra[2];
int nroOficina = Convert.ToInt32(sSinBarra[3]);
int piso = Convert.ToInt32(sSinBarra[4]);
string fechaIng = sSinBarra[5];
int dia = Convert.ToInt32(fechaIng.Substring(0, 2));
int mes = Convert.ToInt32(fechaIng.Substring(2, 2));
int año = Convert.ToInt32(fechaIng.Substring(4, 4));
string fechaIngreso = dia + "/" + mes + "/" + año;
fechaIng = fechaIngreso;
Empleados unEmpleado = new Empleados();
unEmpleado.NroEmpleado1 = nroEmp;
unEmpleado.Nombre1 = nombre.TrimEnd().TrimStart();
unEmpleado.Posicion = posicion.TrimEnd().TrimStart();
unEmpleado.NroOficina = nroOficina;
unEmpleado.Piso = piso;
unEmpleado.FechaIngreso = fechaIngreso;
emp.Add(unEmpleado);
emp.Sort(delegate(Empleados e1, Empleados e2)
{
return e1.NroEmpleado1.CompareTo(e2.NroEmpleado1);
});
}
foreach (Empleados em in emp)
{
string empOrd = em.NroEmpleado1.ToString() + "\t" + em.Nombre1 + "\t" + em.Posicion + "\t" + em.NroOficina.ToString()
+ "\t" + em.Piso.ToString() + "\t" + em.FechaIngreso;
lstLinesAfterChange.Items.Add(empOrd);
}
}
}
private void btnSave_Click(object sender, EventArgs e)
{
//SaveFileDialog saveFileDialog1 = new SaveFileDialog();
//saveFileDialog1.ShowDialog();
//if (saveFileDialog1.FileName != "")
//{
// FileStream fs = (FileStream)saveFileDialog1.OpenFile();
// fs.Close();
//}
SerializeToXML(agen);
}
}
}
I have this, and i want to serialize to xml both lists :
List<Empleados> emp = new List<Empleados>();
List<Agenda> agen = new List<Agenda>();
I used SerializeToXML method that i found in other tutorial but when i run it an error shows up
"Error 1 Inconsistent accessibility: parameter type
'System.Collections.Generic.List' is less accessible
than method
'AppPrueba.Form1.SerializeToXML(System.Collections.Generic.List)' C:\Users\722825\Desktop\Santi
Cosas\AppPrueba\AppPrueba\Form1.cs 27 28 AppPrueba"
Thanks in advance if you can help me!
All the classes that you are trying to serialize to xml should be public. And for collection serialization you cannot use generics - either use untyped collections, like, ArrayList , or create a non-generic descendant class form List .
agen is private and SerializeToXML is public static. You need agen public
Your error says that the variabel List agen = new List(); needs to be public like this: public List agen = new List();

Categories