Figure out name of project in SSIS - sql-server

Is it possible to figure out the name of the project while in SSIS either through a script task, or a variable?
I know how to get the PackageName, and the TaskName, but in this case, I need the ProjectName.

This is, admittedly, holy-set-the-house-on-fire-hacktastic, but it works when run via SSDT... You need a couple of things for this Script Task to work:
1. A variable to access so you can climb to its parent.
2. A reference to Microsoft.SqlServer.DTSRuntimeWrap
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using RuntimeWrapper = Microsoft.SqlServer.Dts.Runtime.Wrapper;
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
public void Main()
{
RuntimeWrapper.IDTSProject100 p = ((RuntimeWrapper.IDTSProject100)((Package)Dts.Variables["User::myVar"].Parent).Project);
string projectName = GetProjectName(p);
Dts.TaskResult = (int)ScriptResults.Success;
}
private string GetProjectName(RuntimeWrapper.IDTSProject100 proj)
{
System.Reflection.PropertyInfo pInfo = proj.GetType().GetProperty("Project",
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
Project p = (Project)pInfo.GetValue(proj, null);
return p.Name;
}
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
}

This is a possible solution I tried a while ago.
Just Map System::VersionGUID to #Version and System::PackageID to #Package as the below query.
DECLARE #Package uniqueidentifier = ?
DECLARE #Version uniqueidentifier = ?
DECLARE #ProjectName nvarchar(100) = (
SELECT proj.name
FROM [catalog].[packages] pkg
INNER JOIN catalog.projects proj ON pkg.project_id = proj.project_id
WHERE package_guid= #Package
AND version_guid = #Version
)
SELECT ISNULL(#ProjectName, 'running from ssdt') as ProjectName;
If the project is deployed in SSIS Catalogs and it is not running from SSDT you will get the project name, otherwise it will return null using the ISNULL function you can put whatever. I hope this can help you someway.
EDIT: version_guid clarification
The version_guid will be different if the project has been deployed in several folders.
Looking for documentation msdn site is very short about version_guid property.
Gets the version GUID generated when the package is first created.
This field is read-only.
Asumming the creation of the package in the SSISDB will be at deployment the version_guid should be different for every project deployment.
I didn't test in deep but I recreated that scenario: deploy the same project in two different folders.
Note package_guid and version_guid columns.

Related

Getting "NEXT VALUE FOR" for a SQL Server sequence using EF Core 3.1 - impossible?

I'm writing a new ASP.NET Core Web API, and one of my requirements is to be able to leverage EF Core 3.1 to grab the next value of a sequence defined in my SQL Server as the ID for a record I need to store.
I'm struggling to find a way to do this - in EF 6.x, I used a method directly on the DbContext descendant like this:
public int GetNextSequenceValue()
{
var rawQuery = Database.SqlQuery<int>("SELECT NEXT VALUE FOR dbo.TestSequence;");
var task = rawQuery.SingleAsync();
int nextVal = task.Result;
return nextVal;
}
and for EF Core up to 2.1, I would have been able to use Database.ExecuteSqlCommand() to run a SQL snippet and get back results. But it seems, in EF Core 3.x, I'm out of luck....
I know there are the .FromSqlRaw() and .FromSqlInterpolated methods on the DbSet - but since I only need to return the next value of a sequence (an INT), that's not going to fly. And I also know these methods also exist on the context.Database level which looks like it would be really close to what I had in EF 6.x - but here, those methods will only return the number of rows affected - I haven't found a way to send back the new value from the SEQUENCE.
Can it really be that in EF Core 3.x, I have to actually resort back to way-old ADO.NET code to fetch that value?? Is there REALLY no way to execute an arbitrary SQL snippet and get back some results from the context??
If you want to run an arbitrary TSQL batch and return a scalar value, you can do it like this:
var p = new SqlParameter("#result", System.Data.SqlDbType.Int);
p.Direction = System.Data.ParameterDirection.Output;
context.Database.ExecuteSqlRaw("set #result = next value for some_seq", p);
var nextVal = (int)p.Value;
Looks like executing raw SQL is not priority for EF Core, so up to now (EF Core 3.1) it's providing publicly just few basic limited methods. FromSql requires entity type or keyless entity type, and ExecuteSqlRaw / ExecuteSqlInterpolated are the "modern" bridge to ADO.NET ExecuteNonQuery which returns the affected rows.
The good thing is that EF Core is built on top of a public service architecture, so it can be used to add some missing functionalities. For instance, services can be used to build the so called IRelationalCommand, which has all the DbCommand execute methods, in particular ExecuteScalar needed for SQL in question.
Since EF Core model supports sequences, there is also a service for building the IRelationalCommand needed to retrieve the next value (used internally by HiLo value generators).
With that being said, following is a sample implementation of the custom method in question using the aforementioned concepts:
using System;
using System.Globalization;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Update;
namespace Microsoft.EntityFrameworkCore
{
public static partial class CustomExtensions
{
public static long GetNextSequenceValue(this DbContext context, string name, string schema = null)
{
var sqlGenerator = context.GetService<IUpdateSqlGenerator>();
var sql = sqlGenerator.GenerateNextSequenceValueOperation(name, schema ?? context.Model.GetDefaultSchema());
var rawCommandBuilder = context.GetService<IRawSqlCommandBuilder>();
var command = rawCommandBuilder.Build(sql);
var connection = context.GetService<IRelationalConnection>();
var logger = context.GetService<IDiagnosticsLogger<DbLoggerCategory.Database.Command>>();
var parameters = new RelationalCommandParameterObject(connection, null, null, context, logger);
var result = command.ExecuteScalar(parameters);
return Convert.ToInt64(result, CultureInfo.InvariantCulture);
}
}
}
In your fluent api configs you can create migration that set ID automatically to be next value from Sequence
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasSequence<int>("OrderNumbers");
modelBuilder.Entity<Order>()
.Property(o => o.OrderNo)
.HasDefaultValueSql("NEXT VALUE FOR shared.OrderNumbers");
}
For creating sequence:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasSequence<int>("OrderNumbers", schema: "shared")
.StartsAt(1000)
.IncrementsBy(5);
}
Read more from here: https://www.talkingdotnet.com/use-sql-server-sequence-in-entity-framework-core-primary-key/
For people suffering with oracle version of this problem, here's a solution:
var p = new OracleParameter("result", OracleDbType.Decimal, null, System.Data.ParameterDirection.Output);
Database.ExecuteSqlRaw($"BEGIN :result := my_seq.nextval; END;", p);
var nextVal = p.Value;
Ugly, but the best thing I could come up with:
var connection = repcontext.Database.GetDbConnection();
connection.Open();
using var cmd = connection.CreateCommand();
cmd.CommandText = "SELECT NEXT VALUE FOR AA.TransSeq;";
var obj = cmd.ExecuteScalar();
connection.Close();
seqnum = (int)obj;
This code should work in a variety of situations:
public static class DbSequence
{
private const string sqlCode = "SELECT NEXT VALUE FOR {0}.{1};";
public static T GetNextSeq<T>(this DbContext dbContext, string seqName)
{
var sqlCnn = dbContext.Database.GetDbConnection();
bool cnnClosed = sqlCnn.State != ConnectionState.Open;
if (cnnClosed) sqlCnn.Open();
try
{
using (var sqlCmd = sqlCnn.CreateCommand())
{
sqlCmd.Transaction = dbContext.Database.CurrentTransaction?.GetDbTransaction();
sqlCmd.CommandText = string.Format(sqlCode, "dbo", seqName);
var result = sqlCmd.ExecuteScalar();
if ((result == null) || (result == DBNull.Value)) throw new InvalidOperationException();
return (T)result;
}
}
finally
{
if (cnnClosed) sqlCnn.Close();
}
}
}
This code works when the connection is closed, opening it when needed and closing it after itself. It should also work when a transaction has been initiated. According to this source: https://learn.microsoft.com/en-us/sql/t-sql/statements/create-sequence-transact-sql?view=sql-server-ver16#general-remarks sequences run outside of transactions. Still, if it's available, I set up a transaction for the command. I also use generics and extension methods.

