I need a way to query in Nhibernate for items that have a Dictionary Property containing value.
Assume:
public class Item
{
public virtual IDictionary<int, string> DictionaryProperty {get; set;}
}
and mapping:
public ItemMap()
{
HasMany(x => x.DictionaryProperty)
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore)
.AsMap<string>(
index => index.Column("IDNumber").Type<int>(),
element => element.Column("TextField").Type<string>().Length(666)
)
.Cascade.AllDeleteOrphan()
.Fetch.Join();
}
I want to query all Items that have a dictionary value of "SomeText". The following example in Linq fails:
session.Query<Item>().Where(r => r.DictionaryProperty.Any(g => g.Value == "SomeText"))
with error
cannot dereference scalar collection element: Value
So is there any way to achieve that in NHibernate? Linq is not an exclusive requirement but its preffered. Not that I'm not interested to query over dictionary keys that can be achieved using .ContainsKey . Φορ this is similar but not the same
Handling IDictionary<TValueType, TValueType> would usually bring more issues than advantages. One way, workaround, is to introduce a new object (I will call it MyWrapper) with properties Key and Value (just an example naming).
This way we have to 1) create new object (MyWrapper), 2) adjust the mapping and that's it. No other changes... so the original stuff (mapping, properties) will work, because we would use different (readonly) property for querying
public class MyWrapper
{
public virtual int Key { get; set; }
public virtual string Value { get; set; }
}
The Item now has
public class Item
{
// keep the existing for Insert/Updae
public virtual IDictionary<int, string> DictionaryProperty {get; set;}
// map it
private IList<MyWrapper> _properties = new List<MyWrapper>();
// publish it as readonly
public virtual IEnumerable<MyWrapper> Properties
{
get { return new ReadOnlyCollection<MyWrapper>(_properties); }
}
}
Now we will extend the mapping:
HasMany(x => x.Properties)
.Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore)
.Component(c =>
{
c.Map(x => x.Key).Column("IDNumber")
c.Map(x => x.Value).Column("TextField")
})
...
;
And the Query, which will work as expected:
session
.Query<Item>()
.Where(r =>
r.Properties.Any(g => g.Value == "SomeText")
)
NOTE: From my experience, this workaround should not be workaround. It is preferred way. NHibernate supports lot of features, but working with Objects brings more profit
I am trying to configure op-locking using fluent nhibernate.
There is a lot of info out there but none seems to fit the scenario I'm in. My class and map are as follows (edited for brevity):
Entity:
public class EmailGroup : CRUDDomainObject<EmailGroup>
{
public virtual string Id { get; set; }
public virtual MailServer Server { get; set;}
public virtual string FromAddress { get; set;}
public virtual string ToAddress { get; set;}
public virtual long Version { get; set; }
}
Map:
public class EmailGroupMap : ClassMap<EmailGroup>
{
public const string TABLE_ID = "EMAILGROUP";
public const string FIELD_ID = "EMAILID";
public const string FIELD_MAIL_SERVER = "MAILSERVID";
public const string FIELD_FROM_ADDRESS = "EMLFROM";
public const string FIELD_TO_ADDRESS = "EMLTO";
public const string FIELD_VERSION = "VERSION";
public EmailGroupMap()
{
Table(TABLE_ID);
Id(x => x.Id)
.Column(FIELD_ID)
.Not.Nullable()
.GeneratedBy.Assigned()
.Length(12);
References(x => x.Server)
.Column(FIELD_MAIL_SERVER)
.NotFound.Ignore();
Map(x => x.FromAddress)
.Column(FIELD_FROM_ADDRESS)
.Not.Nullable()
.Length(120);
Map(x => x.ToAddress)
.Column(FIELD_TO_ADDRESS)
.Not.Nullable()
.Length(1000);
Version(X => X.Version)
.Column(FIELD_VERSION)
.Generated.Always()
.UnsavedValue("0")
.Access.Property();
DynamicUpdate();
OptimisticLock.Version();
}
}
All looks well to me here, but when I load the entity and modify it, the version number is not incremented. Likewise if I manually increment the version, while a session is open, I get no StaleObjectException.
Does this config look valid to the more experienced eye? If so what else could I be missing?
UPDATE:
After implementing a database managed timestamp the version column is (of course) being incremented. However NHibernate doesn't treat the row as optimistically locked. I captured the update query from the SQL server to check the where clause (truncated for brevity):
exec sp_executesql N'UPDATE [EMAILGROUP]
SET [EMLDESC] = #EMLDESC, [MAILSERVID] = #MAILSERVID, [EMLFROM] = #EMLFROM, [EMLTO] = #EMLTO, [EMLCC] = #EMLCC, [EMLBCC] = #EMLBCC
WHERE [EMAILID] = #EMAILID'
Why did you specify Generated.Always()? That tells NHibernate that this isn't a real column but instead calculated by the database. Documentation: http://nhibernate.info/doc/nh/en/index.html#mapping-generated
Remove that and it should work.
The most typical scneario for Version and SQL Server (not sure if this is your case) is the sql type timestamp (obsolete) or better rowversion. This should be mapped to C# byte[]. So these changes should solve it:
1) Version column on the server must be of type rowversion (or timestamp). Such a column is automatically updated on any changes related to current row. only one such column can exist per table
2) The entity should look like this
public class EmailGroup : CRUDDomainObject<EmailGroup>
{
...
public virtual byte[] Version { get; set; }
3) the fluent mapping code should remain as it is. It should be a job of a fluent mapper to do the tricks behind. what we need to achieve is something like this:
<version name="Version" generated="always" unsaved-value="null" type="BinaryBlob">
<column name="Version" not-null="false" sql-type="timestamp"/>
</version>
Please, see more here: http://ayende.com/blog/3946/nhibernate-mapping-concurrency
I've scoured Google and SO but haven't come across anyone having the same problem. Here is my model:
public class Hierarchy
{
public virtual Event Prerequisite { get; set; }
public virtual Event Dependent { get; set; }
public override bool Equals(object obj)
{
var other = obj as Hierarchy;
if (other == null)
{
return false;
}
else
{
return this.Prerequisite == other.Prerequisite && this.Dependent == other.Dependent;
}
}
public override int GetHashCode()
{
return (Prerequisite.Id.ToString() + "|" + Dependent.Id.ToString()).GetHashCode();
}
}
Here is my mapping:
public class HierarchyMap : ClassMap<Hierarchy>
{
public HierarchyMap()
{
CompositeId()
.KeyReference(h => h.Prerequisite, "PrerequisiteId")
.KeyReference(h => h.Dependent, "DependentId");
}
}
And here is the ever present result:
{"The entity 'Hierarchy' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id)."}
Is there some special configuration I need to do to enable composite id's? I have the latest FNh (as of 6/29/2012).
Edit
I consider the question open even though I've decided to map an Id and reference the 2 Event's instead of using a CompositeId. Feel free to propose an answer.
I figured out this was due to auto mapping trying to auto map the ID
Even though i had an actual map for my class - it still tried to auto map the ID. Once i excluded the class from auto mapping, it worked just fine.
I'm using Fluent NHibernate's AutoMap feature to map my entities. Most of my entities inherit from a base class Entity which has a property public IList<Tag> Tags.
The tags are in a separate table in the database, so I use a many-to-many relation. But Fluent NHibernate creates mappings for a one-to-many relation.
I'd like to write a convention to override these mappings to use HasManyToMany(...) if the class inherits from Entity. Is this possible and how?
The convention could either rely on the property's type or its name.
Some code for illustration:
// entities
public class Entity
{
public virtual int Id { get; set; }
// ... some other properties
public virtual IList<Tag> { get; set; }
}
public class Tag
{
public virtual int Id { get; set; }
public virtual string TagName { get; set; }
}
public class Event : Entity
{
// ... some properties
}
// Fluent NHibernate configuration
public static ISessionFactory CreateSessionFactory()
{
var config = new CustomAutomappingConfiguration();
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("Sql")))
.Mappings(m =>
{
m.AutoMappings.Add(AutoMap.AssemblyOf<Event>(config)
.IgnoreBase<Entity>()
.Conventions.Add<CustomForeignKeyConvention>()
.Conventions.Add<CustomManyToManyTableNameConvention>();
})
.BuildSessionFactory();
}
I don't think you can accomplish the mapping with conventions. However, if you want to keep one linking table between the entities and tags, you can do the following:
m.AutoMappings.Add(AutoMap.AssemblyOf<Event>(config)
.IncludeBase<Entity>()
.Override<Entity>(map =>
map.HasManyToMany(e => e.Tags)
.Inverse()
.Cascade.SaveUpdate()));
Notice that I changed IgnoreBase<Entity>() to IncludeBase<Entity>(). This will add an Entity table, but will keep one linking table. With this mapping, you will get the following table DDL:
create table [Entity] (
Id INT IDENTITY NOT NULL,
primary key (Id)
)
create table TagToEntity (
Entity_id INT not null,
Tag_id INT not null
)
create table Event (
Entity_id INT not null,
primary key (Entity_id)
)
create table [Tag] (
Id INT IDENTITY NOT NULL,
TagName NVARCHAR(255) null,
primary key (Id)
)
alter table TagToEntity
add constraint FKD7554554A8C4CA9
foreign key (Tag_id)
references [Tag]
alter table TagToEntity
add constraint FKD75545564C9EC79
foreign key (Entity_id)
references [Entity]
alter table Event
add constraint FKA2FD7DF664C9EC79
foreign key (Entity_id)
references [Entity]
If you choose to do an Override<> per subclass, you will have a linking table per subclass.
In my case, I wanted to use an attribute to indicate a property that should participate in a many-to-many relationship where only one side of the relationship is declared. You could easily modify this to map by other conventions.
Many-to-many relationships are handled by FluentNHibernate.Automapping.Steps.HasManyToManyStep, an IAutomappingStep returned by the DefaultAutomappingConfiguration. This step will only map a property if it discovers a corresponding property of the related type (so both ends of the many-to-many relationship have to be declared).
The approach I've taken is to:
Create a decorator class for HasManyToManyStep that supports detecting and mapping many-to-many properties based on the presence of an attribute (or some other convention)
Create a class derived from DefaultAutomappingConfiguration to when automapping and override GetMappingSteps, wrapping any instance of HasManyToManyStep with the decorator
Here's the decorator, which tries to use the default HasManyToManyStep functionality first. Otherwise, if HasManyToManyAttribute is defined for the member, it will also create the relationship. The code used to create the relationship is nearly identical to the code used by HasManyToManyStep - just without reference to the other side of the relationship.
class ExplicitHasManyToManyStep : IAutomappingStep
{
readonly IAutomappingConfiguration Configuration;
readonly IAutomappingStep DefaultManyToManyStep;
public ExplicitHasManyToManyStep(IAutomappingConfiguration configuration, IAutomappingStep defaultManyToManyStep)
{
Configuration = configuration;
DefaultManyToManyStep = defaultManyToManyStep;
}
#region Implementation of IAutomappingStep
public bool ShouldMap(Member member)
{
if (DefaultManyToManyStep.ShouldMap(member))
{
return true;
}
//modify this statement to check for other attributes or conventions
return member.MemberInfo.IsDefined(typeof(HasManyToManyAttribute), true);
}
public void Map(ClassMappingBase classMap, Member member)
{
if (DefaultManyToManyStep.ShouldMap(member))
{
DefaultManyToManyStep.Map(classMap, member);
return;
}
var Collection = CreateManyToMany(classMap, member);
classMap.AddCollection(Collection);
}
#endregion
CollectionMapping CreateManyToMany(ClassMappingBase classMap, Member member)
{
var ParentType = classMap.Type;
var ChildType = member.PropertyType.GetGenericArguments()[0];
var Collection = CollectionMapping.For(CollectionTypeResolver.Resolve(member));
Collection.ContainingEntityType = ParentType;
Collection.Set(x => x.Name, Layer.Defaults, member.Name);
Collection.Set(x => x.Relationship, Layer.Defaults, CreateManyToMany(member, ParentType, ChildType));
Collection.Set(x => x.ChildType, Layer.Defaults, ChildType);
Collection.Member = member;
SetDefaultAccess(member, Collection);
SetKey(member, classMap, Collection);
return Collection;
}
void SetDefaultAccess(Member member, CollectionMapping mapping)
{
var ResolvedAccess = MemberAccessResolver.Resolve(member);
if (ResolvedAccess != Access.Property && ResolvedAccess != Access.Unset)
{
mapping.Set(x => x.Access, Layer.Defaults, ResolvedAccess.ToString());
}
if (member.IsProperty && !member.CanWrite)
{
mapping.Set(x => x.Access, Layer.Defaults, Configuration.GetAccessStrategyForReadOnlyProperty(member).ToString());
}
}
static ICollectionRelationshipMapping CreateManyToMany(Member member, Type parentType, Type childType)
{
var ColumnMapping = new ColumnMapping();
ColumnMapping.Set(x => x.Name, Layer.Defaults, childType.Name + "_id");
var Mapping = new ManyToManyMapping {ContainingEntityType = parentType};
Mapping.Set(x => x.Class, Layer.Defaults, new FluentNHibernate.MappingModel.TypeReference(childType));
Mapping.Set(x => x.ParentType, Layer.Defaults, parentType);
Mapping.Set(x => x.ChildType, Layer.Defaults, childType);
Mapping.AddColumn(Layer.Defaults, ColumnMapping);
return Mapping;
}
static void SetKey(Member property, ClassMappingBase classMap, CollectionMapping mapping)
{
var ColumnName = property.DeclaringType.Name + "_id";
var ColumnMapping = new ColumnMapping();
ColumnMapping.Set(x => x.Name, Layer.Defaults, ColumnName);
var Key = new KeyMapping {ContainingEntityType = classMap.Type};
Key.AddColumn(Layer.Defaults, ColumnMapping);
mapping.Set(x => x.Key, Layer.Defaults, Key);
}
}
HasManyToManyAttribute class, because there is no other convention I can easily rely on in my case:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class HasManyToManyAttribute : Attribute
{
}
Configuration class derived from DefaultMappingConfiguration class:
class AutomappingConfiguration : DefaultAutomappingConfiguration
{
public override IEnumerable<IAutomappingStep> GetMappingSteps(AutoMapper mapper, IConventionFinder conventionFinder)
{
return base.GetMappingSteps(mapper, conventionFinder).Select(GetDecoratedStep);
}
IAutomappingStep GetDecoratedStep(IAutomappingStep step)
{
if (step is HasManyToManyStep)
{
return new ExplicitHasManyToManyStep(this, step);
}
return step;
}
}
I try to write a (fluent) mapping against an interface
public interface IOrderDiscount : IDomainObject<long>
where
public interface IDomainObject<IdT> : IDomainObject
{
IdT Id { get; }
}
like so (and all other thinkable varieties of access strategies)
Id(d => d.Id, "DiscountId")
.GeneratedBy.HiLo("9")
.WithUnsavedValue(0)
.Access.AsReadOnlyPropertyThroughCamelCaseField();
but all I get are variations of
Could not find field 'id' in class 'IOrderDiscount'
My base class implements this as
public virtual IdT Id { get; protected set; }
but event using a backing field does not change a thing.
So I am left to wonder, how I could get this to work...
Anyone with an idea?
Specify the custom column name via the Column method instead:
Id(d => d.Id)
.Column("DiscountId")
.GeneratedBy.HiLo("9")
.WithUnsavedValue(0)
.Access.AsReadOnlyPropertyThroughCamelCaseField();