Create XML representation of WCF binding instance - wcf

I'm trying to write code to convert a WCF wsHttpBinding to customBinding, using the method described on WSHttpBinding.CreateBindingElements Method
.
Binding wsHttpBinding = ...
BindingElementCollection beCollection = originalBinding.CreateBindingElements();
foreach (var element in beCollection)
{
customBinding.Elements.Add(element);
}
Once I have generated the custom binding, I want to generate an XML representation for that new custom binding. (The same XML representation that's found in an application's .config file).
Is there a way to do that?
(I'm aware of the tool referenced in this answer: https://stackoverflow.com/a/4217892/5688, but I need something I can call within an application and without depending on a service in the cloud)

The class I was looking for was System.ServiceModel.Description.ServiceContractGenerator
Exemple to generate a configuration for an instance of any kind of Binding:
public static string SerializeBindingToXmlString(Binding binding)
{
var tempConfig = Path.GetTempFileName();
var tempExe = tempConfig + ".exe";
var tempExeConfig = tempConfig + ".exe.config";
// [... create empty .exe and empty .exe.config...]
var configuration = ConfigurationManager.OpenExeConfiguration(tempExe);
var contractGenerator = new ServiceContractGenerator(configuration);
string bindingSectionName;
string configurationName;
contractGenerator.GenerateBinding(binding, out bindingSectionName, out configurationName);
BindingsSection bindingsSection = BindingsSection.GetSection(contractGenerator.Configuration);
// this needs to be called in order for GetRawXml() to return the updated config
// (otherwise it will return an empty string)
contractGenerator.Configuration.Save();
string xmlConfig = bindingsSection.SectionInformation.GetRawXml();
// [... delete the temporary files ...]
return xmlConfig;
}
This solution feels like a hack because of the need to generate empty temporary files, but it works.
Now I'll have to look for a way to have a fully in-memory instance of a System.Configuration.Configuration (maybe by writing my own implementation)

Added missing code parts:
// [... create empty .exe and empty .exe.config...]
// [... delete the temporary files ...]
public static string SerializeBindingToXmlString(Binding binding)
{
var tempConfig = System.IO.Path.GetTempFileName();
var tempExe = tempConfig + ".exe";
var tempExeConfig = tempConfig + ".exe.config";
using(System.IO.FileStream fs = new System.IO.FileStream(tempExe, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite))
{
}
using (System.IO.FileStream fs = new System.IO.FileStream(tempExeConfig, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite))
{
fs.SetLength(0);
using (System.IO.StreamWriter sr = new System.IO.StreamWriter(fs, System.Text.Encoding.UTF8)) {
sr.WriteLine("<?xml version= \"1.0\" encoding=\"utf-8\" ?>");
sr.WriteLine(#"<configuration />");
}
}
var configuration = System.Configuration.ConfigurationManager.OpenExeConfiguration(tempExe);
var contractGenerator = new System.ServiceModel.Description. ServiceContractGenerator(configuration);
string bindingSectionName;
string configurationName;
contractGenerator.GenerateBinding(binding, out bindingSectionName, out configurationName);
var bindingsSection =System.ServiceModel.Configuration.BindingsSection.GetSection(contractGenerator.Configuration);
// this needs to be called in order for GetRawXml() to return the updated config
// (otherwise it will return an empty string)
contractGenerator.Configuration.Save();
string xmlConfig = bindingsSection.SectionInformation.GetRawXml();
System.IO.File.Delete(tempExeConfig);
System.IO.File.Delete(tempExe);
return xmlConfig;
}

Related

How to access configuration provider values in asp.net core startup

In my asp.net core 3.1 project I have setup a custom sql provider which is working great. In my Startup I want to load only the values from the SQLConfigurationProvider into a separate dictionary for use throughout my app. I can see that the configuration object contains a collection of providers as per below screenshot. However I cannot find a way to access only the SQLConfigurationProvider and get the subsequent values. Is this possible?
What you can do is very simple. Once you build your ConfigurationRoot
IConfigurationRoot configRoot = config.Build();
What you can do is to enumerate to a List only the providers you want:
var providers = configRoot.Providers.Where(
p => (p.GetType().Name == "AzureAppConfigurationProvider") ||
(p.GetType().Name == "AzureKeyVaultConfigurationProvider")).ToList();
Then, recreate a new temporary ConfigurationRoot using only the selected providers:
var tempRoot = new ConfigurationRoot(new List<IConfigurationProvider>(providers));
Then, in order to get the values you can do the following:
tempRoot.AsEnumerable().ToDictionary(a => a.Key, a => a.Value)
This way you will get a Dictionary with the key/value pairs of the configuration.
Note: When you initialize a new ConfigurationRoot from the providers, the SDK triggers again the loading of the configuration which might introduce delays (especially if the configuration is retrieved from a cloud service). If you don't want something like that then you probably have to go with the solution suggested by Brando.
According to your description, I suggest you could firstly use Configuration.Providers to get the SQLConfigurationProvider.
But the ConfigurationProvider's data property is protected, so we should write a extension method to get the value and set it into a directory.
More details, you could refer to below codes:
Create a extension class:
public static class ConfigurationProviderExtensions
{
public static HashSet<string> GetFullKeyNames(this IConfigurationProvider provider, string rootKey, HashSet<string> initialKeys)
{
foreach (var key in provider.GetChildKeys(Enumerable.Empty<string>(), rootKey))
{
string surrogateKey = key;
if (rootKey != null)
{
surrogateKey = rootKey + ":" + key;
}
GetFullKeyNames(provider, surrogateKey, initialKeys);
if (!initialKeys.Any(k => k.StartsWith(surrogateKey)))
{
initialKeys.Add(surrogateKey);
}
}
return initialKeys;
}
}
Then you could add below codes to get the provider and get the value..
// Replace the EnvironmentVariablesConfigurationProvider to your provider
var re = ((ConfigurationRoot)Configuration).Providers.FirstOrDefault(x=>x.GetType() == typeof(EnvironmentVariablesConfigurationProvider));
var directory = new Dictionary<string, string>();
foreach (var key in re.GetFullKeyNames(null, new HashSet<string>()).OrderBy(p => p))
{
if (re.TryGet(key, out var value))
{
directory.Add(key, value);
}
}
Result:

Error using OpenXML to read a .docx file from a memorystream to a WordprocessingDocument to a string and back

I have an existing library that I can use to receive a docx file and return it. The software is .Net Core hosted in a Linux Docker container.
It's very limited in scope though and I need to perform some actions it can't do. As these are straightforward I thought I would use OpenXML, and for my proof of concept all I need to do is to read a docx as a memorystream, replace some text, turn it back into a memorystream and return it.
However the docx that gets returned is unreadable. I've commented out the text replacement below to eliminate that, and if I comment out the call to the method below then the docx can be read so I'm sure the issue is in this method.
Presumably I'm doing something fundamentally wrong here but after a few hours googling and playing around with the code I am not sure how to correct this; any ideas what I have wrong?
Thanks for the help
private MemoryStream SearchAndReplace(MemoryStream mem)
{
mem.Position = 0;
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(mem, true))
{
string docText = null;
StreamReader sr = new StreamReader(wordDoc.MainDocumentPart.GetStream());
docText = sr.ReadToEnd();
//Regex regexText = new Regex("Hello world!");
//docText = regexText.Replace(docText, "Hi Everyone!");
MemoryStream newMem = new MemoryStream();
newMem.Position = 0;
StreamWriter sw = new StreamWriter(newMem);
sw.Write(docText);
return newMem;
}
}
If your real requirement is to search and replace text in a WordprocessingDocument, you should have a look at this answer.
The following unit test shows how you can make your approach work if the use case really demands that you read a string from a part, "massage" the string, and write the changed string back to the part. It also shows one of the shortcomings of any other approach than the one described in the answer already mentioned above, e.g., by demonstrating that the string "Hello world!" will not be found in this way if it is split across w:r elements.
[Fact]
public void CanSearchAndReplaceStringInOpenXmlPartAlthoughThisIsNotTheWayToSearchAndReplaceText()
{
// Arrange.
using var docxStream = new MemoryStream();
using (var wordDocument = WordprocessingDocument.Create(docxStream, WordprocessingDocumentType.Document))
{
MainDocumentPart part = wordDocument.AddMainDocumentPart();
var p1 = new Paragraph(
new Run(
new Text("Hello world!")));
var p2 = new Paragraph(
new Run(
new Text("Hello ") { Space = SpaceProcessingModeValues.Preserve }),
new Run(
new Text("world!")));
part.Document = new Document(new Body(p1, p2));
Assert.Equal("Hello world!", p1.InnerText);
Assert.Equal("Hello world!", p2.InnerText);
}
// Act.
SearchAndReplace(docxStream);
// Assert.
using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(docxStream, false))
{
MainDocumentPart part = wordDocument.MainDocumentPart;
Paragraph p1 = part.Document.Descendants<Paragraph>().First();
Paragraph p2 = part.Document.Descendants<Paragraph>().Last();
Assert.Equal("Hi Everyone!", p1.InnerText);
Assert.Equal("Hello world!", p2.InnerText);
}
}
private static void SearchAndReplace(MemoryStream docxStream)
{
using (WordprocessingDocument wordDocument = WordprocessingDocument.Open(docxStream, true))
{
// If you wanted to read the part's contents as text, this is how you
// would do it.
string partText = ReadPartText(wordDocument.MainDocumentPart);
// Note that this is not the way in which you should search and replace
// text in Open XML documents. The text might be split across multiple
// w:r elements, so you would not find the text in that case.
var regex = new Regex("Hello world!");
partText = regex.Replace(partText, "Hi Everyone!");
// If you wanted to write changed text back to the part, this is how
// you would do it.
WritePartText(wordDocument.MainDocumentPart, partText);
}
docxStream.Seek(0, SeekOrigin.Begin);
}
private static string ReadPartText(OpenXmlPart part)
{
using Stream partStream = part.GetStream(FileMode.OpenOrCreate, FileAccess.Read);
using var sr = new StreamReader(partStream);
return sr.ReadToEnd();
}
private static void WritePartText(OpenXmlPart part, string text)
{
using Stream partStream = part.GetStream(FileMode.Create, FileAccess.Write);
using var sw = new StreamWriter(partStream);
sw.Write(text);
}