Load latest flat files via SSIS

I have to load 2 flat files into a SQL Server table. Flat files are loaded to a folder. It has thousands of other files. If it was same file with different dates I would have used foreach loop and done it..but here is the scenario.
File names I want to load are as follows:
Non_Payment_Stat_Data1_12_2017.txt
Payment_Stat_Data1_12_2017.txt
Files are loaded daily
I need to load just above file type to table (pick the days load)
There are many other files some of which are Payment_Stat_Data or Non_Payment_Stat_Data without the date part at the end. We don't want to load these into the table.
I tried using script task c# code and it gave me latest file but not the one we wanted to load.
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.IO;
namespace ST_2650e9fc7f2347b2826459c2dce1b5be.csproj
{
[System.AddIn.AddIn("ScriptMain", Version = "1.0", Publisher = "", Description = "")]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
#region VSTA generated code
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
public void Main()
{
// TODO: Add your code here
var directory= new DirectoryInfo(Dts.Variables["User::VarFolderPath"].Value.ToString());
FileInfo[] files = directory.GetFiles();
DateTime lastModified = DateTime.MinValue;
foreach (FileInfo file in files)
{
if (file.LastWriteTime > lastModified)
{
lastModified = file.LastWriteTime;
Dts.Variables["User::VarFileName"].Value = file.ToString();
}
}
MessageBox.Show(Dts.Variables["User::VarFileName"].Value.ToString());
Dts.TaskResult = (int)ScriptResults.Success;
}
}
}
Source:http://www.techbrothersit.com/2013/12/ssis-how-to-get-most-recent-file-from.html
The code works but gives another latest flat file.. I only want to pull Non_Payment_Stat_Data1_12_2017.txt and Payment_Stat_Data1_12_2017.txt files. They will have the date changing every day.
If the file is named in a predictable fashion based on today's date (which you seem to be saying it is), then just use an expression for the file name(s) in the Flat File Connection Manager.

