I want to use DotLiquid to create HTML in my ASP Net Core 2.2 Project
My data source comes from a JSon string which is passed to the method.
After deserializing the string with DeserializeObject I get an error in the Hash.FromAnonymousObject method
This works:
var stuff1 = new
{
Name = "John",
Surname = "Smith",
Addresses = new[] {
new { City = "New York", State = "NY"},
new { City = "Milano", State = "IT" }
}
};
var hash1 = DotLiquid.Hash.FromAnonymousObject(stuff1);
This give me the error Incorrect number of arguments supplied for call to method 'Newtonsoft.Json.Linq.JToken get_Item(System.Object)' Parameter name: property
dynamic stuff2 = JsonConvert.DeserializeObject("{
'Name': 'John', 'Surname': 'Smith',
'Addresses': [
{ 'City': 'New York', 'State': 'NY' },
{ 'City': 'Milano', 'State': 'IT' }
]}");
var hash2 = DotLiquid.Hash.FromAnonymousObject(stuff2);
DotLiquid has an alternative Hash generator from Dictionary.
I was just able to solve this, with complex json structures, leveraging that by doing the following:
include the following:
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
then (for brevity the json is simple below, but works for complex structures, with nontrivial liquid templates)
string template = "<h4>hello {{name}}</h4>";
string json = "{ \"name\" : \"john doe\"}";
dynamic expandoObj = JsonConvert.DeserializeObject<ExpandoObject>(json, new ExpandoObjectConverter());
IDictionary<string, object> expandoDict = new Dictionary<string, object>(expandoObj);
var liquidTemplate = DotLiquid.Template.Parse(template);
var result = liquidTemplate.Render(Hash.FromDictionary(expandoDict));
Related
I am trying to use object initializer to set up a custom array in vb.net, there may be only 1 entry or there could be 100 on any given instance of this array. I have been successful in completing this task in C# however can not find documentation of completing it in Vb.net
I have a model:
Public Class Artist
Public Name As String
Public Task As String
End Class
within another model we have further listed this object as part of the collection
Public Property Artists() As Artist
which brings me to the controller
.Artists = New Artist() With
{.Name = "bob", .Task = "1"}
that is successful for 1 entry; how would I add another entry under the same instance of the object such as the following in c#
Artists = new Artist[]
{
new Artist() { name = "bob", Task = "1" },
new Artist() { name = "fred", Task = "2" },
new Artist() { name = "george", Task = "3" }
}
this all falls within a with statement itself being a sub of another object which seems to rule out traditional dimensioning
Dim cB = New CB {
.StoryTitle = "Test"
.IsbnNumber = 200
.Artists = new Artists...
}
Ultimate Solution
Dim cB = New CB With {
.StoryTitle = "Test",
.IsbnNumber = 200,
.Artists = New Artist() {New Artist() With {
.Name = "bob",
.Task = "1"
}, New Artist() With {
.Name = "fred",
.Task = "2"
}, New Artist() With {
.Name = "george",
.Task = "3"
}}
}
I have been successful in completing this task in C# however can not find documentation of completing it in Vb.net
Download ILSpy, drag/drop your Exe or Dll that you successfully created with C# and save project as a VB.Net project.
I'm bilingual with C# and VB.Net but when it comes to Lambda's and LINQ in VB.Net this is how I translate between the languages.
Or an even simpler and quicker method is: http://converter.telerik.com/
C#:
Artists a = new Artist[]
{
new Artist() { name = "bob", Task = "1" },
new Artist() { name = "fred", Task = "2" },
new Artist() { name = "george", Task = "3" }
}
VB.Net:
Dim a As Artists = New Artist() {New Artist() With {
.name = "bob",
.Task = "1"
}, New Artist() With {
.name = "fred",
.Task = "2"
}, New Artist() With {
.name = "george",
.Task = "3"
}}
Your syntax is a little bit off. Try this:
Dim artists() As Artist = {
New Artist() With {.Name = "bob", .Task = "1"},
New Artist() With {.Name = "bob", .Task = "1"}
}
I'd like to return a RedirectToRouteResult that sends users to a URL like the following:
/MyController/MyAction/id?newid=3&newid=5&newid=7
The newid parameter has several values.
My call looks like: return RedirectToAction(string.Empty, routeValues);
Here is what I have tried so far, and that do not work:
// ...?newid%5B0%5D=3&newid%5B1%5D=5&newid%5B2%5D=7
var routeValues = new RouteValueDictionary {
{"id", myid},
{"newid[0]", 3},
{"newid[1]", 5},
{"newid[2]", 7},
};
// ...?newid=System.Int32%5B%5D
var routeValues = new { id = myid, newid = new int[] { 3, 5, 7 } };
// ...?newid=System.String%5B%5D
var routeValues = new { id = myid, newid = new string[] { "3", "5", "7" } };
// ...?newid=System.Int32%5B%5D
var routeValues = new RouteValueDictionary {
{"id", myid},
{"newid", new int[] { 3, 5, 7 } }
};
What is the secret to make this work?
That's one thing that's really missing from the framework. Your best bet is to manually roll it:
public ActionResult Foo()
{
var ids = new[] { 3, 5, 7 };
var url = new UriBuilder(Url.Action("MyAction", "MyController", new { id = "123" }, Request.Url.Scheme));
url.Query = string.Join("&", ids.Select(x => "newid=" + HttpUtility.UrlEncode(x.ToString())));
return Redirect(url.ToString());
}
Putting this into a custom extension method could increase the readability of course.
I was in the case where I don't even know the names of the keys that where provide multiple times. And since the querystring does accept multiple keys as a coma separated list. I found it helpful to write an extension method based on Darin's answer.
public static UriBuilder TransformToMultiplyKeyUrl(this RouteValueDictionary self, string baseUrl)
{
var url = new UriBuilder(baseUrl);
//Transform x=y,z into x=y&x=z
url.Query = String.Join("&",
self.SelectMany(
pair => ((string)pair.Value).Split(',')
.Select(v => String.Format("{0}={1}", pair.Key, HttpUtility.UrlEncode(v)))));
return url;
}
I'd like to return a RedirectToRouteResult that sends users to a URL like the following:
/MyController/MyAction/id?newid=3&newid=5&newid=7
The newid parameter has several values.
My call looks like: return RedirectToAction(string.Empty, routeValues);
Here is what I have tried so far, and that do not work:
// ...?newid%5B0%5D=3&newid%5B1%5D=5&newid%5B2%5D=7
var routeValues = new RouteValueDictionary {
{"id", myid},
{"newid[0]", 3},
{"newid[1]", 5},
{"newid[2]", 7},
};
// ...?newid=System.Int32%5B%5D
var routeValues = new { id = myid, newid = new int[] { 3, 5, 7 } };
// ...?newid=System.String%5B%5D
var routeValues = new { id = myid, newid = new string[] { "3", "5", "7" } };
// ...?newid=System.Int32%5B%5D
var routeValues = new RouteValueDictionary {
{"id", myid},
{"newid", new int[] { 3, 5, 7 } }
};
What is the secret to make this work?
That's one thing that's really missing from the framework. Your best bet is to manually roll it:
public ActionResult Foo()
{
var ids = new[] { 3, 5, 7 };
var url = new UriBuilder(Url.Action("MyAction", "MyController", new { id = "123" }, Request.Url.Scheme));
url.Query = string.Join("&", ids.Select(x => "newid=" + HttpUtility.UrlEncode(x.ToString())));
return Redirect(url.ToString());
}
Putting this into a custom extension method could increase the readability of course.
I was in the case where I don't even know the names of the keys that where provide multiple times. And since the querystring does accept multiple keys as a coma separated list. I found it helpful to write an extension method based on Darin's answer.
public static UriBuilder TransformToMultiplyKeyUrl(this RouteValueDictionary self, string baseUrl)
{
var url = new UriBuilder(baseUrl);
//Transform x=y,z into x=y&x=z
url.Query = String.Join("&",
self.SelectMany(
pair => ((string)pair.Value).Split(',')
.Select(v => String.Format("{0}={1}", pair.Key, HttpUtility.UrlEncode(v)))));
return url;
}
I'm trying to encrypt the URL parameters by implementing an EncryptedActionLink that returns a link with an encrypted parameter "p" to a generic action "ResolveUrl". The controller should recieve the request and invoke the proper action, or redirect it to the actual action without showing later the unencrypted values at the address bar (RedirectToAction doesn't work because of this).
So far, I've done this extension method:
public static MvcHtmlString EncryptedActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
{
var RouteValueDictionary = new RouteValueDictionary(routeValues);
RouteValueDictionary.Add("actionName", actionName);
RouteValueDictionary.Add("noise", new Random().Next(5000,10000));
var routeValuesText = RouteTable.Routes.GetVirtualPath(null, RouteValueDictionary).VirtualPath;
var Encryption64 = new Encryption64();
var routeValuesTextCrypto = Encryption64.Encrypt(routeValuesText, "ABC123AB");
return htmlHelper.ActionLink(linkText, "ResolveUrl", controllerName, new { p = routeValuesTextCrypto }, htmlAttributes);
}
using this method, i get the following URL:
<%: Html.EncryptedActionLink("MyText", "MyAction", "MyContoller", new { Parameter1 = 123, Parameter2 = "String", Parameter3 = false }, null)%>
http://localhost:21536/MyContoller/ResolveUrl?p=iqo6yhy0Zl3jZXdMmnJ9KdvQhqCb5X6gg19%2FqZ8XUe19r5PJ6xO84plZr1GUHCHNY9h2SDO1o4CaF9W2DdmpywXooEQ1S0rNYjpnH4s3wb%2FqM8sGxoqAqyIoC%2F2nqW7U
Now, all my contollers inherits from ContollerBase. There I define the ResolveUrl Action as this:
public ActionResult ResolveUrl(String p)
{
var Encryption64 = new Encryption64();
var query = Encryption64.Decrypt(p, "ABC123AB");
if (query.Length > 2)
query = query.Substring(2);
var tokens = query.Split(new String [] { "&" }, StringSplitOptions.RemoveEmptyEntries);
var RouteValueDictionary = new RouteValueDictionary();
for (int i = 0; i < tokens.Count(); i++)
{
var centerPos = tokens[i].IndexOf("=");
RouteValueDictionary.Add(tokens[i].Substring(0,centerPos),tokens[i].Substring(centerPos+1));
}
Type thisType = this.GetType();
MethodInfo theMethod = thisType.GetMethod(RouteValueDictionary["actionName"].ToString());
var theParameters = theMethod.GetParameters();
var theParametersObject = new object[theParameters.Count()];
System.ComponentModel.TypeConverter converter = new System.ComponentModel.TypeConverter();
for (int i=0 ; i<theParameters.Count();i++)
{
theParametersObject[i] = converter.ConvertTo(RouteValueDictionary[theParameters[i].Name],theParameters[i].ParameterType);
}
return (ActionResult)theMethod.Invoke(this, theParametersObject);
}
the thing about that code is that the ResolveUrl doesn't work. First, when there are two implementatios for one action (POST/GET) then an exception is throwed. And the second thing that fails is the parameter type conversion (for exampte converting from string to an nullable type).
How can I encrypt the URL parameters? Is any of my code useful? What is the best way to do this?
if you are trying to encrypt url parameters (route values) you can use custom valuedataprovider that will automatically decrypt the value on action without showing unencrypted value in address bar.
I am playing with .NET MVC at the moment and I writing some unit tests. I have an interface repository that looks like this:
public interface IRepository<TEntity> : IDisposable where TEntity : class
{
IQueryable<TEntity> GetAll();
TEntity Single(Expression<Func<TEntity, bool>> predicate);
}
I have mocked the GetAll method like this:
_mockRepository.Setup(x => x.GetAll()).Returns(
new List<Post>
{
new Post { Title = "Test Post 1" },
new Post { Title = "Test Post 2" }
}.AsQueryable());
Now I'm not sure how I would mock the Single method as it has a parameter that contains the expression I want to evaluate.
Thanks,
b3n
Edit:
I changed the code to:
_mockRepository.Setup(x => x.Single(It.IsAny<Expression<Func<Post, bool>>>()))
.Returns( (Expression<Func<Post, bool>> expr) => new List<Post> {
new Post { PostId = 1, Title = "Test Post 1", Created = DateTime.Now },
new Post { PostId = 2, Title = "Test Post 2", Created = DateTime.Now }
}.Where(expr));
But I receive the following error now:
'System.Collections.Generic.List' does not contain a definition for 'Where' and the best extension method overload 'System.Linq.Queryable.Where(System.Linq.IQueryable, System.Linq.Expressions.Expression>)' has some invalid arguments.
Is this even the best approach to do this or should the single method only return a new Post without even considering the passed in expression?
Edit 2:
This is the working result.
_mockRepository.Setup(x => x.Single(It.IsAny<Expression<Func<Post, bool>>>())).Returns(
(Expression<Func<Post, bool>> expr) =>
new List<Post>
{
new Post { PostId = 1, Title = "Test Post 1", Created = DateTime.Now },
new Post { PostId = 2, Title = "Test Post 2", Created = DateTime.Now }
}.Single(expr.Compile()));
I believe you can use an argument matcher as well as a lambda in the Returns method if you need to use the argument:
_mockRepository.Setup(x => x.Single(It.IsAny<Expression<Func<TEntity, bool>>>())
.Returns((Expression<Func<TEntity, bool>> expr) => ... );
Edit:
The error you are now getting is because you are trying to pass an expression tree to LINQ-To-Objects' Where method. To fix this, use the following:
_mockRepository.Setup(x => x.Single(It.IsAny<Expression<Func<Post, bool>>>()))
.Returns( (Expression<Func<Post, bool>> expr) => new List<Post> {
new Post { PostId = 1, Title = "Test Post 1", Created = DateTime.Now },
new Post { PostId = 2, Title = "Test Post 2", Created = DateTime.Now }
}.Where(expr.Compile()));
The Compile() will convert the expression tree into a simple Func delegate. In addition, since the mocked method returns a single object, Where() will not work, either.