In my application, I have some reports which needs to be viewed frequently.After viewing the reports many times by different users, it shows load error.For different systems, many temporary files are created.i need to delete those files in my single system.now i manually deleting all the temporary files in the temp directory and configure the IIS again.then the report loads properly.But we need to delete these temporary files frequently which makes our life dreadful.Only the report files needs to be deleted.How can i delete these temporary files automatically using code?
I have used the following code for this.but some files cant be deleted as those files are in use.Do those temporary files in other system can cause load error in our system?how can i solve this?
dim temp as string=Environment.GetEnvironmentVariable("TEMP")
dim k as sting()=System.IO.Directory.GetFiles(temp)
dim i as integer
For i=0 to k.length
On Error Resume Next
If k(i).Contains(".rpt") then
kill(k(i))
System.IO.File.Delete(k(i))
Next
Create a thread from the Application_Start() (or write a standalone exe)
You can just automate what you are manually doing.
You can delete these files with a older modifier day, once an hour, with a very simple program .
Assuming you are presenting these temporary files to the user can I suggest creating an HTTP handler. The handler will provide the ability to generate a file and deliver it to the user to be either downloaded or viewed in browser. This approach allows for the customization of caching.
The example below is only showing the handler portion, this is as basic as it gets as doesn't go into the file creation as I am not sure how you are creating the files currently. You can send a stream of some sort.
Example: (Sorry in C#, but you can go from here.)
using System;
using System.Data;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.IO;
namespace Handlers
{
/// <summary>
/// Summary description for $codebehindclassname$
/// </summary>
[WebService(Namespace = "http://www.tempuri.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class ColorImage : IHttpHandler
{
public bool IsReusable { get { return true; } }
public void ProcessRequest(HttpContext context)
{
Bitmap bmGenerate = CreateBitmapMethod();
context.Response.ContentType = "image/png";
context.Response.AddHeader("Response-Type", "image/png");
using (MemoryStream memoryStream = new MemoryStream())
{
bmGenerate.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
memoryStream.WriteTo(context.Response.OutputStream);
}
}
}
}
Related
Good Day Everyone,
I know, the versions I am talking about are rather obsolete by now but that's the tools I am stuck to work with at the workplace. This is my first question on StackOverflow, and I hope I will get the formatting right hehe ;-) Please pardon me for the long text, I am used to give a lot of details and, in a sense, I feel the more details I provide the more accurate might be the answers ;-)
In almost 10 years working in IT, I've always been able to find answers to my questions (i.e. solutions to my problems) by Googling well chosen keywords and expressions. Well, it looks like the aforementioned Sync Framework is either not very well known to the Internet community, or it is a real pain to try to understand its simplest concepts for most mortals. After extensive research I have to found a single, simple example of syncing SQL Express using Sync Framework 1.0 and the C# language, not even on MSDN! I am fairly new to ASP.NET / C# but I understand the concepts and I have a working web application that successfully stores and retrieve data from an SQL Server 2008 database. It has been in use by clients for two years now. We now have a requirement for the clients to be able to bring their data offline and be able to update it offline and then sync with the server. UPDATEs, INSERTs and DELETEs will occur at both ends.
What I am trying to find is VERY simple (or so I thought): C# code examples that uses SQL Server Change Tracking information (NOT custom Change Tracking) to sync the server (SQL Server 2008) and client computers (SQL Server 2008 Express, NOT Compact Edition). The simplest case would be a single table with few columns. I am fairly confident to understand the SQL Server part and I've prepared both sides of the database to receive sync requests from the client web application (enabled Change Tracking, PrimaryKeyID has data type GUID, the application's user account on the server has VIEW_CHANGE_TRACKING permission, etc. etc.)
I know it's the web application that serves as the interface between the two and that manage the sync session (in C#). I was quite naive to think that the only thing left to do was to provide the two connection strings, tell what tables are to sync and specify a bidirectional sync. Apparently, that's more complicated than that hehe. In a desperate attempt, I have tried to base my code on the following from Microsoft and adapt it to SQL Express (the example is for Compact). I am close to admit defeat and shamefully lower my head :-(
http://msdn.microsoft.com/en-us/library/bb726015%28v=sql.100%29.aspx
Based on the above (second section "Complete Example Using SQL Server Change Tracking"), I removed everything I do not need: things related to the password, the statistics, and the code applying changes to the data. I've also removed MS' numerous comment lines for clarity. I've manually applied changes at the SQL Server itself at both ends executing scripts in SSMS (and therefore there must be Change Tracking info that was generated and that is usable when the web app will request the sync). QUESTION 1: Am I wrong in saying so? Finally, I changed some stuff in an attempt to use objects relevant to SQL Express instead of Compact.
QUESTION 2: The code at Microsoft is apparently able to tell whether it is the initial (first) or subsequent sync of this replica. I don't have a clue how it can!
In the end, the code left in its simplest form is as below (with QUESTIONS 3, 4, 5 ;-), but shows some errors. I thank you VERY MUCH in advance for your help. Any comments and/or suggestions are welcome. I am sure if/when this is resolved it will benefit quite a lot of people out there. I will continue to research on it on my end (the boss won't give me a choice ;-) and I promise I will post the solution here if I ever succeed in syncing!
Thanks and have a great day everyone!
Kindest Regards,
Zyxy
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.IO;
//using System.Data.SqlServerCe;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.Server;
//using Microsoft.Synchronization.Data.SqlServerCe;
namespace some_namespace
{
public class SyncProgram
{
public SyncProgram()
{
// empty constructor
}
public static bool MainSync() // Entry point, say, called by a Sync button on an ASPX page.
{
bool boolSyncRes = false; // tells whether sync was a success or not
// Initial sync: they create a new instance of the Orchestrator.
ZyxySyncOrchestrator zyxySyncOrchestrator = new ZyxySyncOrchestrator();
// Subsequent synchronization.
// They don't. there was only irrelevant stats stuff here.
boolSyncRes = true;
return boolSyncRes;
}
}
public class ZyxySyncOrchestrator : SyncOrchestrator
{
public ZyxySyncOrchestrator()
{
Utility util = new Utility();
this.LocalProvider = new ZyxyServerSyncProvider(); // QUESTION 3: ??? cannot implicitly convert type DbServerSyncProvider to Microsoft.Synchronization.SyncProvider
//Instantiate a server synchronization provider and specify it
//as the remote provider for this synchronization agent.
this.RemoteProvider = new ZyxyServerSyncProvider(); // cannot implicitly convert type DbServerSyncProvider to Microsoft.Synchronization.SyncProvider
// QUESTION 4: Is the following code actually creating the base (user) table ZyxySync
// (as opposed to its change tracking metadata table)??
// I wasn't sure whether this part of the code on Microsoft's webpage was part of
// populating the db with sample data and structure or if it's really meant to deal with
// the change tracking metadata.
SyncTable zyxySyncTable = new SyncTable("ZyxySync");
zyxySyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
zyxySyncTable.SyncDirection = SyncDirection.DownloadOnly;
this.Configuration.SyncTables.Add(zyxySyncTable);
}
}
//Create a class that is derived from Microsoft.Synchronization.Server.DbServerSyncProvider.
public class ZyxyServerSyncProvider : DbServerSyncProvider
{
public ZyxyServerSyncProvider()
{
Utility util = new Utility();
SqlConnection serverConn = new SqlConnection(util.ServerConnString);
this.Connection = serverConn;
//Retrieve a new anchor value from the server. We use a timestamp value
//that is retrieved and stored in the client database.
//During each sync the new and last anchor values are used to determine the set of changes
SqlCommand selectNewAnchorCommand = new SqlCommand();
string newAnchorVariable = "#" + SyncSession.SyncNewReceivedAnchor;
selectNewAnchorCommand.CommandText =
"SELECT " + newAnchorVariable + " = change_tracking_current_version()";
selectNewAnchorCommand.Parameters.Add(newAnchorVariable, SqlDbType.BigInt);
selectNewAnchorCommand.Parameters[newAnchorVariable].Direction = ParameterDirection.Output;
selectNewAnchorCommand.Connection = serverConn;
this.SelectNewAnchorCommand = selectNewAnchorCommand;
//Create a SyncAdapter for the ZyxySync table by using
//the SqlSyncAdapterBuilder.
// Specify a name for the SyncAdapter that matches the
// the name specified for the corresponding SyncTable.
SqlSyncAdapterBuilder zyxyBuilder = new SqlSyncAdapterBuilder(serverConn);
zyxyBuilder.TableName = "dbo.ZyxySync";
zyxyBuilder.ChangeTrackingType = ChangeTrackingType.SqlServerChangeTracking;
SyncAdapter zyxySyncAdapter = zyxyBuilder.ToSyncAdapter();
zyxySyncAdapter.TableName = "ZyxySync";
this.SyncAdapters.Add(zyxySyncAdapter);
}
}
// Class derived from Microsoft.Synchronization.Data.Server.DbServerSyncProvider
// QUESTION 5: Or should have I used the two below? I believe they only apply to SQL Compact...
//Microsoft.Synchronization.Data.ClientSyncProvider
//Microsoft.Synchronization.Data.ServerSyncProvider
//http://msdn.microsoft.com/en-us/library/microsoft.synchronization.data.clientsyncprovider%28v=sql.100%29.aspx
//http://msdn.microsoft.com/en-us/library/microsoft.synchronization.data.server.dbserversyncprovider%28d=printer,v=sql.100%29.aspx
public class ZyxyClientSyncProvider : DbServerSyncProvider
{
public ZyxyClientSyncProvider()
{
Utility util = new Utility();
SqlConnection clientConn = new SqlConnection(util.ClientConnString);
this.Connection = clientConn;
}
}
public class Utility
{
public string ClientConnString
{
get { return #"Data Source=localhost\LocalExpressInstance;Initial Catalog=DatabaseName;User ID=UserName;Password=WontTellYou;"; }
}
public string ServerConnString
{
get { return #" Data Source=ServerName\ServerInstance;Initial Catalog=DatabaseName;User ID=UserName;Password=WontTellYou;"; }
}
}
}
the SyncOrchestrator will not work with the DBServerSyncProvider.
In Sync Framework, there are two types of database providers: the offline provider and the peer-to-peer/collaboration providers. (they both work in offline scenario so thats confusing).
the offline provider is used in hub-spoke topologies. Only the client tracks what was synched. The server doesnt even know its part of a sync. this is the same provider used by the Local Database Cache Project item in Visual Studio. The only supported databases out of the box is SqlCeClientSyncProvider and DBServerSyncProvider and uses the SyncAgent to synchronize.
the peer-to-peer provider can be used in a peer-to-peer sync as well as hub-spoke scenarios. Each peer maintains metadata on what was synched. This uses the much newer SyncOrchestrator/SqlCeSyncProvider/SqlSyncProvider(works with SQL Server, Express, LocalDB, and SQL Azure). This uses a custom change tracking.
you can't interchange the providers used by SyncAgent and SyncOrchestrator. You can'r reuse the SQL commands either because they differ in the way they track, select, apply changes and record what was synched.
Ok I managed to make it work so here is a SIMPLE code sample that works (in my case anyway). In addition to the above steps (enabling Change Tracking, setting the right user permissions, etc.), what I did not understand is the following:
1) I found out that I can set it up so that the Sync Framework and sync session is all managed on the client side. Without any dependency on what's installed on the server, I was able to use SF 2.1 instead of the old 1.0. That helped a lot.
2) In preparation for the sync session, one must first PROVISION the database so that it is ready for sync. What I did is to run the following C# with the client connection string (so that it provisions the client db) and then run it again with the server connection string (so that it provisions the server db). This is a run once program (on both sides) to prepare the db. You do NOT run it for every sync session you establish.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Text.RegularExpressions;
using System.Security.Principal;
using System.IO;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.Server;
using Microsoft.Synchronization.Data.SqlServer; // SF 2.1
using Microsoft.Synchronization.SimpleProviders; // SF 2.1
using Microsoft.Synchronization.MetadataStorage; // SF 2.1
// ZYXY: Based on:
// http://msdn.microsoft.com/en-us/library/ff928603.aspx
// NOTES:
// - Microsoft Sync Framework 2.1 redistributable package must be installed on Client computers but is not required on the Server, as long as a server-side synchronization setup is performed by a client computer.
// This is a run once program.
namespace DISS_Database_Sync_Provisioning_Console
{
class Program
{
static void Main(string[] args)
{
SqlConnection sqlConn = new SqlConnection("Data Source=ServerName\\InstanceName;Initial Catalog=SomeDatabase;User ID=SOmeUser;Password=SomePassword;");
Console.Write("Provisioning database...");
// define a new scope named DISS_Sync_Scope
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("DISS_Sync_Scope");
// get the description of the ZyxySync table
DbSyncTableDescription tableDesc = SqlSyncDescriptionBuilder.GetDescriptionForTable("dbo.ZyxySync", sqlConn);
// add the table description to the sync scope definition
scopeDesc.Tables.Add(tableDesc);
// create a server scope provisioning object based on the DISS_Sync_Scope
SqlSyncScopeProvisioning sqlProvision = new SqlSyncScopeProvisioning(sqlConn, scopeDesc);
// skipping the creation of base table since table already exists
sqlProvision.SetCreateTableDefault(DbSyncCreationOption.Skip);
// start the provisioning process
sqlProvision.Apply();
sqlConn.Close();
sqlConn.Dispose();
Console.Write("\nDatabase has been successfully configured for synchronization. Please press any key to exit.");
Console.Read();
}
}
}
3) Below is the code that is run everytime the synchronization is launched (e.g. when the user click their "Synchronize" button in their web application.)
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Security.Principal;
using System.IO;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.Server;
using Microsoft.Synchronization.Data.SqlServer; // SF 2.1
using Microsoft.Synchronization.SimpleProviders; // SF 2.1
using Microsoft.Synchronization.MetadataStorage; // SF 2.1
namespace diss_ssmb
{
public class SyncProgram
{
public SyncProgram()
{
// empty constructor
}
public static bool MainSync() // Entry point, say, called by a Sync button on an ASPX page.
{
bool boolSyncRes = false; // tells whether sync was a success or not
// Initial sync: they create a new instance of the Orchestrator.
ZyxySyncOrchestrator zyxySyncOrchestrator = new ZyxySyncOrchestrator();
// Subsequent synchronization.
// They don't. there was only irrelevant stats stuff here.
boolSyncRes = true;
return boolSyncRes;
}
}
public class ZyxySyncOrchestrator : SyncOrchestrator
{
public ZyxySyncOrchestrator()
{
Utility util = new Utility();
this.LocalProvider = new ZyxyClientSyncProvider();
//Instantiate a server synchronization provider and specify it
//as the remote provider for this synchronization agent.
this.RemoteProvider = new ZyxyServerSyncProvider();
SyncTable zyxySyncTable = new SyncTable("ZyxySync");
zyxySyncTable.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
zyxySyncTable.SyncDirection = SyncDirection.Bidirectional;
// this.Configuration.SyncTables.Add(zyxySyncTable);
this.Synchronize();
}
}
public class ZyxyServerSyncProvider : SqlSyncProvider
{
public ZyxyServerSyncProvider()
{
Utility util = new Utility();
SqlConnection serverConn = new SqlConnection(util.ServerConnString);
this.Connection = serverConn;
this.ScopeName = "DISS_Sync_Scope";
//Retrieve a new anchor value from the server. We use a timestamp value
//that is retrieved and stored in the client database.
//During each sync the new and last anchor values are used to determine the set of changes
SqlCommand selectNewAnchorCommand = new SqlCommand();
string newAnchorVariable = "#" + SyncSession.SyncNewReceivedAnchor;
selectNewAnchorCommand.CommandText =
"SELECT " + newAnchorVariable + " = change_tracking_current_version()";
selectNewAnchorCommand.Parameters.Add(newAnchorVariable, SqlDbType.BigInt);
selectNewAnchorCommand.Parameters[newAnchorVariable].Direction = ParameterDirection.Output;
selectNewAnchorCommand.Connection = serverConn;
// this.SelectNewAnchorCommand = selectNewAnchorCommand; // SF 2.1 commented out because SelectNewAnchorCommand isn't there.
SqlSyncAdapterBuilder zyxyBuilder = new SqlSyncAdapterBuilder(serverConn);
zyxyBuilder.TableName = "dbo.ZyxySync";
zyxyBuilder.ChangeTrackingType = ChangeTrackingType.SqlServerChangeTracking;
SyncAdapter zyxySyncAdapter = zyxyBuilder.ToSyncAdapter();
zyxySyncAdapter.TableName = "ZyxySync";
// this.SyncAdapters.Add(zyxySyncAdapter); // SF 2.1 commented out because SelectNewAnchorCommand isn't there.
}
}
public class ZyxyClientSyncProvider : SqlSyncProvider
{
public ZyxyClientSyncProvider()
{
Utility util = new Utility();
SqlConnection clientConn = new SqlConnection(util.ClientConnString);
this.Connection = clientConn;
this.ScopeName = "DISS_Sync_Scope";
}
}
public class Utility
{
public string ClientConnString
{
get { return #"Some connection string such as in the above code sample"; }
}
public string ServerConnString
{
get { return #"Some serverconnection string such as in the above code sample"; }
}
}
}
4) The above successfully synched bidirectionally when INSERTs, UPDATEs and DELETEs occurred simultaneously at both ends between two consecutive sync session, HOWEVER, when no conflict had to be resolved (e.g. when the same record is updated at both ends). I yet have to do further testing in cases when there are conflicts that have to be resolved. How is the Sync Framework resolving such conflicts by default? I assume we can adjust these settings to tell it to establish the winner based either on...
- a timestamp value
- the replicaID
- the user role
- the transaction type
- ...
Anyways I truly hope that helps somebody, because I had a real hard time to figure it out from the web! Good luck!
Zyxy
I have this function and it works fine to get a translated value from this specific resource file called OkayMessages.
public static string GetResourceString(string resourceKey){
ResourceManager resourceManager = Resources.OkayMessages.ResourceManager;
return resourceManager.GetString(resourceKey);
}
But i have more than 1 resource file and i want this function to get values from those files as well.. Only, i'm having trouble with dynamically/programmatically selecting the right resource(manager).
I have tried to use the code below, and some variants to that, but i always get an error.
public static string GetResourceString(string resourceFile, string resourceKey){
ResourceManager resourceManager = new System.Resources.ResourceManager("Resources." + resourceFile, Assembly.GetExecutingAssembly());
return resourceManager.GetString(resourceKey);
}
The error i got most of the times was: Could not find any resources appropriate for the specified culture or the neutral culture. Make sure "Resources.OkayMessages.resources" was correctly embedded or linked into assembly..
Update: I'm using the \App_GlobalResources\ folder for my resource files, and it seems that this is the problem. When i place a resource file in the root of my project, i can initialize a ResourceManager without problems.
After searching in the wrong direction for a while, I just found the most simple answer to this problem. It turns out that there is a method called GetGlobalResourceObject.
So in my case I'm now using this line of code which does all:
GetGlobalResourceObject("OkayMessages", "PasswordChanged").ToString();
Read carefully this article and you'll find that you need to specify correct namespace of the resource. That's your problem. Here is working example if OkayResources.resx resides in project root folder:
using System.Reflection;
using System.Resources;
using System.Web.UI;
namespace WebApplication1
{
public partial class _Default : Page
{
public _Default()
{
var result = GetResourceString("OkayResources", "SomeKey");
}
private static string GetResourceString(string resourceFileName, string key)
{
var resourceName = "WebApplication1." + resourceFileName;
var resourceManager = new ResourceManager(resourceName, Assembly.GetExecutingAssembly());
return resourceManager.GetString(key);
}
}
}
If you put your resource file into Resources folder you'll have to update resource namespace:
var resourceName = "WebApplication1.Resources." + resourceFileName;
In my web app, I have an aspx page which contains an html table and several lines of text. I need users to be able to download this whole page as a separate file.
In the past I have used the a webclient to do this:
Dim myWebClient As New System.Net.WebClient
myWebClient.DownloadFile(strSource, strDest)
Response.AddHeader("Content-Disposition", "attachment;filename=test.doc")
Response.Write("test.doc")
but it appears this is only able to download html pages.
Can this be done?
The reason you can only download HTML is because .aspx is not served over the internet (only the resulting HTML is).
When you are doing the myWebClient.DownloadFile() the application is simply making a GET request to the URI and saving the resulting HTML. The .aspx never leaves the server - rather it is processed server side resulting in the HTML you are ending up with.
.ASPX is a scripted page, so you can't actually download the original ASPX. The IIS server on the other end has a handler for .aspx resulting in .NET Processing it. Generally, you don't want a server returning raw ASPX source.
It would require special handling on the server side to be able to get a raw ASPX page. For instance, you could create an ASHX script handler that does it for you, you'd request something like getfile.ashx?filename=myfile.aspx and the getfile.ashx handler would read the ASPX page off the disk and write that as the response. (Security note: If this is the route you choose, make sure to sanitize the page that is specified so they don't do something silly like getfile.ashx?filename=C:\my\secret\file.txt) It would be even better to set the trust level of that handler to medium or lower.
But that all requires serverside development. From the client side, there isn't anything you can do until the server wants to play along.
Here is an example of a file handler:
public class MyHandler : IHttpHandler
{
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
var file = context.Request.QueryString["file"];
//Make sure to do your security checks on the file path here.
using (var source = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
{
source.CopyTo(context.Response.OutputStream);
}
}
}
You can setup the handler either inside of an ASHX or through the httpHandlers section in the web.config.
Or if you are using MVC2+, you don't need an HTTP Handler as you can just use an action to achieve the same thing:
public ActionResult GetFile(string path)
{
//Make sure to do your security checks
using (var source = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
return File(source, "text/html");
}
}
I want to override a string from a System.ComponentModel.DataAnnotations for an ASP.NET project. Do I need to make a satellite assembly, messing with custom build tasks, al.exe etc.? Even if yes, I couldn't find how to convert .resx to .resources to feed it to al.exe. And if no, where to put the .resx. and how to name it?
UPD: To make it clear: I wanted to use a custom resource string instead of one from the default resource from the assembly. I didn't want to make changes in the every place that uses that string. After all, the resources exist just for overriding them.
Phil Haack has an excellent article Localizing ASP.Net MVC Validation which specifically guides you through overriding your strings. This article applies more to DataAnnotations than it does ASP.net MVC. So, this will help however your are using DataAnnotattions.
Below I have listed the simplest steps to add Localized Resources in Visual Studio.
Open the Project Properties dialog.
Select the Resources tab.
Click to create a new default
resources file.
This will create two files in your Properties folder.
Resources.resx
Resources.Designer.cs
When Resources.resx has
opened, change it's Access Modifier
to Public.
Add your strings.
To add additional resource files for specific cultures you will need to.
Right click your Project in the
Solution Explorer.
Select Add -> New Item -> Resource
File.
Name it Resources.en-us.resx.
(replace 'en-us' with appropriate
code)
Click Add
Drag it into the Properties folder.
Open Resources.en-us.resx and change it's Access Modifier
to Public.
Add your strings.
Repeat for each Culture you need to
support.
During the build VS will convert the .resx files to .resource files and build wrapper classes for you. You can then access via the namespace YourAssembly.Properties.Resources.
With this using statement.
using YourAssembly.Properties;
You can decorate with attributes like this:
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = "MyStringName")]
Note: I used the Properties folder for consistency. To use the App_GlobalResources move your .resx files there and change your using statement to match the directory name. Like this:
using YourAssembly.App_GlobalResources;
Edit: The closest that you can get to Strongly Typed resource names would be to do something like this:
public class ResourceNames
{
public const string EmailRequired = "EmailRequired";
}
You can then decorate with attributes like this.
[Required(ErrorMessageResourceType = typeof(Resources), ErrorMessageResourceName = ResourceNames.EmailRequired)]
To enable automatic client culture detection add the globalizationsection to the web.config file.
<configuration>
<system.web>
<globalization enableClientBasedCulture="true" culture="auto:en-us" uiCulture="auto:en-us"/>
</system.web>
<configuration>
Here I have enabled a client based culture and set the culture and the uiculture to "auto" with a default of "en-us".
Creating Separate Satellite Assemblies:
The MSDN Creating Satellite Assemblies article will help as well.
If you are new to satellite assemblies make sure you read Packaging and Deploying Resources.
When creating satellite assemblies in the past, I have found it useful to use VS build events. These are the steps I would take.
Create a separate Class Library project in my solution.
Create or Add my .resx files to this project.
Add a Post-Build Event to the Project Properties dialog. (Like the one below)
Sample VS Post-Build Script:
set RESGEN="C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\resgen.exe"
set LINKER="C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\al.exe"
set ASSEMBLY=$(TargetName)
set SOURCEDIR=$(ProjectDir)
Set OUTDIR=$(TargetDir)
REM Build Default Culture Resources (en)
%RESGEN% %SOURCEDIR%en\%ASSEMBLY%.en.resx %SOURCEDIR%en\%ASSEMBLY%.resources
REM Embed Default Culture
%LINKER% /t:lib /embed:%SOURCEDIR%en\%ASSEMBLY%.resources /culture:en /out:%OUTDIR%%ASSEMBLY%.resources.dll
REM Embed English Culture
IF NOT EXIST %OUTDIR%en\ MKDIR $%OUTDIR%en\
%LINKER% /t:lib /embed:%SOURCEDIR%en\%ASSEMBLY%.resources /culture:en /out:%OUTDIR%en\%ASSEMBLY%.resources.dll
REM These are just a byproduct of using the project build event to run the resource build script
IF EXIST %OUTDIR%%ASSEMBLY%.dll DEL %OUTDIR%%ASSEMBLY%.dll
IF EXIST %OUTDIR%%ASSEMBLY%.pdb DEL %OUTDIR%%ASSEMBLY%.pdb
If you would prefer not to use ResGen.exe to convert your .resx files, you could do something like this.
using System;
using System.Collections;
using System.IO;
using System.Resources;
namespace ResXConverter
{
public class ResxToResource
{
public void Convert(string resxPath, string resourcePath)
{
using (ResXResourceReader resxReader = new ResXResourceReader(resxPath))
using (IResourceWriter resWriter = new ResourceWriter(
new FileStream(resourcePath, FileMode.Create, FileAccess.Write)))
{
foreach (DictionaryEntry entry in resxReader)
{
resWriter.AddResource(entry.Key.ToString(), entry.Value);
}
resWriter.Generate();
resWriter.Close();
}
}
}
}
One of the potential draw backs to doing the conversion this way is the need to reference the System.Windows.Forms.dll. You will still need to use Assembly Linker.
Edit: As wRAR has reminded us if you are signing your assemblies your keys must match.
While this is strange, especially for people familiar with open source localization technologies, one cannot build a satellite assembly for any system assembly or even a 3rd-party signed one:
If your main assembly uses strong
naming, satellite assemblies must be
signed with the same private key as
the main assembly. If the
public/private key pair does not match
between the main and satellite
assemblies, your resources will not be
loaded.
Whether the same is possible automatically, but without a satellite assembly, is unknown, though I doubt that.
If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in DataAnnotations validation messages. This epic hack works for us.
Go to "Microsoft .NET Framework 4.6.1 Language Pack" download page https://www.microsoft.com/en-us/download/details.aspx?id=49977
Select language and download
Extract NDP461-KB3102436-x86-x64-AllOS-{LANG}.exe with 7-Zip
Extract CAB file x64-Windows10.0-KB3102502-x64.cab with 7-Zip
Locate "msil_system.componentmod..notations.resources_...."
... in which you'll find "system.componentmodel.dataannotations.resources.dll"
Open .resources.dll with ILSpy, locate Resources and click Save button above String Table to save as System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources.{LANGUAGE}.resources
Add to your project under say a "Resources"
Ensure the files Build Action property of the resources files is set to "Embedded Resource"
Then in a PreStart method of your project you overwrite the System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources.resourceMan private static field (told you it was a hack) with the ones you have in your project.
using System;
using System.Linq;
using System.Reflection;
using System.Resources;
[assembly: WebActivator.PreApplicationStartMethod(typeof(ResourceManagerUtil), nameof(ResourceManagerUtil.PreStart))]
class ResourceManagerUtil
{
public static void PreStart()
{
initDataAnnotationsResourceManager();
}
/// <summary>
/// If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in
/// DataAnnotations validation messages. Here we override DataAnnotationsResources to use a ResourceManager that uses language .resources
/// files embedded in this assembly.
/// </summary>
static void initDataAnnotationsResourceManager()
{
var embeddedResourceNamespace = "<YourProjectDefaultNamespace>.<FolderYouSavedResourcesFilesIn>";
var dataAnnotationsResourcesName = "System.ComponentModel.DataAnnotations.Resources.DataAnnotationsResources";
var thisAssembly = typeof(ResourceManagerUtil).Assembly;
var dataAnnotationsAssembly = typeof(System.ComponentModel.DataAnnotations.ValidationAttribute).Assembly;
var resourceManager = new ResourceManager(embeddedResourceNamespace + "." + dataAnnotationsResourcesName, thisAssembly);
// Set internal field `DataAnnotationsResources.resourceMan`
var dataAnnotationsResourcesType = dataAnnotationsAssembly.GetType(dataAnnotationsResourcesName);
var resmanProp = dataAnnotationsResourcesType.GetField("resourceMan", BindingFlags.NonPublic | BindingFlags.Static);
resmanProp.SetValue(null, resourceManager);
}
}
Assuming that you want to override the default error message strings in the validation attributes, you can do that by setting the ErrorMessageResourceName and the ErrorMessageResourceType properties like this:
[Required(ErrorMessageResourceName = "Required_Username", ErrorMessageResourceType = typeof(MyResourceFile)]
public string Username { get; set; }
You can create a resource file called MyResourceFile.resx that contains Required_Username with the error message you want.
Hope this helps.
I want to provide an answer with the same idea as by Duncan Smart, but for .NET Core 2.2 instead of .NET Framework 4.x.
Here it is.
using System;
using System.Linq;
using System.Reflection;
using System.Resources;
public static class ResourceManagerHack
{
/// <summary>
/// If the server doesn't have .NET language packs installed then no matter what CurrentUICulture is set to, you'll always get English in
/// DataAnnotations validation messages. Here we override DataAnnotationsResources to use a ResourceManager that uses language .resources
/// files embedded in this assembly.
/// </summary>
public static void OverrideComponentModelAnnotationsResourceManager()
{
EnsureAssemblyIsLoaded();
FieldInfo resourceManagerFieldInfo = GetResourceManagerFieldInfo();
ResourceManager resourceManager = GetNewResourceManager();
resourceManagerFieldInfo.SetValue(null, resourceManager);
}
private static FieldInfo GetResourceManagerFieldInfo()
{
var srAssembly = AppDomain.CurrentDomain
.GetAssemblies()
.First(assembly => assembly.FullName.StartsWith("System.ComponentModel.Annotations,"));
var srType = srAssembly.GetType("System.SR");
return srType.GetField("s_resourceManager", BindingFlags.Static | BindingFlags.NonPublic);
}
internal static ResourceManager GetNewResourceManager()
{
return new ResourceManager($"{typeof(<YourResource>).Namespace}.Strings", typeof(<YourResource>).Assembly);
}
private static void EnsureAssemblyIsLoaded()
{
var _ = typeof(System.ComponentModel.DataAnnotations.RequiredAttribute);
}
}
And I call this like so:
public static void Main(string[] args)
{
ResourceManagerHack.OverrideComponentModelAnnotationsResourceManager();
CreateWebHostBuilder(args).Build().Run();
}
Furthermore, I created a ~/Resources/<YourResource>.resx file and populated it with the default values and changed them at will. Lastly I created a public empty class <YourResource>.
Question: Is it possible in back end code (not in the code behind but in an actual back end class) to load and render a page or control defined in a .aspx or .ascx without having to use Load(path) and instead just create an instance of the page/control class?
I want to be able to do this (from a back end class NOT a code behind):
MyControl myCtl = new MyApp.Views.Shared.MyControl();
String html = Util.ControlToString(myCtl); //I get an empty string & hidden errors
instead of this
public static string ControlToString(string path)
{
Page pageHolder = new Page();
MyControl myCtl = (MyControl)pageHolder.LoadControl(path);
pageHolder.Controls.Add(myCtl);
StringWriter output = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, output, false);
return output.ToString();
}
Details:
In a Asp.net WebApp I occasionally need to render a user control (.ascx) or page (.aspx) as a HTML string. When a page or control inherits from a code behind, its class shows up in intellisense in my back end code and I can create an instance and set properties without getting compile time or run time errors. However, when I try to render the page or control I always get an empty string and upon inspection the page or control shows suppressed internal rendering errors unless I load the page or control using its physical file path.
I think the key issue has to do with when & how the .aspx / .ascx files are runtime compiled. I don't want to create a pre compiled class library of user controls because that would make the design process awkward and I really like the designer features offered by the .aspx / .ascx pages and so I'd love to find a way to make the pages compile in the solution so that they are usable like any other back end class but can still be created using the designer. I want the best of both worlds (1) to be able to edit pages and controls in the designer and (2) create instances and set their properties using back end classes.
Here is an approach that may help in situations like this.
The "back-end" code may not know where the user control is located, but the User Control does know where it is.
So, in the User Control, add a static method like this:
public partial class MyControl : UserControl
{
...
public static MyControl LoadControl(CustomDto initialData)
{
var myControl =
(MyControl)
((Page) HttpContext.Current.Handler)
.LoadControl("~\\UserControlsSecretPath\\MyControl.ascx");
myControl._initialData = initialData;
return myControl;
}
...
private CustomDto _initialData;
}
(The CustomDto is included to illustrate how initial data can be passed to the User Control. If you don't need to do that, take it out!)
With this, the code that loads the user control does not need to know the path to where the user control is physically located. If that location ever changes, then update this one location. All other code that uses this UserControl is unchanged.
In your back-end code, or anywhere else, you can do something this:
var control = MyControl.LoadControl(customDto);
PlaceHolder1.Controls.Add(control);
Generally speaking: no.
As far as I know, ASP.NET inherits from your classes to combine the .aspx/.ascx template with your code. This is why your controls show up empty: the code to combine the template with your code is missing. This is usually done by ASP.NET the first time you access a page or user control (that's precisely why the first hit is a little slow: it's actually generating and compiling the hookup-code).
For precompiled websites ASP.NET generates this code as part of your precompiled website's .dll in advance, which is why such sites load quicker. However, IIRC you'll still need to instantiate the generated classes rather than your original classes.
It's a pretty common request, but so far MS has not provided the tools to do this.
Edit: Although I fail to see why you'd want to render a control to an in-memory string, I might have a solution to the build problems.
If you stick to non-compiled .ascx files (using the web site model rather than the web application model), you can actually develop them separately by placing them physically in subfolder of your main project, and treat them as content files only. Then, you can make a separate project with this subfolder as the root folder. You only need to treat the files in this subfolder as web site files, the main project can still be a web application. (Actually recommended, 'cause you don't want the .csproj files included in the main project.)
However, shared code (that is, shared between the controls project and the main project) should be put in a separate library project, so you can compile each project separately without interdependencies.
Using LoadControl within the main project will compile them on the fly (code behind is possible); if you need to set properties, you must however define interfaces in the shared project, implement them on the appropriate user controls and cast the control created by LoadControl to the appropriate interface.
I developed a solution that solves my problem in VS 2008:
Create Main Site Solution: Create a MVC 1 Website solution in
VS 2008
Create Model Class Library: Add a Class Library for the Model Code
Create View Code: Add an "Empty Website" to hold the .ascx pages, and add a reference the model library
Create Deployment Site: Add a deployment project that compiles the "Empty Website" goto the "properties page" and Check: "Merge All outputs into a single assembly" and "Treat as library component" and be sure to UnCheck: "Allow this precompiled site to be updatable"
Reference Deployment Output: In the main project add a reference to the output of the Deployment site.
ASP. - Compiled Controls: Controls show up under the ASP. namespace and are named in two ways
A. if the .ascx / aspx page did not declare a "ClassName" then they are named using their folder and file name with underscores ex. <%# Control Language="C#" ClassName="Admin_Index" %>
B. if they did declare a class name then that is their name
List item
Usage: Example code is below
Here is an example usage
public ActionResult Index()
{
var ctl = new ASP.Directory_FirmProfile(); //create an instance
ctl.Setup(new MyDataModel); //assign data
//string test = CompiledControl.Render(ctl); //output to string
return new HtmlCtl.StrongView(ctl); //output to response
}
public class CompiledControl
{
public static string Render(Control c)
{
Page pageHolder = new Page();
pageHolder.Controls.Add(c);
StringWriter output = new StringWriter();
HttpContext.Current.Server.Execute(pageHolder, output, false);
return output.ToString();
}
public static void Render(Control c, StringWriter output)
{
Page pageHolder = new Page();
pageHolder.Controls.Add(c);
HttpContext.Current.Server.Execute(pageHolder, output, false);
}
public static void Render(Control c, HttpResponseBase r)
{
Page pageHolder = new Page();
pageHolder.Controls.Add(c);
HttpContext.Current.Server.Execute(pageHolder, r.Output, false);
}
}
public class StrongView : ActionResult
{
private Control ctl;
public StrongView(Control ctl)
{
this.ctl = ctl;
}
public string VirtualPath{get;set;}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HtmlCtl.CompiledControl.Render(ctl, context.HttpContext.Response);
}
}
I've come up with a simpler solution along the lines of Ruben's advice.
It has worked without problems for about a month:
//Example usage
//reference the control
var emailCTL = new HtmlCtl.ControlOnDisk<MyControlType>(#"~\Views\EmailTemplates\MyControlType.ascx");
//if you have a code behind you will get intellisense allowing you to set these properties
// and re-factoring support works most places except the template file.
emailCTL.c.title = "Hello World "; //title is a property in the code behind
emailCTL.c.data = data; //data is a property in the code behind
string emailBody = emailCTL.RenderStateless();
//Helper Class
public class ControlOnDisk<ControlType> where ControlType : UserControl
{
public ControlType c;
Page pageHolder = new Page();
public ControlOnDisk(string path)
{
var o = pageHolder.LoadControl(path);
c = (ControlType)o;
pageHolder.Controls.Add(c);
}
public string RenderStateless()
{
StringWriter output = new StringWriter();
// set up dumby context for use in rendering to email stream
StringBuilder emailMessage = new StringBuilder();
TextWriter tw = new StringWriter(emailMessage);
HttpResponse dumbyResponse = new HttpResponse(tw);
HttpRequest dumbyRequest = new HttpRequest("", "http://InsertURL.com/", ""); //dummy url requierd for context but not used
HttpContext dumbyContext = new HttpContext(dumbyRequest, dumbyResponse);
//HttpContextBase dumbyContextBase = new HttpContextWrapper2(dumbyContext);
dumbyContext.Server.Execute(pageHolder, output, false);
return output.ToString();
}
}