How do we create a reusable RenderFragment in a separate code file? - razor

In this Microsoft article, they show how to extract a piece of Blazor code into a method so that it can be reused. They show the following example...
<h1>Hello, world!</h1>
#RenderWelcomeInfo
<p>Render the welcome info a second time:</p>
#RenderWelcomeInfo
#code {
private RenderFragment RenderWelcomeInfo = __builder =>
{
<p>Welcome to your new app!</p>
};
}
They then go on to explain that if you want to extract this method into a separate .razor file, you can make it static.
That's all fine for a simple example such as the ones they show, but if you want to anything realistic, chances are you will need to pass at least one parameter to the method.
However, if you change their SayHello example to take a parameter...
public static RenderFragment SayHelloName(string name) = __builder => {
<h1>Hello #name</h1>
};
...then you get an interesting mix of compiler errors...
The = sign has a red underline, and hovering over it gives an error Function body expected
The variable __builder is highlighted in red, and hovering over it gives an error Cannot resolve symbol __builder
The space right after the variable __builder is highlighted in red, and hovering over it gives an error Cannot resolve symbol __builder\n\nUnexpected token
The => has a red underline, and hovering over that gives an error Not all code paths return a value in lambda expression of type 'RenderFragment' (which is interesting, as the return type doesn't have a generic type)
The usage of name in the body of the method is red with an error Cannot resolve symbol 'name'
The }; right at the end is underlined in red and shows an error of } expected, with the opening braces being the class declaration.
To confuse matters more, only the 4th and 5th of these errors show up in the Error panel, albeit with slightly different wording.
Following the example show in Telerik's KB, I tried the following...
public static RenderFragment<object> SayHelloName(string name) => context => __builder => {
<h1>Hello #name</h1>
};
However, when I try to use this...
<div>#SayHelloName("Jim")</div>
...the following is rendered on the page...
Microsoft.AspNetCore.Components.RenderFragment`1[System.Object]
...instead of "Hello Jim" as I expected.
Anyone able to explain what I'm doing wrong? How do I extract a RenderFragment method that takes parameters into a separate class?

Alternative syntax:
#page "/razor-template"
#timeTemplate
#petTemplate(new Pet { Name = "Nutty Rex" })
#code {
private RenderFragment timeTemplate = #<p>The time is #DateTime.Now.</p>;
private RenderFragment<Pet> petTemplate = (pet) => #<p>Pet: #pet.Name</p>;
private class Pet
{
public string? Name { get; set; }
}
}
This is from the docs: https://learn.microsoft.com/en-gb/aspnet/core/blazor/components/?view=aspnetcore-6.0#razor-templates

Reading a bit further down the page you linked to:
RenderFragment delegates can also accept parameters. The following
component passes the message (message) to the RenderFragment delegate:
<div class="chat">
#foreach (var message in messages)
{
#ChatMessageDisplay(message)
}
</div>
#code {
private RenderFragment<ChatMessage> ChatMessageDisplay = message => __builder =>
{
<div class="chat-message">
<span class="author">#message.Author</span>
<span class="text">#message.Text</span>
</div>
};
}
So it looks like you need
public static RenderFragment<string> SayHelloName = name => __builder => {
<h1>Hello #name</h1>
};
If you want to pass in more parameters, wrap them in a type e.g Greeting:
public class Greeting
{
public string Title { get; set; }
public string Name { get; set; }
}
and then pass the Greeting as a type parameter similar to ChatMessage example in the docs:
public static RenderFragment<Greeting> SayHelloName = greeting=> __builder => {
<h1>Hello #greeting.Title #greeting.Name</h1>
};
Or you can use a non-generic version:
public static RenderFragment SayHelloName(string title, string name) => __builder => {
<h1>Hello #greeting.Title #greeting.Name</h1>
};

The key thing to understand about RenderFragment is it's a delegate. The code within the fragment will run in the context of the class where it is defined.
There are many ways to define one. Here are a few:
<h3>RenderFragments.razor</h3>
#code {
public static RenderFragment SayHello = (builder) =>
{
builder.OpenElement(0, "div");
builder.AddContent(1, "Hello");
builder.CloseElement();
};
public static RenderFragment SayHelloAnotherWay => (__builder) =>
{
<div>
Hello Another Way
</div>
};
public static RenderFragment SayHelloAnotherWay1(string name) => (__builder) =>
{
<div>
Hello Another Way #name
</div>
};
public static RenderFragment SayHelloToName(string name) => (builder) =>
{
builder.AddMarkupContent(0, $"<div>Hello {name}</div>");
};
public static RenderFragment<string> SayHello1 => (context) => (builder) =>
{
builder.AddMarkupContent(0, $"<div>Hello {context}</div>");
};
}
And usage:
#page "/"
#RenderFragments.SayHello
#RenderFragments.SayHelloToName("Fred")
#RenderFragments.SayHello1(Name)
#RenderFragments.SayHelloAnotherWay
#RenderFragments.SayHelloAnotherWay1(Name)
#code {
string Name = "John";
}

Related

Call DbFunction with EF core 2.1

I try to call a scalar function stored in my DB.
Here is my code:
public class PronosticDbContext : DbContext
{
public PronosticDbContext(DbContextOptions<PronosticDbContext> options) : base(options)
{
}
[DbFunction(FunctionName = "GetPlayerPointsForDay", Schema = "dbo")]
public static int GetPlayerPointsForDay(int dayId, int userId) => throw new NotSupportedException();
}
The call :
private IQueryable<PlayerRankingData> GetPlayerRanking(int? dayId)
{
return Context.Days.Where(x => x.Id == dayId.Value).Select(x => new PlayerRankingData
{
// Some properties
Users = x.Season.Users.Select(us => new PlayerRankingData.User
{
// Other properties
Last = PronosticDbContext.GetPlayerPointsForDay(x.Id, us.User.Id),
// Others again
}).OrderByDescending(u => u.Points).ThenByDescending(u => u.Last).ThenBy(u => u.Login)
});
}
I don't understand why but I always go to the NotSupportedException, my scalar function is never called. Why did I miss ?
I also tried like this, but same result...
modelBuilder
.HasDbFunction(typeof(PronosticDbContext).GetMethod(nameof(GetPlayerPointsForDay)))
.HasSchema("dbo")
.HasName(nameof(GetPlayerPointsForDay));
Ok, I got it.
The thing is you can't call the scalar function directly (I don't know why), it must be encapsulate in a linq query, like this :
Last = Context.Users.Select(u => PronosticDbContext.GetPlayerPointsForDay(x.Id, us.User.Id)).FirstOrDefault()
And that's all. No need to declaration in the DbContext, this is enough :
[DbFunction]
public static int? GetPlayerPointsForDay(int dayId, int userId) => throw new NotSupportedException();

Proper way to return JSON list in Web API with MVC and partial model

I am trying to use Web API to grab certain fields from my MVC controller. I can't seem to match the right type with the right list. I am fine with converting everything to string.
I either get an error in code (can not convert types), or if I get it to compile, I get this error:
"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'."
From other similar posts, people responded with how to create a list, but not with the declaration of the return value of the Get. Please include both.
Also I would prefer not to add additional controllers as I need to do this on a number of my models.
Here is my code--note you can see I tried a number of different ways:
public class APICLIENTsController : ApiController
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET api/<controller>
public IEnumerable<string> Get()
//public IEnumerable<CLIENT> Get()
{
//return db.CLIENTs.OrderBy(x => x.CLIENTNAME).ToList();
string[] listOfUsers = db.CLIENTs.OrderBy(x => x.CLIENTNAME).Select(r => new
{
ID = r.CLIENTID.ToString(),
NAME = r.CLIENTNAME
});
return listOfUsers.ToList();
//return db.CLIENTs.Select(x => new { x.CLIENTNAME }).ToArray();
}
If you want to return JSON use the
JsonResult
type.
public JsonResult Get()
{
//return db.CLIENTs.OrderBy(x => x.CLIENTNAME).ToList();
string[] listOfUsers = db.CLIENTs.OrderBy(x => x.CLIENTNAME).Select(r => new
{
ID = r.CLIENTID.ToString(),
NAME = r.CLIENTNAME
});
return Json(listOfUsers.ToList(), JsonRequestBehavior.AllowGet);
}
Your query is returning a collection of anonymous objects, not string[] so it will throw an exception. Even if you were to generate string[] by concatenating the CLIENTID and CLIENTNAME properties, it would be a little use to the client.
Create a model to represent what you need to return to the view
public class ClientVM
{
public int ID { get; set; }
public string Name { get; set; }
}
and modify your method to
public IEnumerable<ClientVM> Get()
{
IEnumerable<ClientVM> model = db.CLIENTs.OrderBy(x => x.CLIENTNAME).Select(r => new ClientVM
{
ID = r.CLIENTID,
Name = r.CLIENTNAME
});
return model;
}
Side note: depending on how your calling and consuming this in the client, you may need to change the Content-Type to specifically return json (refer these answers for more detail)

AutoMapper - passing parameter to custom resolver weird behavior

Although I'm relatively new to AutoMapper I'm using it in a small project I'm developing. I've never had problems using it before but now I'm facing some weird behavior passing parameters to a Custom Resolver.
Here's the scenario: I get a list of messages from my repository and then map those to a frontend friendly version of it. Nothing fancy, just some normal mapping between objects. I have a field in that frontend object that tells if a certain user already voted for that message and that's what I'm using the Custom Resolver for (it's that second "ForMember"):
public List<SupportMessageUi> GetAllVisible(string userId)
{
Mapper.CreateMap<SupportMessage, SupportMessageUi>()
.ForMember(dest => dest.Votes,
opt => opt.ResolveUsing<SupportMessageVotesResolver>())
.ForMember(dest => dest.UserVoted,
opt => opt.ResolveUsing<SupportMessagesUserVotedResolver>()
.ConstructedBy(() => new SupportMessagesUserVotedResolver(userId)));
var messages = _unitOfWork.MessagesRepository.Get(m => m.Visible);
var messagesUi = Mapper.Map<List<SupportMessageUi>>(messages);
return messagesUi;
}
I'm calling this method on a web service and the problem is: the first time I call the webservice (using the webservice console) it all runs perfectly. For example, if I pass '555' as the userId I get to this method with the correct value:
And in the Custom Resolver the value was correctly passed to the constructor:
The results returned are correct. The problem comes next. The second time I call the service, passing a different argument ('666' this time) the argument that gets to the constructor of the Custom Resolver is the old one ('555'). Here's what I mean:
Right before mapping the objects we can see that the value passed to the constructor was correct ('666'):
But when it gets to the constructor of the Resolver the value is wrong, and is the old one ('555'):
All subsequent calls to the service use the original value in the Custom Resolver constructor ('555'), independently of the value I pass to the service (also happens if I make the call from another browser). If I shut down the server and relaunch it I can pass a new parameter (that will be used in all other calls until I shut it down again).
Any idea on why this is happening?
It's happening because AutoMapper.CreateMap is a static method, and only needs to be called once. With the CreateMap code in your web method, you're trying to call it every time you call that method on your web service. Since the web server process stays alive between calls (unless you restart it, like you said) then the static mappings stay in place. Hence, the necessity of calling AutoMapper.Reset, as you said in your answer.
But it's recommended that you put your mapping creation in AppStart or Global or a static constructor or whatever, so you only call it once. There are ways to call Map that allow you to pass in values, so you don't need to try to finesse things with the constructor of your ValueResolver.
Here's an example using a ValueResolver (note the change to implementing IValueResolver instead of inheriting ValueResolver<TSource, TDestination>):
[Test]
public void ValueTranslator_ExtraMapParameters()
{
const int multiplier = 2;
ValueTranslator translator = new ValueTranslator();
Mapper.AssertConfigurationIsValid();
ValueSource source = new ValueSource { Value = 4 };
ValueDest dest = translator.Translate(source, multiplier);
Assert.That(dest.Value, Is.EqualTo(8));
source = new ValueSource { Value = 5 };
dest = translator.Translate(source, multiplier);
Assert.That(dest.Value, Is.EqualTo(10));
}
private class ValueTranslator
{
static ValueTranslator()
{
Mapper.CreateMap<ValueSource, ValueDest>()
.ForMember(dest => dest.Value, opt => opt.ResolveUsing<ValueResolver>().FromMember(src => src.Value));
}
public ValueDest Translate(ValueSource source, int multiplier)
{
return Mapper.Map<ValueDest>(source, opt => opt.Items.Add("multiplier", multiplier));
}
private class ValueResolver : IValueResolver
{
public ResolutionResult Resolve(ResolutionResult source)
{
return source.New((int)source.Value * (int)source.Context.Options.Items["multiplier"]);
}
}
}
private class ValueSource { public int Value { get; set; } }
private class ValueDest { public int Value { get; set; } }
And here's an example using a TypeConverter:
[Test]
public void TypeTranslator_ExtraMapParameters()
{
const int multiplier = 3;
TypeTranslator translator = new TypeTranslator();
Mapper.AssertConfigurationIsValid();
TypeSource source = new TypeSource { Value = 10 };
TypeDest dest = translator.Translate(source, multiplier);
Assert.That(dest.Value, Is.EqualTo(30));
source = new TypeSource { Value = 15 };
dest = translator.Translate(source, multiplier);
Assert.That(dest.Value, Is.EqualTo(45));
}
private class TypeTranslator
{
static TypeTranslator()
{
Mapper.CreateMap<TypeSource, TypeDest>()
.ConvertUsing<TypeConverter>();
}
public TypeDest Translate(TypeSource source, int multiplier)
{
return Mapper.Map<TypeDest>(source, opt => opt.Items.Add("multiplier", multiplier));
}
private class TypeConverter : ITypeConverter<TypeSource, TypeDest>
{
public TypeDest Convert(ResolutionContext context)
{
TypeSource source = (TypeSource)context.SourceValue;
int multiplier = (int)context.Options.Items["multiplier"];
return new TypeDest { Value = source.Value * multiplier };
}
}
}
private class TypeSource { public int Value { get; set; } }
private class TypeDest { public int Value { get; set; } }
Answering myself: I was not using AutoMapper.Reset(). Once I did that everything started working properly.
Helpful reading: http://www.markhneedham.com/blog/2010/01/27/automapper-dont-forget-mapper-reset-at-the-start/

NHibernate LinqToHqlGenerator for SQL Server 2008 full text index 'Containing' keyword

I think I'm missing something fundamental when implementing a LinqToHql generator class.
I've successfully registered the SQL Server 2008 contains query using a custom dialect with this registration:
RegisterFunction("contains", new StandardSQLFunction("contains", null));
I have only one class with a full text index to be queried:
public class SearchName
{
public virtual Guid Id {get; set;}
public virtual string Name {get; set;} // this is the search field
}
The contains function works properly in HQL:
var names = Session.CreateQuery("from SearchName where contains(Name,:keywords)")
.SetString("keywords", "john")
.List();
and the generated SQL is perfect:
select searchname0_.Id as Id4_,
searchname0_.Name as Name4_
from Search_Name searchname0_
where contains(searchname0_.Name, 'john' /* #p0 */)
The next challenge was to implement the Linq to HQL generator:
public class MyLinqtoHqlGeneratorsRegistry :
DefaultLinqToHqlGeneratorsRegistry
{
public MyLinqtoHqlGeneratorsRegistry()
{
this.Merge(new ContainsGenerator());
}
}
public class ContainsGenerator : BaseHqlGeneratorForMethod
{
public ContainsGenerator()
{
SupportedMethods = new[] {
ReflectionHelper.GetMethodDefinition<SearchName>(d => d.Name.Contains(String.Empty))
};
}
public override HqlTreeNode BuildHql(MethodInfo method,
System.Linq.Expressions.Expression targetObject,
ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
return treeBuilder.MethodCall("contains",
visitor.Visit(targetObject).AsExpression(),
visitor.Visit(arguments[0]).AsExpression()
);
}
}
}
Calling the method like this:
var namesLinq = Session.Query<SearchName>().Where(x=> x.Name.Contains("john")).ToList();
Unfortunately, this doesn't seem to override the built-in Contains method, and the generated SQL is wrong:
select searchname0_.Id as Id4_,
searchname0_.Name as Name4_
from Search_Name searchname0_
where searchname0_.Name like ('%' + 'john' /* #p0 */ + '%')
Is it not possible to override the default Contains method, or have I just made a silly mistake?
PS - I'm using NHibernate 3.3.1.4000
OK, I've finally figured it out!
First, I managed to delete the registration code from my configuration:
...
.ExposeConfiguration(cfg =>
{
cfg.LinqToHqlGeneratorsRegistry<MyLinqtoHqlGeneratorsRegistry>();
...
}
Second, don't try to override the existing Linq behaviors. I moved my Contains extension method to the full-text class.
Third, build the Hql tree correctly.
For others trying to implement a SQL 2008 Free-text contains search, here's the complete implementation:
public static class DialectExtensions
{
public static bool Contains(this SearchName sn, string searchString)
{
// this is just a placeholder for the method info.
// It does not otherwise matter.
return false;
}
}
public class MyLinqtoHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public MyLinqtoHqlGeneratorsRegistry()
: base()
{
RegisterGenerator(ReflectionHelper.GetMethod(() =>
DialectExtensions.Contains(null, null)),
new ContainsGenerator());
}
}
public class ContainsGenerator : BaseHqlGeneratorForMethod
{
string fullTextFieldName = "Name";
public ContainsGenerator()
: base()
{
SupportedMethods = new[] {
ReflectionHelper.GetMethodDefinition(() =>
DialectExtensions.Contains(null, null))
};
}
public override HqlTreeNode BuildHql(MethodInfo method,
System.Linq.Expressions.Expression targetObject,
ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
// cannot figure out how to interrogate the model class to get an
// arbitrary field name...
// perhaps the RegisterGenerator() call above could be used to pass a
// property name to the ContainsGenerator constructor?
// in our case, we only have one full text searchable class, and its
// full-text searchable field is "Name"
HqlExpression[] args = new HqlExpression[2] {
treeBuilder.Ident(fullTextFieldName).AsExpression(),
visitor.Visit(arguments[1]).AsExpression()
};
return treeBuilder.BooleanMethodCall("contains", args);
}
}
For the above to work, you must have declared and used your custom dialect:
public class CustomMsSql2008Dialect : NHibernate.Dialect.MsSql2008Dialect
{
public CustomMsSql2008Dialect()
{
RegisterFunction(
"contains",
new StandardSQLFunction("contains", null)
);
}
}
Then you can use your new contains search this way:
var namesLinq = Session.Query<SearchName>().Where(x => x.Contains("john")).ToList();
... and the resulting SQL is perfect! (at least if you only have one table you're performing full-text searches on)
EDIT: UPDATED IMPLEMENTATION TO SUPPORT MORE THAN ONE FULLTEXT 'Contains' SEARCH PER QUERY.
Here's the revised version:
public static class DialectExtensions
{
public static bool FullTextContains(this string source, string pattern)
{
return false;
}
}
public class MyLinqtoHqlGeneratorsRegistry : DefaultLinqToHqlGeneratorsRegistry
{
public MyLinqtoHqlGeneratorsRegistry()
: base()
{
RegisterGenerator(ReflectionHelper.GetMethod(() => DialectExtensions.FullTextContains(null, null)),
new FullTextContainsGenerator());
}
}
public class FullTextContainsGenerator : BaseHqlGeneratorForMethod
{
public FullTextContainsGenerator()
{
SupportedMethods = new[] { ReflectionHelper.GetMethod(() => DialectExtensions.FullTextContains(null, null)) };
}
public override HqlTreeNode BuildHql(MethodInfo method,
System.Linq.Expressions.Expression targetObject,
ReadOnlyCollection<System.Linq.Expressions.Expression> arguments,
HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
HqlExpression[] args = new HqlExpression[2] {
visitor.Visit(arguments[0]).AsExpression(),
visitor.Visit(arguments[1]).AsExpression()
};
return treeBuilder.BooleanMethodCall("contains", args);
}
}
To use the revised version, the syntax is slightly different:
var namesLinq = Session.Query<SearchName>().Where(x => x.Name.FullTextContains("john")).ToList();

How to add a type forward to an existing Castle Windsor registration

I'm currently registering a bunch of stuff at one point in my initialisation sequence
Container.Register(AllTypes.FromAssemblyContaining<MyAssembly>()
.BasedOn(typeof(IRepository<>))
.WithService.Self().Configure(c => c.LifeStyle.Transient));
I'm using WithService.Self so that it doesn't automatically pick up AllInterfaces, so that the interface that I will want to add later on as a type forward has not already been added.
I'd like then (later on) to be able to add a type forward to one of the already registered components, and Intellisense on the ForwardedTypes property suggested using .Forward(typof()) e.g.
Container.Register(Component.For<IOtherInterface>()
.Forward(typeof(IOtherInterface))
.ImplementedBy<AlreadyRegisteredType>().LifeStyle.Transient);
Is this possible?
EDIT:
I've been trying to get the stuff that Krzysztof has suggested working so I've generated a test project (below). I've tried various combinations to get the ConfigureFor to forward IMyInterface to MySecondType, but just can't get it to work, when done as a second step after initial registration of my component types. I'm probably being dim, but I'm just not getting how the ConfigureFor command works, and the documentation is a little sketchy (non-existant) on the subject.
namespace TestProject1
{
public class MyType : IMyInterface
{
public virtual string MyProperty { get; set; }
}
public class MySecondType : IMyInterface
{
public virtual string MySecondProperty { get; set; }
}
public interface IMyInterface
{
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
//New Container
var container = new WindsorContainer();
//Register Types
container.Register(
AllTypes.FromAssemblyContaining<MyType>().BasedOn<IMyInterface>().WithService.Self().Configure(
c => c.LifeStyle.Transient));
//Other stuff happens here...
//Now Register our interface as a forward
container.Register(AllTypes.FromAssemblyContaining<IMyInterface>()
.BasedOn<IMyInterface>()
.WithService.Base()
.ConfigureFor<IMyInterface>(r => r.Forward<MySecondType>()).Configure(c => c.LifeStyle.Transient));
var typeA = new MySecondType();
var typeB = container.Resolve<IMyInterface>();
Assert.IsInstanceOfType(typeB.GetType(), typeA.GetType());
}
}
}
What you described in the comment is registering another component for the AlreadyRegisteredType.
If you want to add a forward to the same component use
ConfigureFor<AlreadyRegisteredType>(c => c.Forward<IOtherInterface>())
full example:
Container.Register(AllTypes.FromThisAssembly()
.BasedOn<IEmptyService>()
.WithService.Base()
.ConfigureFor<EmptyServiceComposite>(r => r.Forward<EmptyServiceComposite>()));
Once you registered some type
Container.Register(Component.For<SomeType>());
you can forward some interface to it in this way:
Container.Register(Component.For<ISomeInterface>()
.UsingFactoryMethod<ISomeInterface>(kernel => kernel.Resolve<SomeType>()));