How do I map the collection of Parts using a convention?
public class Part
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
public class Car
{
private readonly List<Part> _parts = new List<Part>();
public virtual int Id { get; set; }
public virtual IList<Part> Parts
{
get { return _parts.AsReadOnly(); }
}
}
I have tried this convention but it always expects the field name without an underscore prefix:
public class HasManyConvention : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
instance.Access.ReadOnlyPropertyThroughCamelCaseField(CamelCasePrefix.Underscore);
}
}
I've tried it with the 1.2.0.694 and 2.0.0.698 builds with the same result:
"Could not find field 'parts' in class 'TestFluentNHibernate.Car'"
First of all, your _parts member can't be read-only. NHibernate needs write access to the member to set the value/reference. To return a truly read-only collection through the Parts property you have to return a System.Collections.ObjectModel.ReadOnlyCollection. This also "removes" all the methods found in the read/write collection types that are still there if you just return f.ex. list.AsReadOnly(). A read-only list returned in this way still have .Add() method and others to edit the collection, but they will cause a runtime exception so returning a ReadOnlyCollection to begin with is a great for preventing this possibility.
A lot of people seem to like returning an IEnumerable which is also read-only, but it can be cast to a List or other read/write collection type and changed that way.
You have to implement AddPart and RemovePart methods to allow external code to add and remove items to the read-only collection.
Your convention looks correct, I'm using the exact same syntax with 1.2.0.694 successfully, my convention:
instance.Key.Column(instance.EntityType.Name + "Fk");
instance.Fetch.Subselect();
instance.Inverse();
instance.Cascade.All();
instance.Access.ReadOnlyPropertyThroughCamelCaseField(CamelCasePrefix.Underscore);
Example class:
public class Car
{
private List<Part> _parts;
public Car()
{
// Initialize member collection _parts
_parts = new List<Part>();
}
public virtual int Id { get; set; }
// Return readonlycollection here, ReadOnlyCollection implements
// IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable,
// so the returned list can be any collection type inheriting from these base types.
public ReadOnlyCollection<Part> Parts
{
get { return new List<Part>(_parts).AsReadOnly(); } }
}
public AddPart(Part part)
{
// Usually I don't want duplicates
if (!_parts.Contains(part))
_parts.Add(part);
}
}
Hopefully this helps. Try...
public class Car
{
protected virtual List<Part> _parts { get;set; }
public Car()
{
// Initialize "Parts" to not be null
this.Parts = new List<Part>();
}
public void PopulateParts (int someID)
{
// Do a call out to one of your helpers to populate "Parts"
// so it can be used.
//this.Parts = SomeCall(someID);
}
public virtual int Id { get; set; }
public virtual IList<Part> Parts
{
get { return this._parts; } // No need for "ReadOnly" since "protected set" will prevent changes outside of this class.
protected set { this._parts = value; }
}
}
Protected set is going to make this a read only element outside of the Car class.
Since I don't see how you are going to populate Parts, I created a call "PopulateParts" as a hook so you can populate the List.