Need to add a unique ID property in a composer tab, what's the best way of doing this and here's my attempt:
[PageTypeProperty(
EditCaption = "UniqueID",
HelpText = "UniqueID",
Type = typeof(PropertyString),
//DisplayInEditMode=false,
Tab = typeof(ComposerTab))]
public virtual Guid UniqueID
{
get { return UniqueID; }
set { UniqueID = System.Guid.NewGuid(); }
}
Intended behaviour: each time a new textbox is added on the page, it should be assigned a unique ID.
Problem with this piece of code is I don't see a unique ID entered in the text box when in Composer - Edit Mode (when I check the properties of this content block)
Please comment on correct way to set this GUID property and where. Thanks!
Later edit:
http://tedgustaf.com/blog/2011/9/create-episerver-composer-function-with-page-type-builder/
This example describes my approach
Block.cs
namespace MyProject.ComposerFunctions
{
[PageType("70421202Efdghajkfgkjd43535",
Name = "Block",
Description = "Block",
Filename = "Block.ascx")]
public class Block : BaseComposerFunctionData
{
[PageTypeProperty(
EditCaption = "UniqueID",
HelpText = "UniqueID",
Type = typeof(PropertyString),
//DisplayInEditMode=false,
Tab = typeof(ComposerTab))]
public virtual Guid UniqueID
{
get { return UniqueID; }
set { UniqueID = System.Guid.NewGuid(); }
}
}
}
Block.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="Block.ascx.cs" Inherits="MyProject.ComposerControls.Block" %>
Block.ascx.cs
namespace MyProject.ComposerControls
{
public partial class Block : Dropit.Extension.Core.BaseContentFunction
{
protected override void OnLoad(EventArgs e)
{
if (!IsPostBack)
{
this.DataBind();
}
}
}
}
BaseContentFunction is derived from UserControl, IPageSource, IFunctionSource
BaseComposerFunctionData is derived from TypedPageData
I'm not quit sure if this will work, but could you try the following in your Block.cs?
[PageTypeProperty(
EditCaption = "UniqueID",
HelpText = "UniqueID",
Type = typeof(PropertyString),
//DisplayInEditMode=false,
Tab = typeof(ComposerTab))]
public virtual Guid UniqueID { get; set; }
public override void SetDefaultValues(ContentType contentType)
{
UniqueID = System.Guid.NewGuid();
}
Related
I'm implementing a custom validation attribute. This attribute does not only look at the value of the property it is applied to, but also at the value of another property. The other property is specified by its name.
I need to find a way to get the full id that the input for the other property will have in the final HTML output.
This is a simplified version of my validation attribute:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class MyCustomValidationAttribute : ValidationAttribute, IClientModelValidator
{
private string _otherPropertyName;
public MyCustomValidationAttribute(string otherPropertyName)
{
_otherPropertyName = otherPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var otherProperty = context.ObjectInstance.GetType().GetProperty(_otherPropertyName);
var otherPropertyValue = Convert.ToString(otherProperty.GetValue(context.ObjectInstance, null));
// Validation logic...
}
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
MergeAttribute(context.Attributes, "data-val-mycustomvalidation", errorMessage);
// THIS ROW NEEDS TO BE FIXED
MergeAttribute(context.Attributes, "data-val-mycustomvalidation-otherpropertyname", _otherProperyName);
}
private void MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (!attributes.ContainsKey(key))
{
attributes.Add(key, value);
}
}
}
This demonstrates how it is used in a model class:
public class Report
{
[MyCustomValidation("Value2", ErrorMessage = "Error...")]
public string Value1 { get; set; }
public string Value2 { get; set; }
}
This is the JavaScript to make sure that the validation is also done on the client side:
$.validator.addMethod('mycustomvalidation',
function (value, element, parameters) {
var otherPropertyValue = $('#' + parameters.otherpropertyname).val();
// Validation logic...
});
$.validator.unobtrusive.adapters.add('mycustomvalidation', ['otherpropertyname'],
function (options) {
options.rules.mycustomvalidation = options.params;
options.messages['mycustomvalidation'] = options.message;
});
My viewmodel for the page/view with the form looks like this:
public MyViewModel
{
public Report MyReport { get; set; }
}
Note that I'm not using Report as my viewmodel, but rather as the type of a property in the viewmodel. This is important since this is the root of my problem...
The code in the view to show the input for Value1 is nothing strange (I'm using Razor Pages):
<div>
<label asp-for="MyReport.Value1"></label>
<input asp-for="MyReport.Value1" />
<span asp-validation-for="MyReport.Value1"></span>
</div>
And the output becomes:
<label for="MyReport_Value1">Value1</label>
<input
type="text"
id="MyReport_Value1"
name="MyReport.Value1"
data-val="true"
data-val-mycustomvalidation="Error..."
data-val-mycustomvalidation-otherpropertyname="Value2"
value=""
>
<span
data-valmsg-for="MyReport.Value1"
data-valmsg-replace="true"
class="text-danger field-validation-valid"
></span>
So the problem is that in the HTML output I need data-val-mycustomvalidation-otherpropertyname to be "MyReport_Value2" instead of just "Value2". Otherwise the validation code won't be able to find the second HTML input (with id MyReport_Value2) and perform the validation.
I figure this must be done in the method AddValidation() in the attribute class, but how do I get the full name that the HTML input will recieve?
I'm guessing there is some way to get this by using the context parameter. I've seen examples of something like "*.TemplateInfo.GetFullHtmlFieldId(PropertyName)" but I can't get it to work.
Any help is appreciated!
You pass Value2 to MyCustomValidationAttribute and set _otherPropertyName with Value2,and use
MergeAttribute(context.Attributes, "data-val-mycustomvalidation-otherpropertyname", _otherProperyName);
so that html will be
data-val-mycustomvalidation-otherpropertyname="Value2"
You only need to pass Report_Value2 to MyCustomValidationAttribute rather than Value2.
public class Report
{
[MyCustomValidation("Report_Value2", ErrorMessage = "Error...")]
public string Value1 { get; set; }
public string Value2 { get; set; }
}
So that you will get
data-val-mycustomvalidation-otherpropertyname="Report_Value2"
ValidationContext is binded to instance that belong to validating property i.e Model. Hence locating reference of ViewModel looks difficult.
I can provide three different solution you can use which one suits your requirement.
Solution 1:
Using ValidationContext you can able to get Name of the class where Property belong to. This solution will work only if ViewModel Property Name must be same as Model Class Name.
e.g. if Model Class is Student then property name must be Student. If property name is Student1 it wont work.
Solution 2 & 3 will work even if Class name and property name are different.
Model
public class Student
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Please enter name")]
public string Name { get; set; }
[Required]
[Country("Name")]
public string Country { get; set; }
}
ViewModel
public class StudentViewModel
{
public Student Student {get;set;} //Solution 1 wil not work for Student1
}
ValidationAttribute
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class CountryAttribute : ValidationAttribute, IClientModelValidator
{
private string _otherPropertyName;
private string _clientPropertyName;
public CountryAttribute(string otherPropertyName)
{
_otherPropertyName = otherPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var otherProperty = validationContext.ObjectInstance.GetType().GetProperty(_otherPropertyName);
var otherPropertyValue = Convert.ToString(otherProperty.GetValue(validationContext.ObjectInstance, null));
_clientPropertyName = otherProperty.DeclaringType.Name +"_"+ otherProperty.Name;
}
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-mycustomvalidation-otherpropertyname", _clientPropertyName);
}
}
Solution 2:
Using ClientModelValidationContext you can able to get ViewModel reference that is passed from the controller to view. By using reflection we can get the name of the property i.e Model.
To work with solution you need to pass empty ViewModel reference from controller.
Controller
public IActionResult New()
{
StudentViewModel studentViewModel = new StudentViewModel();
return View(studentViewModel);
}
ValidationAttribute
public void AddValidation(ClientModelValidationContext context)
{
var otherClientPropName = context.ModelMetadata.ContainerMetadata.Properties
.Single(p => p.PropertyName == this._otherPropertyName)
.GetDisplayName();
var viewContext = context.ActionContext as ViewContext;
if (viewContext?.ViewData.Model is StudentViewModel)
{
var model = (StudentViewModel)viewContext?.ViewData.Model;
var instanceName = model.GetType().GetProperties()[0].Name;
otherClientPropName = instanceName + "_" + otherClientPropName;
}
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-mycustomvalidation-otherpropertyname", otherClientPropName);
}
Solution 3:
Using context.Attributes["id"] you can able to get current property id value as string . By using string manipulation you can get prefix then you can merge with other property name.
This solution doesn't require empty ViewModel reference from controller.
Controller
public IActionResult New()
{
return View();
}
ValidationAttribute
public void AddValidation(ClientModelValidationContext context)
{
var otherClientPropName = context.ModelMetadata.ContainerMetadata.Properties
.Single(p => p.PropertyName == this._otherPropertyName)
.GetDisplayName();
var id = context.Attributes["id"];
var idPrefix = id.Split("_");
if (idPrefix.Length > 1)
{
otherClientPropName = idPrefix[0] + "_" + otherClientPropName;
}
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-mycustomvalidation-otherpropertyname", otherClientPropName);
}
HTML Output
<input class="form-control" type="text" data-val="true" data-val-required="Please enter name" id="Student_Name" name="Student.Name" value="">
<input class="form-control input-validation-error" type="text" data-val="true" data-val-mycustomvalidation-otherpropertyname="Student_Name" data-val-required="The Country field is required." id="Student_Country" name="Student.Country" value="">
This is a method that also works when there are fields rendered that are deeper children of the model.
//Build the client id of the property name.
var dependentClientId = dependentPropertyName;
var clientId = context.Attributes["id"];
var clientIdArr = clientId.Split("_");
if (clientIdArr.Length > 1)
{
//Replace the last value of the array with the dependent property name.
clientIdArr[clientIdArr.Length - 1] = dependentPropertyName;
dependentClientId = string.Join("_", clientIdArr);
}
MergeAttribute(context.Attributes, "data-val-mycustomvalidation-otherpropertyname", dependentClientId );
When I save a document that has a generic type DataView<Customer>, I'm manually setting the collection name to "customers". However, I'm having some trouble making an index using AbstractIndexCreationTask with a non-default collection name. Here's my index:
public class customers_Search
: AbstractIndexCreationTask<DataView<Customer>, customers_Search.Result>
{
public class Result
{
public string Query { get; set; }
}
public customers_Search()
{
Map = customers =>
from customer in customers
where customer.Data != null
select new
{
Query = AsDocument(customer.Data).Select(x => x.Value)
};
Index(x => x.Query, FieldIndexing.Analyzed);
}
}
When this gets deployed, it looks like this:
from customer in docs.DataViewOfCustomer
where customer.Data != null
select new {
Query = customer.Data.Select(x => x.Value)
}
This doesn't work obviously, and if I change DataViewOfCustomer to "customers" it works just fine.
I'd rather not have to use non-type-checked (string) indexes to deploy. Is there a way to set the collection name that from the AbstractIndexCreationTask class?
Update
Since my data class is generic, I made a generic index which fixes up the names.
public class DataViewQuery<TEntity>
: AbstractIndexCreationTask<DataView<TEntity>, DataViewQueryResult>
{
private readonly string _entityName;
private readonly string _indexName;
// this is to fix the collection name for the index name
public override string IndexName { get { return _indexName; } }
// this is to fix the collection name for the index query
public override void Execute(IDatabaseCommands databaseCommands, DocumentConvention documentConvention)
{
var conventions = documentConvention.Clone();
conventions.FindTypeTagName =
type =>
typeof(DataView<TEntity>) == type
? _entityName
: documentConvention.FindTypeTagName(type);
base.Execute(databaseCommands, conventions);
}
public DataViewQuery(string entityName)
{
_entityName = entityName;
_indexName = String.Format("{0}/{1}", entityName, "Query");
Map = items =>
from item in items
where item.Data != null
select new
{
Query = AsDocument(item.Data).Select(x => x.Value)
};
Index(x => x.Query, FieldIndexing.Analyzed);
}
}
public class DataViewQueryResult
{
public string Query { get; set; }
}
Then I can create a specific index which has all the configuration in it.
// sets the collection type (DataView<Customer>) for the index
public class CustomerQuery : DataViewQuery<Customer>
{
// sets the collection name for the index
public CustomerQuery() : base(EntityName.Customers) { }
}
You need to configure this in the conventions.
The property to configure is FindTypeTagName
Working on creating my first Orchard Module and I am running into issues getting the form data saved back to the database. I have everything registered correctly as far as I can tell from looking at a lot of samples so I must be missing something minor.
I am able to get the Apartment form to show under the new menu, validation is working but when I fill the form completly and hit save I get:
Your Apartment has been created.
Checking the database the record is not in the table and checking the logs shows:
2013-12-19 09:15:23,416 [19]
NHibernate.Transaction.ITransactionFactory - DTC transaction prepre
phase failed NHibernate.Exceptions.GenericADOException: could not
execute batch command.[SQL: SQL not available] --->
System.Data.SqlClient.SqlException: Cannot insert the value NULL into
column 'FloorPlanName', table
'Orchard.dbo.CommunityWebsiteSolutions_ApartmentPartRecord';
column does not allow nulls. INSERT fails.
Running SQL Profiler shows an insert with all columns being set to NULL.
Migrations.cs
SchemaBuilder.CreateTable(typeof(ApartmentPartRecord).Name, table => table
.ContentPartRecord()
.Column<string>("FloorPlanName", c => c.WithLength(25).NotNull())
.Column<string>("FullAddress", c => c.WithLength(256).NotNull()))
.Column<string>("ShortDescription", c => c.WithLength(150).NotNull())
.Column("NumberOfBedrooms", DbType.Int32, c => c.NotNull())
.Column("NumberOfBathrooms", DbType.Int32, c => c.NotNull())
.Column("SquareFootage", DbType.Int32, c => c.NotNull())
.Column("WhenAvailable", DbType.DateTime)
.Column("RentAmount", DbType.Decimal)
);
ContentDefinitionManager.AlterPartDefinition(typeof (ApartmentPart).Name, part => part.Attachable());
ApartmentPart
public class ApartmentPartRecord : ContentPartRecord {
public virtual string FloorPlanName { get; set; }
public virtual string ShortDescription { get; set; }
public virtual string FullAddress { get; set; }
public virtual int? NumberOfBedrooms { get; set; }
public virtual int? NumberOfBathrooms { get; set; }
public virtual int? SquareFootage { get; set; }
public virtual DateTime? WhenAvailable { get; set; }
public virtual decimal? RentAmount { get; set; }
}
public class ApartmentPart : ContentPart<ApartmentPartRecord> {
[Required, StringLength(256)]
[Display(Name = "Address / Unit Number")]
public string FullAddress {
get { return Record.FullAddress; }
set { Record.FullAddress = value; }
}
[Required, StringLength(25)]
[Display(Name = "Floor Plan")]
public string FloorPlanName {
get { return Record.FloorPlanName; }
set { Record.FloorPlanName = value; }
}
[Required, StringLength(150)]
[Display(Name = "Sales Description")]
public string ShortDescription {
get { return Record.ShortDescription; }
set { Record.ShortDescription = value; }
}
[Required]
[Display(Name = "Bedroom Count")]
public int? NumberOfBedrooms {
get { return Record.NumberOfBedrooms; }
set { Record.NumberOfBedrooms = value; }
}
[Required]
[Display(Name = "Bathroom Count")]
public int? NumberOfBathrooms {
get { return Record.NumberOfBathrooms; }
set { Record.NumberOfBathrooms = value; }
}
[Required]
[Display(Name = "Square Footage")]
public int? SquareFootage {
get { return Record.SquareFootage; }
set { Record.SquareFootage = value; }
}
[Display(Name = "First Availability")]
public DateTime? WhenAvailable {
get { return Record.WhenAvailable; }
set { Record.WhenAvailable = value; }
}
[Display(Name = "Rent Amount")]
public decimal? RentAmount {
get { return Record.RentAmount; }
set { Record.RentAmount = value; }
}
}
Driver
public class ApartmentPartDriver : ContentPartDriver<ApartmentPart>
{
protected override string Prefix
{
get { return "Apartment"; }
}
//GET
protected override DriverResult Editor(ApartmentPart part, dynamic shapeHelper)
{
return ContentShape("Parts_Apartment_Edit",
() => shapeHelper.EditorTemplate(
TemplateName: "Parts/Apartment",
Model: part,
Prefix: Prefix));
}
//POST
protected override DriverResult Editor(ApartmentPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
Handler
public class ApartmentPartHandler : ContentHandler {
public ApartmentPartHandler(IRepository<ApartmentPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
}
Your error message explains this pretty clearly:
System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'FloorPlanName', table 'Orchard.dbo.CommunityWebsiteSolutions_ApartmentPartRecord'; column does not allow nulls. INSERT fails.
Your problem occurs because:
You are using nullable types such as string and int? types in your Record class, which means you want to allow nulls.
Yet, you are specifying in your DB migration that you want to disallow nulls.
And when C# instantiates your Record class, it initializes the fields using the default value, which is null for nullable types.
You can do one of the following:
Make your DB columns nullable (remove NotNull)
Make your Record class use non-nullable types (for example, int instead of int?). Note that this is not an option for reference types such as string.
Give non-null default values to the fields of your Record class by giving the class a constructor. This is arguably bad practice since you will be calling virtual properties in a base class, but seems to be ok in NHibernate.
Give non-null default values to the fields of your Record class by giving your part an OnInitializing handler, which would be placed in your Handler class.
UPDATE
You commented that you are expecting the fields to be filled in by the TryUpdateModel in the Editor function of your driver class. This does eventually happen, but the actual sequence of events that occurs is this (you can see this in the CreatePOST method of Orchard.Core.Contents.Controllers.AdminController):
ContentManager.New() with the content type ID to create content item in memory. This step calls OnInitializing for the appropriate content parts for the content type, which are defined in handlers.
ContentManager.Create() with the content item in Draft Mode. This step actually tries to persist the item to the DB once.
ContentManager.UpdateEditor(). This is the call that actually calls Editor of the appropriate driver for the content type.
Check the ModelState and roll back the transaction if anything has failed.
Step 2 will fail if you have NULL values in columns marked NotNull, because the fields have default values at that point. For these columns, you have to fill them in before step 2 by using OnInitializing or by using a constructor on your Record part.
In other words, TryUpdateModel in your driver is actually applying changes directly to the entity that has already been Created and is now attached to the NHibernate session.
I'm trying to specify a unique column for an entity, using the Fluent NHibernate Automapper Override. For my test class of CodeType, I'd like to make the Type property unique. The goal would be for a "new CodeType()" being created with the same type field as a currently saved CodeType to be overlaid on top of the current entity.
I have the following CodeType class:
public class CodeType : SecurableEntity
{
public virtual string Type { get; set; }
public virtual string Description { get; set; }
/// <summary>
/// This is a placeholder constructor for NHibernate.
/// A no-argument constructor must be available for NHibernate to create the object.
/// </summary>
public CodeType() { }
}
I have the following CodeTypeMap Class:
public class CodeTypeMap : IAutoMappingOverride<CodeType>
{
public void Override(AutoMapping<CodeType> mapping)
{
//Doesn't work. Need a way to specify a column as unique.
mapping.Map(m => m.Type).Unique();
}
}
The override is applied to the AutoMap, through the following:
public AutoPersistenceModel Generate()
{
var mappings = AutoMap.AssemblyOf<User>(new AutomappingConfiguration());
mappings.IgnoreBase<Entity>();
mappings.IgnoreBase<SecurableEntity>();
mappings.IgnoreBase(typeof(EntityWithTypedId<>));
mappings.Conventions.Setup(GetConventions());
mappings.UseOverridesFromAssemblyOf<AutoPersistenceModelGenerator>();
mappings.UseOverridesFromAssemblyOf<UserMap>();
mappings.UseOverridesFromAssemblyOf<CodeMap>();
mappings.UseOverridesFromAssemblyOf<CodeTypeMap>();
return mappings;
}
I'd like the following code to update any existing record with "type" equal to "existingType".
SecurableEntityRepository<CodeType> ctr = new SecurableEntityRepository<CodeType>();
CodeType ct = new CodeType();
ct.type = "existingType";
ct = ctr.SaveOrUpdate(ct);
How can I make NHibernate key off of the type field as unique?
Is this possible?
short answer, what you want is something you have to handle in code because there are so many possibilities. Everytime you create a new CodeType you have to check the db if there is already one
SecurableEntityRepository<CodeType> ctr = new SecurableEntityRepository<CodeType>();
CodeType ct = ctr.GetByType("existingType");
if (ct == null)
{
ct = new CodeType { type = "existingType" };
}
ctr.SaveOrUpdate(ct);
or
SecurableEntityRepository<CodeType> ctr = new SecurableEntityRepository<CodeType>();
CodeType ct = ctr.GetByType("existingType");
if (ct != null)
{
ctr.Detach(ct);
ctr.Merge(new CodeType{ type = "existingType" });
}
or
SecurableEntityRepository<CodeType> ctr = new SecurableEntityRepository<CodeType>();
int ctId = ctr.GetIdByType("existingType");
if (ct != 0)
{
ctr.Merge(new CodeType{ Id = ctId, type = "existingType" });
}
and there are some things which can be written differently
public CodeType() { } can be removed or made protected CodeType() { } if not needed for your domain
public AutoPersistenceModel Generate()
{
return AutoMap.AssemblyOf<User>(new AutomappingConfiguration())
.IgnoreBase<Entity>()
.IgnoreBase<SecurableEntity>()
.IgnoreBase(typeof(EntityWithTypedId<>))
.Conventions.Setup(GetConventions())
.UseOverridesFromAssemblyOf<AutoPersistenceModelGenerator>();
}
I found this article on Context Variables in an earlier version of Ninject. My question is two-fold. First, how can I get this behavior with Ninject 2? Secondly, do context variables carry through down the request chain? For example, let's say I wanted to replace these calls:
var a = new A(new B(new C())));
var specialA = new A(new B(new SpecialC()));
... with this:
var a = kernel.Get<A>();
var specialA = kernel.Get<A>(With.Parameters.ContextVariable("special", "true"));
Is it possible to set up a binding like this, where the context remembers that it is in a "special" context when it comes time to construct a C?
Here's some stuff that I use against V2, with ~0 effort to clean it up for you - let me know if you can't disentagle it.
As you surmised, there doesn't seem to be a really explicit API that surfaces the "context parameter, even for nested resolutions" stuff in v2 as-is (it's presence is buried as the 3rd parameter on an overload of the Parameter ctor).
public static class ContextParameter
{
public static Parameter Create<T>( T value )
{
return new Parameter( value.GetType().FullName, value, true );
}
}
public static class ContextParameterFacts
{
public class ProductId
{
public ProductId( string productId2 )
{
Value = productId2;
}
public string Value { get; set; }
}
public class Repository
{
public Repository( ProductId productId )
{
ProductId = productId;
}
public ProductId ProductId { get; set; }
}
public class Outer
{
public Outer( Repository repository )
{
Repository = repository;
}
public Repository Repository { get; set; }
}
public class Module : NinjectModule
{
public override void Load()
{
Bind<ProductId>().ToContextParameter();
}
}
//[ Fact ]
public static void TwoDeepShouldResolve()
{
var k = new StandardKernel( new Module() );
var o = k.Get<Outer>( ContextParameter.Create( new ProductId( "a" ) ) );
Debug.Assert( "a" == o.Repository.ProductId.Value );
}
}
And here's some code [that'll confuse the matter] which demonstrates how I apply it in my context:-
public class ServicesNinjectModule : NinjectModule
{
public override void Load()
{
Bind<ProductId>().ToContextParameter();
Bind<Func<ProductId, ResourceAllocator>>().ToConstant( ( productId ) => Kernel.Get<ResourceAllocator>(
ContextParameter.Create( productId ) ) );
}
}
public static class NinjectContextParameterExtensions
{
public static IBindingWhenInNamedWithOrOnSyntax<T> ToContextParameter<T>( this IBindingToSyntax<T> bindingToSyntax )
{
return bindingToSyntax.ToMethod( context => (T)context.Parameters.Single( parameter => parameter.Name == typeof( T ).FullName ).GetValue( context ) );
}
}
As usual, you should go look a the source and the tests - they'll provide you with a far more detailed and relevant answer than I can.