I need to write a test where I manually create a Microsoft.Azure.ServiceBus.Message and serialize it to JSON. Example code:
var message = new Microsoft.Azure.ServiceBus.Message
{
MessageId = "0c8dfad9-6f0f-4d7f-a248-2a48fc899486",
CorrelationId = "78b507b0-6266-458d-afe6-7882c935e481",
Body = Encoding.UTF8.GetBytes("Hello world"),
};
var json = JsonConvert.SerializeObject(message);
However, I get the following exception when serializing:
Newtonsoft.Json.JsonSerializationException :
Error getting value from 'ExpiresAtUtc' on 'Microsoft.Azure.ServiceBus.Message'.
---- System.InvalidOperationException : Operation is not valid due to the current state of the object.
Any ideas how I can create a valid Message (that can be later serialized)? ExpiresAtUtc is get only so cannot be directly set. Is there a way to indirectly set it?
ExpiresAtUtc is set by the broker, considered an internal (SystemProperties collection), and is intentionally designed that way. There was a similar question about testing and if you really need this, the only way to achieve it would be using reflection.
var message = new Message();
//message.TimeToLive = TimeSpan.FromSeconds(10);
var systemProperties = new Message.SystemPropertiesCollection();
// systemProperties.EnqueuedTimeUtc = DateTime.UtcNow.AddMinutes(1);
var bindings = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty;
var value = DateTime.UtcNow.AddMinutes(1);
systemProperties.GetType().InvokeMember("EnqueuedTimeUtc", bindings, Type.DefaultBinder, systemProperties, new object[] { value});
// workaround "ThrowIfNotReceived" by setting "SequenceNumber" value
systemProperties.GetType().InvokeMember("SequenceNumber", bindings, Type.DefaultBinder, systemProperties, new object[] { 1 });
// message.systemProperties = systemProperties;
bindings = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty;
message.GetType().InvokeMember("SystemProperties", bindings,Type.DefaultBinder, message, new object[] { systemProperties });
Note that approach is not coming from Azure Service Bus team as they see it a dangerous practice that could end up in your production.
Related
I am trying to save the mailmessage in file. So i have used below code to save the mailmessage in file. the same code working in .Net core 3.1 but it's throwing error on .Net 6.
Error Info:
Object reference not set to an instance of an object.
Its happening because for GetConstructor() method returning null value
Please find the snippet of code:
Assembly assembly = typeof(SmtpClient).Assembly;
Type _mailWriterType =
assembly.GetType("System.Net.Mail.MailWriter");
using (FileStream _fileStream =
new FileStream(FileName, FileMode.Create))
{
// Get reflection info for MailWriter contructor
ConstructorInfo _mailWriterContructor =
_mailWriterType.GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null,
CallingConventions.HasThis,
new Type[] { typeof(Stream) },
null);
// Construct MailWriter object with our FileStream
object _mailWriter =
_mailWriterContructor.Invoke(new object[] { _fileStream });
// Get reflection info for Send() method on MailMessage
MethodInfo _sendMethod =
typeof(MailMessage).GetMethod(
"Send",
BindingFlags.Instance | BindingFlags.NonPublic);
// Call method passing in MailWriter
_sendMethod.Invoke(
Message,
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new object[] { _mailWriter, true, true },
null);
// Finally get reflection info for Close() method on our MailWriter
MethodInfo _closeMethod =
_mailWriter.GetType().GetMethod(
"Close",
BindingFlags.Instance | BindingFlags.NonPublic);
// Call close method
_closeMethod.Invoke(
_mailWriter,
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new object[] { },
null);
}
The implementation of this internal class has changed, which shouldn't surprise you as it's internal and not documented, and therefore subject to change at any time (this includes minor builds, not just major version changes).
The code used to have
internal MailWriter(Stream stream)
: base(stream, true)
// This is the only stream that should encoding leading dots on a line.
// This way it is done message wide and only once.
{
}
Now it has
internal MailWriter(Stream stream, bool encodeForTransport)
: base(stream, encodeForTransport)
// This is the only stream that should encoding leading dots on a line.
// This way it is done message wide and only once.
{
}
It changed in this GitHub pull
I have a MVC site that I am developing that is multi-tenant application. I have set up the cache to varybyheader="host". Now I'd like to invalidate the cache only by hostname.
The RemoveOutputCacheItem only takes absolute virtual paths and isn't allowing a custom host name (there by being a non-virtual path).
Any help on how to achieve this?
Thanks.
Update
Here is how I can get the internal cache keys
var runtimeType = typeof(HttpRuntime);
var ci = runtimeType.GetProperty(
"CacheInternal",
BindingFlags.NonPublic | BindingFlags.Static);
var cache = ci.GetValue(ci, new object[0]);
var cachesInfo = cache.GetType().GetField(
"_caches",
BindingFlags.NonPublic | BindingFlags.Instance);
var cacheEntries = cachesInfo.GetValue(cache);
var outputCacheEntries = new List<object>();
foreach (Object singleCache in cacheEntries as Array)
{
var singleCacheInfo =
singleCache.GetType().GetField("_entries",
BindingFlags.NonPublic | BindingFlags.Instance);
var entries = singleCacheInfo.GetValue(singleCache);
foreach (DictionaryEntry cacheEntry in entries as Hashtable)
{
var cacheEntryInfo = cacheEntry.Value.GetType().GetField("_value",
BindingFlags.NonPublic | BindingFlags.Instance);
var value = cacheEntryInfo.GetValue(cacheEntry.Value);
if (value.GetType().Name == "CachedRawResponse")
{
var key = (string)cacheEntry.Value.GetType().BaseType.GetField("_key", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(cacheEntry.Value);
key = key.Substring(key.IndexOf("/"));
outputCacheEntries.Add(key);
}
}
}
var keys = new StringBuilder();
foreach (string key in outputCacheEntries)
{
if (key.Contains(Request.Url.Host))
{
keys.Append(key + " ");
HttpResponse.RemoveOutputCacheItem(key);
}
}
That RemoveOutputCacheItem doesn't work with this key.
They key is generated like this: /HQNnoneV+n+FCNhostVHOSTNAME.comDE
Even a direct call RemoveOutputCache("/HOSTNAME.com") doesn't work either (with vary by custom).
Update #2
So read through the reference source code (http://referencesource.microsoft.com/#System.Web/HttpResponse.cs,3222f830c91ccb06) and it appears that it should attempt to create the custom key. So I should be able to RemoveOutputCache("/") and it should create the custom key for me, but this also appears to not be working as expected, it still appears to clear all keys.
You can try the VaryByCustom
[OutputCache(Duration = 3600, VaryByCustom = "hostname")]
And define the VaryByCustom like:
public override string GetVaryByCustomString(HttpContext Context, string Custom)
{
//Here you set any cache invalidation policy
if (Custom == "hostname")
{
return Context.Request.Url.Host;
}
return base.GetVaryByCustomString(Context, Custom);
}
The answer is that this cannot be done in this manner. Digging into the actual code for RemoveOutputCache where it generates the key doesn't pass in the varyby parameter and therefore I am unable to get the specific key needed.
Perhaps if I dug a bit more into the key generation, perhaps I could write a key gen method, but I've gone a different route. This is just to give everyone else an answer to my issue.
Again I only wanted to vary by header, no params and be able to invalidate just that one cached item.
I'm a little late to the party, but I had the exact same need, and ended up being inspired by the answers in this SO question: Clearing Page Cache in ASP.NET
I setup the following below test, and was able to successfully clear the cache per host with no additional code...
[OutputCache(VaryByHeader = "host", Duration = 600, Location = OutputCacheLocation.Server)]
public ActionResult TestPage()
{
var key = GetCacheKey("TestPage");
HttpContext.Cache[key] = new object();
Response.AddCacheItemDependency(key);
return Content(DateTime.Now.ToString());
}
public ActionResult TestClearCache()
{
var key = GetCacheKey("TestPage");
HttpContext.Cache.Remove(key);
return Content("Cache cleared");
}
private string GetCacheKey(string page)
{
return string.Concat(page, Request.Url.Host.ToLower());
}
I'm using RhinoMocks for testing. It's not good at redirecting statics; I've considered using another library like the successor to Moles (edit: I guess the Fakes tool is only available in VS2012? that stinks) or TypeMock, but would prefer not to.
I have a 3rd party library that takes in an HttpRequest object. My first stab at it was to use:
public void GetSamlResponseFromHttpPost(out XmlElement samlResponse,
out string relayState, HttpContextBase httpContext = null)
{
var wrapper = httpContext ?? new HttpContextWrapper(HttpContext.Current);
// signature of the next line cannot be changed
ServiceProvider.ReceiveSAMLResponseByHTTPPost(
wrapper.ApplicationInstance.Context.Request, out samlResponse, out relayState);
All looked fine, until I went to test it. The real issue here is that I need to stub out wrapper.ApplicationInstance.Context.Request. Which leads into a whole host of old school 'ASP.NET don't like testing' pain.
I have heard that you can use dynamic magic in C# to redirect static methods. Can't find any examples of doing this with something like HttpContext though. Is this possible?
Not an ideal solution, but my solution to test this was to use reflection and modify the object under the hood:
httpRequest = new HttpRequest("default.aspx", "http://test.com", null);
var collection = httpRequest.Form;
// inject a value into the Form directly
var propInfo = collection.GetType().GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
propInfo.SetValue(collection, false, new object[] { });
collection["theFormField"] = val;
propInfo.SetValue(collection, true, new object[] { });
var appInstance = new HttpApplication();
var w = new StringWriter();
httpResponse = new HttpResponse(w);
httpContext = new HttpContext(httpRequest, httpResponse);
// set the http context on the app instance to a new value
var contextField = appInstance.GetType().GetField("_context", BindingFlags.Instance | BindingFlags.NonPublic);
contextField.SetValue(appInstance, httpContext);
Context.Stub(ctx => ctx.ApplicationInstance).Return(appInstance);
My goal here was to have wrapper.ApplicationInstance.Context.Request return a form field value when asked. It may have been roundabout, but it works. This code only exists in test code so I'm happy with it.
So, the below code used to work in .NET 4 to get a System.Net.Mail.MailMessage object as a MemoryStream, however with the release of .NET 4.5 beta a runtime exception occurs.
Assembly assembly = typeof(SmtpClient).Assembly;
Type mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");
using (MemoryStream stream = new MemoryStream())
{
ConstructorInfo mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null);
object mailWriter = mailWriterContructor.Invoke(new object[] { stream });
MethodInfo sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic);
sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true }, null);
.....
}
Runtime exception occurs on sendMethod.Invoke().
Managed to figure out how to get this working again in .NET 4.5 beta. The private API Send() method in MailMessage has changed to: internal void Send(BaseWriter writer, bool sendEnvelope, bool allowUnicode)
Please find updated code below.
Assembly assembly = typeof(SmtpClient).Assembly;
Type mailWriterType = assembly.GetType("System.Net.Mail.MailWriter");
using (MemoryStream stream = new MemoryStream())
{
ConstructorInfo mailWriterContructor = mailWriterType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(Stream) }, null);
object mailWriter = mailWriterContructor.Invoke(new object[] { stream });
MethodInfo sendMethod = typeof(MailMessage).GetMethod("Send", BindingFlags.Instance | BindingFlags.NonPublic);
sendMethod.Invoke(message, BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { mailWriter, true, true }, null);
.....
}
This might be usable if you don't want to go with unsupported hacks and don't mind extra performance hit.
public static class MailMessageExtensions
{
public static string RawMessage(this MailMessage m)
{
var smtpClient = new SmtpClient { DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory };
using (var tempDir = new TemporaryDirectory())
{
smtpClient.PickupDirectoryLocation = tempDir.DirectoryPath;
smtpClient.Send( m );
var emlFile = Directory.GetFiles( smtpClient.PickupDirectoryLocation ).FirstOrDefault();
if ( emlFile != null )
{
return File.ReadAllText( emlFile );
}
else
return null;
}
return null;
}
}
class TemporaryDirectory : IDisposable
{
public TemporaryDirectory()
{
DirectoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory( DirectoryPath );
}
public string DirectoryPath { get; private set; }
public void Dispose()
{
if ( Directory.Exists( DirectoryPath ) )
Directory.Delete( DirectoryPath, true );
}
}
for checking if extra boolean i use :
If _sendMethod.GetParameters.Length = 2 Then
_sendMethod.Invoke(Message, BindingFlags.Instance Or BindingFlags.NonPublic, Nothing, New Object() {_mailWriter, True}, Nothing)
Else
_sendMethod.Invoke(Message, BindingFlags.Instance Or BindingFlags.NonPublic, Nothing, New Object() {_mailWriter, True, True}, Nothing)
End If
The proposed solution with the extra TRUE works beautifully.
I started to getting the error while running my project in VS2012 even though I am not using .net 4.5 but 4.0 in all my libraries.
The error only happens on the machine where you have installed VS2012, looks like VS2012 makes reference to .net 4.5 while you are debugging. When you deploy and run the application in clients running .net 4.0 everything works fine.
Thus : If you run 4.0 - do not add the extra TRUE, if you run 4.5 add it.
We fought with the mail message conversion for a long time. Ultimately the solution was to use MimeKit.
var memoryStream = new MemoryStream();
var mimeMessage = MimeMessage.CreateFromMailMessage(message);
mimeMessage.WriteTo(memoryStream);
If you use the methods above you will get really close and it will work in most cultures but eventually the subject encoding will defeat you.
For those, who are struggling with mailWriterContructor being null in .NET 5 or facing Parameter count mismatch exception, take a closer look on my solution usable for any stream. Link here
I need to request the following URL inside my application:
http://feedbooks.com/type/Crime%2FMystery/books/top
When I run the following code:
Uri myUri = new Uri("http://feedbooks.com/type/Crime%2FMystery/books/top");
The Uri constructor decodes the %2F into a literal /, and I get a 404 error because it has changed the URL to:
http://feedbooks.com/type/Crime/Mystery/books/top
The Uri class has a constructor that takes a parameter dontEscape, but that constructor is deprecated and setting it to true has no effect.
My first thought was to do something like:
Uri myUri = new Uri("http://feedbooks.com/type/Crime%252FMystery/books/top");
With the hopes that it would convert %25 into a literal %, but that didn't work either.
Any ideas how to create a correct Uri object for this particular URL in .NET?
It's a bit easier in .NET 4.0. You can put a setting in your config file like this:
<uri>
<schemeSettings>
<add name="http" genericUriParserOptions="DontUnescapePathDotsAndSlashes" />
</schemeSettings>
</uri>
It only works for the 'http' and 'https' schemes.
Or here's a new version of the LeaveDotsAndSlashesEscaped method. It doesn't need a particular Uri instance, just call it when your application starts up:
private void LeaveDotsAndSlashesEscaped()
{
var getSyntaxMethod =
typeof (UriParser).GetMethod("GetSyntax", BindingFlags.Static | BindingFlags.NonPublic);
if (getSyntaxMethod == null)
{
throw new MissingMethodException("UriParser", "GetSyntax");
}
var uriParser = getSyntaxMethod.Invoke(null, new object[] { "http" });
var setUpdatableFlagsMethod =
uriParser.GetType().GetMethod("SetUpdatableFlags", BindingFlags.Instance | BindingFlags.NonPublic);
if (setUpdatableFlagsMethod == null)
{
throw new MissingMethodException("UriParser", "SetUpdatableFlags");
}
setUpdatableFlagsMethod.Invoke(uriParser, new object[] {0});
}
I ran into the same problem using 2.0...
I discovered a workaround posted at this blog:
// System.UriSyntaxFlags is internal, so let's duplicate the flag privately
private const int UnEscapeDotsAndSlashes = 0x2000000;
public static void LeaveDotsAndSlashesEscaped(Uri uri)
{
if (uri == null)
{
throw new ArgumentNullException("uri");
}
FieldInfo fieldInfo = uri.GetType().GetField("m_Syntax", BindingFlags.Instance | BindingFlags.NonPublic);
if (fieldInfo == null)
{
throw new MissingFieldException("'m_Syntax' field not found");
}
object uriParser = fieldInfo.GetValue(uri);
fieldInfo = typeof(UriParser).GetField("m_Flags", BindingFlags.Instance | BindingFlags.NonPublic);
if (fieldInfo == null)
{
throw new MissingFieldException("'m_Flags' field not found");
}
object uriSyntaxFlags = fieldInfo.GetValue(uriParser);
// Clear the flag that we don't want
uriSyntaxFlags = (int)uriSyntaxFlags & ~UnEscapeDotsAndSlashes;
fieldInfo.SetValue(uriParser, uriSyntaxFlags);
}
It works perfectly.
Hope this helps (better late than never!)
This is a bug I filed a while back. It's supposed to be fixed in 4.0, but I'm not holding my breath. Apparently it's still a problem in the RC.
By combining the 2 previous answers you can have a method you only need to call once per process that works both on .net 4 and .net 3.5.
// System.UriSyntaxFlags is internal, so let's duplicate the flag privately
private const int UnEscapeDotsAndSlashes = 0x2000000;
private void LeaveDotsAndSlashesEscaped()
{
var getSyntaxMethod =
typeof (UriParser).GetMethod("GetSyntax", BindingFlags.Static | BindingFlags.NonPublic);
if (getSyntaxMethod == null)
{
throw new MissingMethodException("UriParser", "GetSyntax");
}
var uriParser = getSyntaxMethod.Invoke(null, new object[] { "http" });
FieldInfo flagsFieldInfo = typeof(UriParser).GetField("m_Flags", BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.Instance);
if (flagsFieldInfo == null)
{
throw new MissingFieldException("UriParser", "m_Flags");
}
int flags = (int) flagsFieldInfo.GetValue(uriParser);
// unset UnEscapeDotsAndSlashes flag and leave the others untouched
flags = flags & ~UnEscapeDotsAndSlashes;
flagsFieldInfo.SetValue(uriParser, flags);
}