Renaming models in database with existing data when using RavenDb

Is there any 'easy' way to rename models in RavenDb when the database already has existing data? I have various models which were originally created in another language, and now I would like to rename them to English as the codebase is becoming quite unmaintainable. If I just rename them, then the data won't be loaded because the properties don't match anymore.
I would like the system to automatically do it on first load. Is there any best way how to approach this? My solution would be:
Check if a document exists to determine if the upgrade has been done or not
If upgrade has not been done, execute patch scripts to update fields
Update document to know that the upgrade has been done
I'd recommend you create new documents from the old documents.
This can be done pretty easily using patching via docStore.UpdateByIndex.
Suppose I had an old type name, Foo, and wanted to rename it to the new type name, Bar. And I wanted all the IDs to change from Foos/123 to Bars/123.
It would look something like this:
var patchScript = #"
// Copy all the properties from the old document
var newDoc = {};
for (var prop in this) {
if (prop !== '#metadata') {
newDoc[prop] = this[prop];
}
}
// Create the metadata.
var meta = {};
meta['Raven-Entity-Name'] = newCollection;
meta['Raven-Clr-Type'] = newType;
// Store the new document.
var newId = __document_id.replace(oldCollection, newCollection);
PutDocument(newId, newDoc, meta);
";
var oldCollection = "Foos";
var newCollection = "Bars";
var newType = "KarlCassar.Bar, KarlCassar"; // Where KarlCassar is your assembly name.
var query = new IndexQuery { Query = $"Tag:{oldCollection}" };
var options = new BulkOperationOptions { AllowStale = false };
var patch = new ScriptedPatchRequest
{
Script = patchScript,
Values = new Dictionary<string, object>
{
{ nameof(oldCollection), oldCollection },
{ nameof(newCollection), newCollection },
{ nameof(newType), newType }
}
};
var patchOperation = docStore.DatabaseCommands.UpdateByIndex("Raven/DocumentsByEntityName", query, patch, options);
patchOperation.WaitForCompletion();
Run that code once at startup, and then your app will be able to work with the new name entities. Your old entities are still around - those can be safely deleted via the Studio.