How to read, edit and export word documents in WPF without Microsoft office being installed?

I have an WPF application that relies heavily on manipulating documents; I want to know if there is a library that works independetly from Microsoft Office Word and that provides the following features:
Reading word documents (*.doc or rtf will be suffisiant, *.docx will be perfect)
Enable me to edit the document from my WPF app
Enable me to export again the document into other formats (word, excel, pdf)
Free :)
Thanks in advance.
I will try to answer in order:
Reading: This article is good for you.
Edit & export: May be this library works for you.
Free: The most difficult part of your question. You can do it for free using Interop Assemblies for Office. But controls for free... Many controls not free around the net.
Hope it helps.
I was faced with similar question some years ago. I had Windows forms application with some 20 reports and about 100 users and I needed to generate Word documents from application. Application was installed on a server. My first attempt was done by using Office interop, but it caused problems with performance and all kinds of unpredictable exceptions. So I started to look for alternatives and I soon landed with OpenXML.
First idea was that our team would use OpenXML SDK to generate and manipulate documents. It soon turned out that the learning curve was way too steep and our management wasn't willing to pay for the extra work.
So we started to look for alternatives. We didn't find any useful free library and so we tried some commercial ones (Aspose, Docentric). Aspose gave great results, but it was too expensive. Docentric's license is cheaper and the product performed well in Word document generation, so we finally decided to purchase it.
WHAT IT TAKES TO GENERATE A DOCUMENT FROM A TEMPLATE
Install Docentric Toolkit (you can get 30 day trial version for free)
In your VisualStudio project ad references to 4 Docentric dlls, which you can find in installation folder C:\Program Files (x86)\Docentric\Toolkit\Bin
Include Entity Framework via NuGet package If you will fill data from SQL database into the Word document
Prepare Word template, where you define layout and include fields which will get filled with data at document generation (see on-line documentation how to do it).
It doesn't take much code to prepare the data to be merged with the template. In my example I prepare order for customer "BONAP" from Northwind database. Orders include customer data, order details and product data. Data model also includes header and footer data.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Docentric.Word;
using System.Diagnostics;
namespace WordReporting
{
// Report data model
public class ReportData
{
public ReportData()
{ }
public string headerReportTemplatetName { get; set; }
public string footerDateCreated { get; set; }
public string footerUserName { get; set; }
public List<Order> reportDetails { get; set; }
}
// model extensions
public partial class Order
{
public decimal TotalAmount { get; set; }
}
public partial class Order_Detail
{
public decimal Amount { get; set; }
}
// Main
class Program
{
static void Main(string[] args)
{
// variable declaration
List<Order> orderList = new List<Order>();
string templateName = #"c:\temp\Orders_template1.docx";
string generatedDocument = #"c:\temp\Orders_result.docx";
// reading data from database
using (var ctx = new NorthwindEntities1())
{
orderList = ctx.Orders
.Include("Customer")
.Include("Order_Details")
.Include("Order_Details.Product")
.Where(q => q.CustomerID == "BONAP").ToList();
}
// collecting data for the report
ReportData repData = new ReportData();
repData.headerReportTemplatetName = templateName;
repData.footerUserName = "<user name comes here>";
repData.footerDateCreated = DateTime.Now.ToString();
repData.reportDetails = new List<Order>();
foreach (var o in orderList)
{
Order tempOrder = new Order();
tempOrder.Customer = new Customer();
tempOrder.OrderID = o.OrderID;
tempOrder.Customer.CompanyName = o.Customer.CompanyName;
tempOrder.Customer.Address = o.Customer.Address;
tempOrder.Customer.City = o.Customer.City;
tempOrder.Customer.Country = o.Customer.Country;
tempOrder.OrderDate = o.OrderDate;
tempOrder.ShippedDate = o.ShippedDate;
foreach (Order_Detail od in o.Order_Details)
{
Order_Detail tempOrderDetail = new Order_Detail();
tempOrderDetail.Product = new Product();
tempOrderDetail.OrderID = od.OrderID;
tempOrderDetail.ProductID = od.ProductID;
tempOrderDetail.Product.ProductName = od.Product.ProductName;
tempOrderDetail.UnitPrice = od.UnitPrice;
tempOrderDetail.Quantity = od.Quantity;
tempOrderDetail.Amount = od.UnitPrice * od.Quantity;
tempOrder.TotalAmount = tempOrder.TotalAmount + tempOrderDetail.Amount;
tempOrder.Order_Details.Add(tempOrderDetail);
}
repData.reportDetails.Add(tempOrder);
}
try
{
// Word document generation
DocumentGenerator dg = new DocumentGenerator(repData);
DocumentGenerationResult result = dg.GenerateDocument(templateName, generatedDocument);
// start MS Word and show generated document
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "WINWORD.EXE";
startInfo.Arguments = "\"" + generatedDocument + "\"";
Process.Start(startInfo);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
// wait for the input to terminate the application
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();
}
}
}
}

