I have a script component in SSIS on SQLServer 2008 R2, that needs to be able to write to a Read-Write variable to produce a file name for a flat file export. I created a package level variable to hold the file name, and set the flat file connection to use an expression containing the variable..
I have a script component that, among other things, builds the file name dynamically in the post execute method. I've set the variable in the ReadWriteVariables setting of the script component.
The package will immediately fail if I don't have a default value in the variable, because the flat file connection manager tries to evaluate the expression to set up the destination file. So, I just put in a placeholder file name.
The problem is that now it always uses the placeholder filename instead of the one that the script specifies. What's the best way to make sure that I can write to those variables? I tried Variables.VariableName = "value", I've also tried using VariableDispenser and this.ReadWriteVariables["VariableName"].value, and none of them are persisting the value I set in the script.
Here is one way you can assign value to a package variable from within Script Component available inside Data Flow Task.
I try to lock the variables inside the Script Task or Script Component instead of specifying them on Properties dialog. I feel this is easier to maintain.
In the following example, I have a package variable named FileName and the variable is being assigned with the value C:\Temp\PathChanged inside the Script Component.
I believe that Script Component may not be the right place to manipulate the package variable value such as file name but again that depends on what you are trying to do.
Hope that helps.
/* Microsoft SQL Server Integration Services Script Component
* Write scripts using Microsoft Visual C# 2008.
* ScriptMain is the entry point class of the script.*/
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
public override void PreExecute()
{
base.PreExecute();
}
public override void PostExecute()
{
base.PostExecute();
IDTSVariables100 varCollection = null;
this.VariableDispenser.LockForWrite("User::FileName");
this.VariableDispenser.GetVariables(out varCollection);
varCollection["User::FileName"].Value = #"C:\Temp\PathChanged";
}
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
}
}
Related
I have some data specific to each razor view and and i do not want to hard-code it to each view. So, i want to add view related compile-time data to each view.
Custom attributes do not work for me because we cannot add custom attributes to razor views.
I do not want to re-fetch/populate this data from the data source(dictionary etc.) for each request or when view reached.
So, is there any way to attach data to each view at once throughout the life time of asp.net application?
Note
Actually i want to add scripts/styles generated by webpack for each view statically. Their links include hash values so they change when source scripts/styles change. So, i just want to get them added to each view only once(equivalent to typing them into view) through out the asp.net application, not every time a view loads.
I created a demo application for you here.
You will want to use your appsettings.json file, and inject your settings into your view.
In my appsettings.json I added a section called "ViewConfiguration":
"ViewConfiguration": {
"ExampleKey": "ExampleValue"
}
Your various values will need to go into your ViewConfiguration section.
For example where I have ExampleKey, you will use a generic name like "IndexPageStyleSheet", and where I have ExampleValue, you will need to update each release with the new stylesheet path. This will only need to be updated when the filename changes.
I then created a ViewConfiguration class which stores all of the values from the appsettings.json file.
You will need to create one property per configuration line, and ensure that the name of the property matches the name of the key in your appsettings.json.
For example where my appsettings.json has ExampleKey, my ViewConfiguration class also has an ExampleKey.
public class ViewConfiguration {
public string ExampleKey { get; set; }
}
In your Startup.cs you will need to tell your IOC container to load your configuration values into your configuration object.
In my Startup.cs, my ConfigureServices method loads my "ExampleValue" into ViewConfiguration.ExampleKey automatically.
public void ConfigureServices(IServiceCollection services) {
// This line is the magic that loads the values from appsettings.json into a ViewConfiguration object.
services.Configure<ViewConfiguration>(Configuration.GetSection("ViewConfiguration"));
services.AddMvc();
}
Now, in my _ViewImports.cshtml I inject my ViewConfiguration object so that I don't need to inject it into every single page. This can be anywhere in the _ViewImports.cshtml file. If you only want to inject specific configuration per folder, you can create a new _ViewImports.cshtml file per folder and inject different configuration objects into each one. It's flexible.
#using Microsoft.Extensions.Options;
#* Please rename this variable to something more appropriate to your application: *#
#inject IOptions<ViewConfiguration> InjectedViewConfig
Now, in any page, you can simply reference the property in your ViewConfiguration object.
For example in my Index.cshtml, I reference the ViewConfiguration.ExampleKey property by referencing the strongly typed property on InjectedViewConfig.Value, and it outputs "ExampleValue" on the page.
This value could just as easily be injected into a script or css link tag as the name of a file. It's very flexible.
<h1>Value: #InjectedViewConfig.Value.ExampleKey</h1>
With further research, you will be able to inject these values from any configuration source, such as Azure application settings or Azure Key Vault. Please see this article for more details.
If you are using mvc, you can create models and add it into the views. Since you don't want to recreate for each view, you can create readonly variables.
static readonly MyModel ModelData = new MyModel { PropName = "Hello" };
public IActionResult Index () => View(ModelData);
In your view you can now strongly type the value. If you are looking to use MVVM, you can refer to ViewModel concept still exists in ASP.NET MVC Core?
Implementing IFileProvider and IFileInfo provides changing the contents of view at compile-time. So, we could replace and provide static data in views with a template engine(i.e. http://dotliquidmarkup.org/).
Check this;
https://www.mikesdotnetting.com/article/301/loading-asp-net-core-mvc-views-from-a-database-or-other-location
I am trying to load some of the views from the database as described in here. So I want to use EF Core in the File provider.
RazorViewEngineOptions has a FileProviders property that you can add your file provider to. The problem is that you have to give it an instace of the file provider. So you'll need to instantiate all of the file providers' dependencies right there in Startup's ConfigureServices method.
Currently I inject an instance of IServiceProvider into the Configure method of Startup. Then I store the instance in a field (called _serviceProvider):
IServiceProvider _serviceProvider;
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider provider)
{
_serviceProvider = provider;
...
}
Then in ConfigureServices I use that field to instanciate the UIDbContext.
services.Configure<RazorViewEngineOptions>(options =>
{
var fileProvider = new DbFileProvider(_serviceProvider.GetService<UIDbContext>());
options.FileProviders.Add(fileProvider);
});
Is there any better way to be able to inject the UIDbContext into the DbFileProvider constructor Or any way to instantiate a UIDbContext inside DbFileProvider without IServiceProvider?
You don't want to use DbContext as a file provider source the way you did.
DbContext isn't thread-safe, so it won't work when you have one single DbContext instance for the whole provider, because multiple requests could call the DbContext and it's operation more than once at the same time, resulting in exception when trying to execute 2 queries in parallel.
You'd have to instantiate a connection (like in the linked article) or DbContext per IFileInfo/IDirectoryContents instance.
DbContextOptions<UIDbContext> should be registered as singleton, so you can resolve it onceinside Configure` w/o any issues and pass it to your provider.
Alternatively you can also call DbContextOptionsBuilder and build/construct a DbContextOptions<T>, but then you have to repeat the configuration for you did inside AddDbContext (i.e. .UseSqlServer()).
However it can be useful, as it allows you to set different settings (i.e. changing the way how includes, errors etc. are logged).
So SimpleInjector now has a packaging nuget that you can use to isolate different aspects of root composition.
Say I have a configurable composition root in a library that is reused by multiple projects in an application. For example, in an Azure solution I might have a Web role and a Worker role which share a large set of the same dependencies for the most part, but with slightly different configuration options depending on the consumer. When I compose the root, I can pass in a plain old RootCompositionSettings object with properties that tell SimpleInjector how to register dependencies.
However, I am not sure how (or if) I can pass these settings to an IPackage instance. Is it possible to pass custom settings to a SimpleInjector package, and if so, how?
I see that the standard practices for registering packages is to invoke either
container.RegisterPackages(); // scans all loaded assemblies for IPackage
// or
container.RegisterPackages(IEnumerable<Assembly>) // specific assemblies only
...so how can we pass parameters into the packaging instance(s)? Is there some way to do it via the container?
The trick here is to pass the information on with the container to the package. You can do this by using the container's Items dictionary, that is much like ASP.NET's HttpContext.Items collection. This can be done as follows:
using SimpleInjector.Advanced;
container.SetItem(typeof(RootCompositionSettings), settings);
container.RegisterPackages();
Now inside your packages, you can do the following:
var settings =
(RootCompositionSettings)container.GetItem(typeof(RootCompositionSettings));
Please note that:
SetItem and GetItem are extension methods that are located in the SimpleInjector.Advanced namespace. Those methods allow you to access the (internal) Items dictionary.
You can pass in any key you like. Passing in typeof(RootCompositionSettings) is just convenient in this case, but not required.
If you need to call the settings in more places, it might be useful to create a more specific extension method that allows you to access the setting instance, but that's up to you.
Another option is to not use the IPackage interface and the SimpleInjector.Packaging library at all. In most cases it doesn't really add anything and you could simply define a public static method in the assembly that does the same as a package does. For instance:
public static class BusinessLayerBootstrapper
{
public static void Bootstrap(Container container, ScopedLifestyle scopedLifestyle,
RootCompositionSettings settings)
{
// Here the same logic as what you would write in your package.
}
}
Most applications are not that dynamic that you need to load assemblies dynamically and the startup project usually has a hard reference to all the other assemblies. In that case it is perfectly sane to simply call a static method.
And even if you have the requirement of dynamically loading assemblies and allowing them to register their stuff in the container, it's quite trivial to build your own IPackage abstraction instead:\
// Your own IPackage interface
public interface IPackage
{
void RegisterServices(Container container, RootCompositionSettings settings);
}
// Your own extension method
public static void RegisterPackages(this Container container,
RootCompositionSettings settings)
{
var packages =
from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetExportedTypes()
where typeof(IPackage).IsAssignableFrom(type)
where !type.IsAbstract
select (IPackage)Activator.CreateInstance(type);
packages.ToList().ForEach(p => p.RegisterServices(container, settings));
}
In fact, except for some extra validations and filtering out dynamic assemblies, the SimpleInjector.Packaging project is not much more than this.
I am using SSIS 2012 and i need to figure out the best way to load multiple configuration files to be used in a custom script.
This is the way it goes:
I need to use a custom script to access a NoSQL database
In this case, the NoSQL database has no rigid schema, therefore the attribute change from document to document
I want to use configuration files to tell how the columns are supposed to be renamed and configure there other basic rules.
the above task is easily done in c#, however if possible i would like read the configuration files using a SSIS component (to read a flat file, excel file or database rules). Therefore i want to know how can i feed the custom script with the data from the stream, the scipt consumes stream (the stream contains the configuration), and after consuming the entire stream, the script component generates rows.
An example case would be be:
script reads an entire stream of numbers.
the script orders the numbers on the stream
the script discards duplicates the script
outputs the ordered sequence of numbers without duplicates.
If I understood correctly, the NoSql database and configuration files just background of the problem and what you really need is an asynchronous
script component to read everything from the pipeline, then do something and finally send the results back to the pipeline?
If so then what you need is create an script component with it's output buffer set to SynchronousInputId=None.
The example of the numbers to be deduped and sorted that you posted could then be solved with the following pseudo-code
(assume you create an output column in the output buffer of the script component called "numberout"
and output buffer property SynchronousInputId is set to None) :
...
public override void PreExecute()
{
base.PreExecute();
CREATE ARRAY TO HOLD NUMBERS
}
public override void PostExecute()
{
base.PostExecute();
SORT AND DEDUPE ARRAY
FOR EACH N IN ARRAY:
output0buffer.addrow()
output0byffer.numberout=N
}
public override void Input0_ProcessInputRow(Input0Buffer Row)
{
INSERT NUMBER TO ARRAY
}
I have a .settings file (containing both User and Application settings) that I would like to contain different values if built in Debug mode. Is there a supported or recommended way to do this?
I use two separate config files for each mode. I copy the files into the BIN folder in the POST-BUILD event.
The Settings.Designer.cs file doesn't contain values, only application setting property declarations. The values are stored separately. Application setting values go into the app.exe.config file, user scoped setting values go in an appdata folder that has a name generated by a hashing algorithm. You'd only have trouble with the latter. Shouldn't matter, the file won't exist when you deploy your Release build to a machine.
In case you mean "can I change the default value" then the answer is: not when you use the settings designer. You'll have to move the setting into a separate class. Make it look similar to this:
using System;
using System.Configuration;
using System.Diagnostics;
namespace ConsoleApplication1.Properties {
internal partial class Settings {
[UserScopedSetting, DebuggerNonUserCode]
#if DEBUG
[DefaultSettingValue("debug value")]
#else
[DefaultSettingValue("release value")]
#endif
public string Setting {
get {
return ((string)(this["Setting"]));
}
set {
this["Setting"] = value;
}
}
}
}
Make sure the namespace name matches the one used in the Settings.Designer.cs file and that you delete the setting from the Settings page.