I have a lot of local resourse files
/Controls/App_LocalResources/SomeControl.ascx.resx,
/Pages/App_LocalResources/SomePage.aspx.resx, etc.
I want to add another language and I do not want to go through all folders and add SomeControl.ascx.de.resx files for example and then have to recompile the whole think.
I would like to use satellite assemblies and embed all the files into something like MyWebPage.de.dll
This was possible in VS2003 version for global resources, but I'm not sure can I do it in VS2008 version for local resources?
I am accessing the resource with the syntax:
<asp:label id="lblSomething" runat="server" meta:resourcekey="labelFirstName"/>
Your question isn't really too clear on whether you are looking for a feature of VS2008 or a feature of the ASP.NET framework. So I'll go with the code solution.
The implicit binding syntax you're using uses ASP.NET's default LocalResourceProvider which takes in the path of page under which the resources live to work out which resources to load. If your resources are stored elsewhere and you still want to use the implicit binding systax in your code in front you'll need to use your own Provider. Sounds complicated but it's fairly straightforward.
To do this you'll need to first subclass ResourceProviderFactory
and override both
IResourceProvider CreateGlobalResourceProvider(string classKey)
IResourceProvider CreateLocalResourceProvider(string virtualPath)
...then implement your own IResourceProvider that gets your resources from your satellite assemblies using a ResourceManager
public interface IResourceProvider
{
object GetObject(string resourceKey, CultureInfo culture);
IResourceReader ResourceReader { get; }
}
You then need to add configuration to your web.config file to let ASP.NET know to use your SatelliteResourceProviderFactory and move your resources in to your external assemby, but that should be you good to go.
Plenty of documentation can be found here...under the "Building a Database Resource Provider" section...
http://msdn.microsoft.com/en-us/library/aa905797.aspx#exaspnet20rpm_topic4
Related
In The Olden Days
In a web.config file, settings could be placed in an appSettings section like so:
<appSettings>
<add key="mysetting" value="123"/>
</appSettings>
Even though my web.config file was in my web project, any assemblies/libraries used in that project could access the settings using:
ConfigurationManager.AppSettings["mysetting"]
Today (and the problem)
I am starting to use .NET core, and just like before, I have assemblies/libraries that are not web projects in of themselves and need to access various configuration settings.
Microsoft's Configuration documentation (https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration) along with all of the other examples I can find, have the configuration class being consumed by a controller and don't provide any guidance on how to make it work with a class in another assembly/library that is not a controller.
For ONE example, if I have a custom attribute that I can decorate a class with and that custom attribute is defined in another library (not in a web project) and it needs to access a configuration setting, how do I do that today? I can't pass in anything to a constructor in such an instance either.
I'm going to assume you're talking about a ASPNET Core project as you specifically mention web.config.
Here's what you need to do.
IOptions<FooSettingsClass> is usually configured at application start which means that it's available at runtime with code that looks something like this.
// Adds services required for using options.
services.AddOptions();
services.Configure<AppSettings>(Configuration.GetSection("FooAppSettings"));
The easiest way is to have the framework inject it through the constructor. Typically you'll see it (as you mentioned) being injected in the controller like this:
class FooController : Controller {
public FooController(IOptions<FooSettingsClass> settings) { .
//..
}
}
If you need to access this configuration is say, a service, then you simply have a constructor, in a different assembly, which accepts those options. So:
public class SomeServiceInAnotherAssembly {
public SomeServiceInAnotherAssembly(IOptions<FooSettingsClass> settings) {
//..
}
}
This obviously means that your FooSettingsClass class needs to be outside of your ASPNET Core project (to avoid circular dependencies), but this is one way of propagating your configuration without writing any code, which is what I've seen other developers do. To me, writing code is a hoop jumping solution bound to have bugs.
Don't forget that your class (in this exampe SomeServiceInAnotherAssembly) needs to be registered at startup, i.e. services.AddScoped<SomeServiceInAnotherAssembly>();
The nice thing about this approach is that it makes your classes testable.
In 8-2017 Microsoft came out with System.Configuration.Manager for .NET CORE v4.4. Currently v4.5 and v4.6 preview.
Install this nuget package. Add directive to a code file
using System.Configuration;
Now, you can do your
var val = ConfigurationManager.AppSettings["mysetting"];
There is one trick for web sites though - you no longer use web.config for application settings and configuration sections. You use app.config as well as other types of projects. But if you deploy in ISS, you might need to use both. In web.config you supply strictly ISS-related entries. Your app-specific entries go to app.config
I understand C# Code Fragments and .NET Assemblies offer the same functionality for modular template development. We manage the code fragments in the CME and assembly code in Visual Studio, but use both the same way in Template Builder.
In terms of code, I can create a C# Code Fragment Template Building Block (TBB), for example:
var timeStamp = DateTime.Now.ToString("d MMM yyyy");
package.PushItem("timeStamp from fragment", package.CreateHtmlItem(timeStamp));
I can also create a .NET assembly Template Building Block using the same code by implementing ITemplate as below.
using System;
using Tridion.ContentManager.Templating;
using Tridion.ContentManager.Templating.Assembly;
namespace CreateAndBreakTemplates
{
[TcmTemplateTitle("Add Date to Package")]
public class AddDateToPackage : ITemplate
{
public void Transform(Engine engine, Package package)
{
var timeStamp = DateTime.Now.ToString("d MMM yyyy");
package.PushItem("timeStamp from assembly",
package.CreateHtmlItem(timeStamp));
}
}
}
The docs explain that "SDL Tridion inserts the code fragment in its predefined method of a predefined class." It looks like this class implements ITemplate and adds some references below (am I missing anything?).
The assembly setup instructions mention at least these dlls.
Tridion.Common.dll
Tridion.ContentManager.dll
Tridion.ContentManager.Templating.dll
Tridion.ContentManager.Publishing.dll
Any other difference between fragment and assembly and how would you choose between the two?
A C# fragment is compiled into an assembly by Tridion when the template is first invoked and after it's been modified. To compile the fragment, Tridion wraps it in some "dungeon dressing" (bonus points for those who know where that term comes from) that:
Uses the Tridion.ContentManager, Tridion.ContentManager.CommunicationManagement, Tridion.ContentManager.ContentManagement and Tridion.ContentManager.Templating namespaces
Makes the Package and Engine available in fields called package and engine respectively
Creates a logger for the C# fragment that is available through a field called log
Adds references to some commonly used assemblies (but does not add a using for their namespaces yet)
Edit: given the other answers it seems many people are not aware of how to accomplish certain tasks in C# fragment TBBs, so I'll document them below:
Import additional namespaces
To import/use additional namespaces into your C# fragment, you need to use this syntax:
<%# Import Namespace="Tridion.ContentManager.ContentManagement.Fields" %>
Note that this will only import namespaces from assemblies that are already referenced by Tridion. There is no mechanism for you to add references to other assemblies explicitly; so if you need a third-party DLL, you will need to add it to the GAC.
Defining custom functions
You can define custom fields and functions in your C# fragment by using this syntax:
<%!
public static string GetDate()
{
return new DateTime().ToString("u").Replace(" ", "T");
}
%>
Defining member fields and (nested) classes
The syntax for defining custom functions also allows you to define nested classes and/or member fields:
<%!
public class MyLittleHelper
{
public MyLittleHelper(string param1)
{
}
}
%>
Frank has explained the difference between the two approaches, but that still leaves the question of how to choose between the two. My personal advise is to never use C# fragments for anything, with only one exception*. As you have found out, there is some dark magic going on in them that I personally do not like. Also, there is so much you cannot do in them that a .NET programmer is quite fond of, such as creating classes.
Putting my personal taste aside, I see only one reason why you would ever resort to C# fragments: if you do not have access to Visual Studio or another tool that builds DLLs. And that is not a very strong argument either: if you want a job done, you should get the proper tools!
*The exception being the C# fragments that Tridion automatically creates for each ITemplate in your assembly, of course.
The main differences between C# code Fragment and .net Assemblies in my point of view are categorized into below high level buckets.
Step-by-Step Debugging
With .net assemblies you could do step-by-step debugging from visual studio where as C# Code fragments it is not possible.
Re-Use or Base Classes
With .net assemblies you could extend ITemplate to create something like BaseTemplate and all your template could extend them so you have common design pattern, where as C# there is no concept of BaseTemplate other than Tridion ITemplate interface.
With .net assemblies you could add common utility classes (often TridionUtilities) and all your templates refer to the same TridionUtilities for common functionality. C# code fragment the utility functions need to be defined within the same TBB and cannot be reused with other TBBs unless you create a class and deploy to GAC.
Easier Upgrade Scans and Maintenance
With .net assemblies it is easier to do any upgrade scans like deprecated APIs/Methods simply referring to new dlls/.net framework. .net assemblies make it easy to identify potential impacts on planning either Tridion upgrades or .net framework upgrades. C# code fragments it is much harder to find the deprecated or any impacts of upgrade.
Developer Friendly
Obviously .net assemblies are developed using Visual Studio (developers love it!) vs. C# Code Fragments in a Text Editor (painful).
When I started back with Tridion 5.3, started with C# code fragments and quickly realized what a mistake I made for not going .net assemblies.
My vote is always .net assemblies and C# code fragments is not even in consideration unless I don't have a choice. lol..
I think the differences indeed are best explained by Frank's answer, as to how would you choose between the two. I normally say, since you are using Visual Studio anyways, always create a .NET Assembly TBB for your code. They offer you a lot more benefits like including 3rd party assemblies, allow for proper coding with classes and methods a lot easier and probably most important, allow for proper debugging (although this last one can be hard to setup depending on where you are, thinking of customer environments, firewalls etc.).
There are for me only two exceptions for using C# Fragments:
The references to classes implementing ITemplate in an assembly, allowing you to use these as separate TBBs
If there is a requirement to manage constants or other hardcoded constants directly from SDL Tridion
Number 2 is of course debatable, but you never can do without configuration properties, for a TBB most of these you can handle using a Parameters Schema, but sometimes it is just a lot easier, to directly write them in a C# Fragment and have that push them to the package for other TBBs to use.
In my training sessions, I usually referred to the following story of the only time I ever choose to use a C# Fragment TBB so far, indicating how much of an exception it is to use them:
I was working at a customer abroad, and my taxi for the airport was leaving in 10 minutes when one of the developers I was coaching asked me a question on how to get a list of items from a Folder in his TBB. I had already closed my Visual Studio and Outlook and was about to shutdown my laptop, but quickly browsed through some of my code samples to find what he needed. Knowing that starting up Visual Studio or Outlook would take a few minutes, I quickly pasted the code in a C# Fragment so he had it for easy reference.
I would never use C# fragments for the sole reason that it makes management of your code quite difficult and you need to manually deploy them. And if you do write your code from Visual Studio, then you should create a .NET Building Block assembly.
I have a couple of web applications whose source code is missing. The project is compiled to a dll and is hosted on a IIS.
I have couple of questions to make.
What is the best way to recreate the
project from the dll file??
We are planning to change the
database server, and the database
connection strings are specified in
the Global.asax ( I mean the public
class Global : HttpApplication ). Is
there a way I can subclass this
Global and override the connection
strings? If yes, how can I make the
IIS refer to the new dll
Thank you all for any suggestions!!
For first part, use decompiler tools such as Reflector/ILSpy/dotPeek to convert IL code from DLL to higher level language such as C#. However, tools cannot get back comments, local variable names and project structure. You need to manually organize the code into files and project structures. From aspx files, you have to figure out the code-behind classes and then link up the source of the class into a correct named code-behind file - for example, if default.aspx says that it inherits from MyApp.Default then create file default.aspx.xs and put the source code for the class into that.
For second part, you can create a new class derived from Global and modify Global.asax to use that class - you need to put the assembly containing new class in bin folder and overwrite inherits clause in asax file to point to the type name of new class. You anyway need to inspect the code of your current Global class (using tools sighted above) to see if you can override connection strings by sub-classing.
Probably the best tool available to reverse engineer a dll into code is .NET Reflector. Unfortunately, the latest version is no longer free, but it is worth the money.
I am not sure exactly if this still applies if you can already reverse engineer your source code. However, I would recommend moving your connection strings outside of your project into web.config as a best practice. This way you can make the change in the future without changing any code.
In my asp.net project currently i have business logic and and data access code in two sub folders(BLL,DAL) which are itself located web site project's app_code folder. I need to segregate them to two separate projects(one project for business layer and one project for Data access code).
How can I maintain connection strings necessary to Data access project which are currently in web.config file?(i.e if I choose Class library template for creating DAL and BLL projects)
How can I maintain various other web.config key values that are currently used in BLL, DAL code files?
How can I deploy compiled project? (ie Web site project I am currently deploying bin folder to Staging> production but this way where should i put DAL.dll and BLL.dll and relevant config files)
1 and 2) Add a 'using System.Configuration' and just reference them. Since their referenced in the project, asp.net will pick it up.
For example:
using System.Configuration;
namespace DataLayer
{
public class BaseDataAccess
{
public static string ConnectionString_Logging
{
get
{
return ConfigurationManager.ConnectionStrings["ConnectionString_Logging_Legacy"].ToString();
}
}
}
}
3) If properly referenced, upon compile, your BLL and DAL dlls will be in your bin folder of the main/ui project. If using web.config, your good to go.
Fundamentally, you should be wrapping those configuration bits up in objects along the way. But in any case, you can move them to a different class project without worry here -- it will pick up the configuration settings from whatever project it is hosted by, so you don't need to somehow provide the configuration to your library.
Your existing code should work, as the settings are read from the Config file of the running process, in this case your Web.Config, however i suggest you use custom configuration settings, these would be read from your Web.Config file, a typical implementation could look something like :
<YourCompany>
<YourCompany.ProjectName>
<Data ConnectionName="NameOfConnectionToUse" SomethingElse="XZY" />
<Business SomeValue="12345" />
</YourCompany.ProjectName>
</YourCompany>
Without getting into ideal settings/custom config etc, as asked - during runtime, your class libraries will get the configuration from the web.config if referenced as such from within these layers with no change. System.Configuration.AppSettings/ConnectionStrings will still work.
I have a large directory structure with JavaScript, images, etc. that depend on each other. I would like to encapsulate it all into a DLL so I only have to reference one thing and not have multiple copies of all these files across projects.
Because the files depend on each other, I'm thinking I can create an IHttpModule that registers a route to accept URLs such as /MyEmbeddedDir/subdir/file.js. Anything in MyEmbeddedDir would then be handled by a custom IHttpHandler that does the correct mapping. Each web application would then need to reference the DLL and add the module and handler to web.config. Does this seem reasonable?
Also, is there an easier way to embed/reference the files than to set the build action to embedded resource and add [assembly: WebResource(...)] to each file (there are dozens!)? Thanks!
Edit: If I'm not using WebResource.axd then I shouldn't need to add [assembly: WebResource(...)]
Yes, having a single container is a great way to manage large number of files (and no, SQLite won't help here! ;).
We have a product, named SolFS, which is a virtual file system, that lets you keep your data in custom storage (resource DLL is one of the options) and provides file API for accessing the files. We even implemented asynchronous pluggable protocol for IE (on the client side, but the task is very similar to yours). SolFS includes a manager application that lets you easily create container files and import files into container.
I ended up going with the IHTTPModule (register route) and IHTTPHandler (obtain embedded resource). The route is configurable in web.config in case it conflicts with existing content.