SQL Server Session Serialization in ASP.Net MVC

I am new to ASP.Net MVC . Any help is greatly appreciated in resolving my problem.
I am using a LINQToSQL db in my MVC application. For one of the auto generated partial class (Example MyClass assume for table MyClass) , I created another Partial class as MyClass and added DataAnnotations Like following...
namespcae NP
{
[MetadaType(typeof(myData))]
[Serializable()]
public partial class MyClass
{
}
public myData
{
[Required]
public string ID { get ; set ;}
// Other properties are listed here
}
}
In my controller class example MyHomeController
I have a code as follows:
List<MyClass> list = new List<MyClass>();
list = dbContext.StoredProcedure(null).ToList<MyClass>()
session["data"] = list.
above code works fine if I use inProc session state. But if I use SQLServer mode then I get error as
"Unable to serialize the session state. In 'StateServer' and
'SQLServer' mode, ASP.NET will serialize the session state objects,
and as a result non-serializable objects or MarshalByRef objects are
not permitted. The same restriction applies if similar serialization
is done by the custom session state store in 'Custom' mode. "
Can anyone tell me what I am doing wrong here..?. I can see the data is getting populated in ASPState database tables. By application throws error as follows.
Just mark as Serializable all classes whose instances you want to store in Session.
Finally I was able to resolve the issue.
Solution:
Add the below statement before querying the database. In my case I was calling LinqToSQl context( dbContext).
dbContext.ObjectTrackingEnabled = false;
Sample Code:
List empList = new List();
dbContext.ObjectTrackingEnabled = false;
empList = dbContext.SomeStoredProcedure().ToList()
Session["employee"] = empList.

Use SmallDateType in Model-First Entity Framework

I can't help but feel I am missing something but to this day I cannot find the answer.
I am doing a model-first entity framework and have a few properties set as DateTime. These translate to DateTime in the database - but I would like to use SmallDateTime. In my case, getting down to seconds and milliseconds just isn't worth the double storage for as many rows as I will have.
Does anyone know a way in the model-first environment to map DateTime to a SmallDateTime DB field? As a last hope, I can generate the DDL, replace all, and update the Model from the database after - but I feel that is obnoxious!
Thanks in advance.
It's a kind of tricky thing.
First of all read this article Model first if you have not yet.
Then create separate class library project with custom IGenerateActivityOutput implementation.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Data.Entity.Design.DatabaseGeneration.OutputGenerators;
using System.Activities;
namespace MyCSDLToSSDL
{
public class MyCsdlToSsdl: IGenerateActivityOutput
{
private CsdlToSsdl _generator;
public MyCsdlToSsdl()
{
_generator = new CsdlToSsdl();
}
public T GenerateActivityOutput<T>(OutputGeneratorActivity owningActivity, NativeActivityContext context, IDictionary<string, object> inputs) where T : class
{
var str = _generator.GenerateActivityOutput<T>(owningActivity, context, inputs) as string;
return str.Replace("Type=\"datetime\"", "Type=\"smalldatetime\"") as T;
}
}
}
The next step is to modify database generation workflow.
Locate TablePerTypeStrategy.xaml in your file system.
Copy this file to the same folder with different name. TablePerTypeStrategy_smalldatetime.xaml for example. Open it with VS.
Change OutputGeneratorType of CsdlToSsdlAndMslActivity to "MyCSDLToSSDL.MyCsdlToSsdl, MyCSDLToSSDL". Double quotes are required.
Change database generation workflow property to "TablePerTypeStrategy_smalldatetime.xaml (VS)".
Try to generate database from model.
Looks very much like a workaround but it works. :) Hope it helps!

Resources