PDF generation in mvc4 with itextsharp

I am working on pdf generation, it is successfully implemented using itextsharp.dll. It’s working fine on local environment after publish also. We have our own server at other site
But same code doesn't work on the server,pdf is not generated instead it gives an error: 'The document has no pages.'
Initially I thought it is due to no data in document but it works locally with or without data in the document.
I had code implemented as follows to make a web request Is any problem in that ??
try
{
var myHttpWebRequest = (HttpWebRequest)WebRequest.Create(strPdfData + "?objpatId=" + patID);
var response = myHttpWebRequest.GetResponse();
myHttpWebRequest.Timeout = 900000;
var stream = response.GetResponseStream();
StreamReader sr = new StreamReader(stream);
content = sr.ReadToEnd();
}
create a method in the controller:
[HttpGet]
public JsonResult GetFile()
{
var json = new WebClient().DownloadFile(string address, string fileName);
//This code is just to convert the file to json you can keep it in file format and send to the view
dynamic result = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
var oc = Newtonsoft.Json.JsonConvert.DeserializeObject<countdata[]>(Convert.ToString(result.countdata));
return Json(oc, JsonRequestBehavior.AllowGet);
}
In the view just call this function:
#Url.Action('genPDF','GetFile');

Generate HBM files using mapping by code

I'm using NHibernate mapping by code and I'm creating the session factory in this way:
var mapper = new ModelMapper();
mapper.AddMappings(Assembly.GetExecutingAssembly().GetExportedTypes());
HbmMapping domainMapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
const bool executeScript = false;
var configuration = new Configuration();
configuration.DataBaseIntegration(c =>
{
c.Dialect<MsSql2005Dialect>();
c.ConnectionString =
ConfigurationManager.ConnectionStrings["ShopConnectionString"]
.ConnectionString;
c.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
});
configuration.AddMapping(domainMapping);
_sessionFactory = configuration.BuildSessionFactory();
I need to get the corresponding HBM files.
How can I achieve that?
Two ways:-
//This will write all the XML into the bin/mappings folder
mapper.CompileMappingForEachExplicitlyAddedEntity().WriteAllXmlMapping();
be careful of this method above as your asp.net app will recycle as changes are detected in your bin folder, another way is:-
var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
//you could add a breakpoint here!
var mappingXml = mapping.AsString();
Use the AsString() extension method:
domainMapping.AsString()
It will give you the xml which you can save into a file. You can call that method e.g. before you build the SessionFactory.