Here's the (edited) domain class:
public class Patient : IntegerKeyEntity
{
...
public virtual string LastName
{
get
{
return Encoding.Unicode.GetString(encryptionService.Decrypt(LastNameEncrypted));
}
set
{
LastNameEncrypted = encryptionService.Encrypt(value);
}
}
/// <summary>
/// Contains the encrypted last name. Access via LastName for unencrypted version.
/// </summary>
public virtual byte[] LastNameEncrypted { get; set; }
...
}
Here's the Fluent mapping:
public class PatientMap : ClassMap<Patient>
{
public PatientMap()
{
...
Map(x => x.LastNameEncrypted, "LastName")
.Not.Nullable();
...
}
}
I don't map LastName in Patient. The table has a LastName column (a varbinary) and no LastNameEncrypted column.
Here's the query:
public virtual IQueryable<TResult> SatisfyingElementsFrom(IQueryable<T> candidates, int start, int limit, string sort, string dir)
{
if (this.MatchingCriteria != null)
{
return candidates.Where(this.MatchingCriteria).OrderBy(sort + " " + dir).Skip(start).Take(limit).ToList().ConvertAll(this.ResultMap).AsQueryable();
}
return candidates.ToList().ConvertAll(this.ResultMap).AsQueryable();
}
The return (inside the if block) is where the error is triggered. The error says "Could not resolve property LastName of Patient".
I am using NHibernate (v2.1.2.4000), Fluent NHibernate (v1.1.0.685) and NHibernate.Linq (v1.1.0.1001). I cannot update these DLLs.
Is it because I don't have a mapping for Patient.LastName? I don't have or need one. If that is the issue, how do I map/tell Fluent NHibernate to ignore that property?
PS: I am not using AutoMapping, only explicit mappings. They are loaded as follows. In my application, only cfg (an NHibernate.Cfg.Configuration object) and mappingAssemblies (which points to the one DLL with the mappings) have a value.
private static ISessionFactory CreateSessionFactoryFor(string[] mappingAssemblies, AutoPersistenceModel autoPersistenceModel, Configuration cfg, IPersistenceConfigurer persistenceConfigurer)
{
FluentConfiguration fluentConfiguration = Fluently.Configure(cfg);
if (persistenceConfigurer != null)
{
fluentConfiguration.Database(persistenceConfigurer);
}
fluentConfiguration.Mappings(m =>
{
foreach (var mappingAssembly in mappingAssemblies)
{
var assembly = Assembly.LoadFrom(MakeLoadReadyAssemblyName(mappingAssembly));
m.HbmMappings.AddFromAssembly(assembly);
m.FluentMappings.AddFromAssembly(assembly).Conventions.AddAssembly(assembly);
}
if (autoPersistenceModel != null)
{
m.AutoMappings.Add(autoPersistenceModel);
}
});
return fluentConfiguration.BuildSessionFactory();
}
This error happens when you do the query. Looking to your code I see only one thing that might cause the problem - that is MatchingCriteria:
return candidates.Where(this.MatchingCriteria)...
What type is stored in this.MatchingCriteria?
Try to replace it with inline conditions: in
..Where(<put_inline_criteria_here>)..
Related
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 have two tables, Locations and Facilities
They map to two classes,
public Location : Entity
{
//properties
}
public Facility : Entity
{
public virtual Location Location { get; set; }
}
Everything works just dandy, until I change facility to this
public Facility : Location
{
}
Now I get an exception from nHibernate saying
NHibernate.ADOException was unhandled by user code
Message=could not execute query
InnerException: System.Data.SqlClient.SqlException
Message=Invalid object name 'Facility'.
For some reason it is not creating the plural name of the table into the sql string.
Thanks for any help!
EDIT
This is my current TableNameConvention
public class TableNameConvention : IClassConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IClassInstance instance)
{
instance.Table(Inflector.Net.Inflector.Pluralize(instance.EntityType.Name));
}
}
When Facility inherits from Entity, the Facility does run through this method. When it inherits from Location, it does not
Edit 2
Figured I'd post everything...
public class AutoPersistenceModelGenerator : IAutoPersistenceModelGenerator
{
#region IAutoPersistenceModelGenerator Members
public AutoPersistenceModel Generate()
{
var mappings = new AutoPersistenceModel();
mappings.AddEntityAssembly(typeof(Person).Assembly).Where(GetAutoMappingFilter);
mappings.Conventions.Setup(GetConventions());
mappings.Setup(GetSetup());
mappings.IgnoreBase<Entity>();
mappings.IgnoreBase(typeof(EntityWithTypedId<>));
mappings.UseOverridesFromAssemblyOf<AutoPersistenceModelGenerator>();
return mappings;
}
#endregion
private Action<AutoMappingExpressions> GetSetup()
{
return c =>
{
c.FindIdentity = type => type.Name == "Id";
};
}
private Action<IConventionFinder> GetConventions()
{
return c =>
{
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.ForeignKeyConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.HasManyConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.HasManyToManyConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.ManyToManyTableNameConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.PrimaryKeyConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.ReferenceConvention>();
c.Add<BHP.DEC.Data.NHibernateMaps.Conventions.TableNameConvention>();
};
}
/// <summary>
/// Provides a filter for only including types which inherit from the IEntityWithTypedId interface.
/// </summary>
private bool GetAutoMappingFilter(Type t)
{
return t.GetInterfaces().Any(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(IEntityWithTypedId<>));
}
}
Have you set a convention?
public class TableNameConvention : IClassConvention
{
public void Apply(FluentNHibernate.Conventions.Instances.IClassInstance instance)
{
string typeName = instance.EntityType.Name;
instance.Table(Inflector.Net.Inflector.Pluralize(typeName));
}
}
This is an old question, but for the sake of others who stumble upon this looking for an answer, you can also create a convention that uses the built-in PluralizationService that comes with EF:
public class TableNameConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
string typeName = instance.EntityType.Name;
instance.Table(PluralizationService.CreateService(CultureInfo.CurrentCulture).Pluralize(typeName));
}
}
I am trying to write a naming strategy for NHibernate that will prefix table names based on what assembly the poco is defined in. Right now my strategy is just trying to append any prefix at all to the tables just to prove I have things wired up right.
The problem that I am encountering is that I am able to create my INamingStrategy and attach it to the NHibernate configuration object, but it never seems to get used. Here is some example coded:
private MsSqlConfiguration GetDatabaseConfiguration()
{
var configuration = MsSqlConfiguration.MsSql2008
.ConnectionString(ConfigFileReader.GetConnectionString(ConnectionStringKey))
.ShowSql();
return configuration;
}
private FluentConfiguration GetFluentConfiguration()
{
return Fluently.Configure().Database(GetDatabaseConfiguration())
.Mappings(m =>
{
foreach (var assembly in GetAssembliesToLoadMappingsFrom())
m.FluentMappings.AddFromAssembly(assembly);
});
}
public global::NHibernate.Cfg.Configuration GetNHibernateConfiguration()
{
var nHibernateConfiguration = GetFluentConfiguration().BuildConfiguration();
var namingStrategy = GetNamingStrategy();
if (namingStrategy != null)
nHibernateConfiguration.SetNamingStrategy(namingStrategy);
return nHibernateConfiguration;
}
public void Build()
{
var schemaExport = new SchemaExport(GetNHibernateConfiguration());
schemaExport.Create(true, true);
}
By placing a breakpoint on the return statement in GetNHibernateConfiguration(), I am able to confirm that nHibernateConfiguration.NamingStrategy contains a reference to my strategy. However, placing breakpoints in every one of the INamingStrategy implementing members of that strategy shows that non of them are ever called. This is confirmed by looking at the generated schema, which has no prefixes.
Likewise, using the same approach to create a session factory, shows that CRUD operations also ignore the strategy.
I am I missing something obvious?
I am using NHibernate 2.1.1.4000
I think your strategy is too complicated. If you are using FluentNHibertate just provide the TableName convention into your initialization.
e.q.:
public class TableNameConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
instance.Table(Inflector.Net.Inflector.Pluralize(instance.EntityType.Name));
}
}
and usage here:
public class AutoPersistenceModelGenerator : IAutoPersistenceModelGenerator
{
/// <summary>
/// Get Conf Setup
/// </summary>
/// <returns>
/// Action of AutoMappingExpressions
/// </returns>
private Action<AutoMappingExpressions> GetSetup()
{
return c =>
{
c.FindIdentity = type => type.Name == "Id";
c.IsBaseType = this.IsBaseTypeConvention;
};
}
private Action<IConventionFinder> GetConventions()
{
return c =>
{
c.Add<PrimaryKeyConvention>();
c.Add<ReferenceConvention>();
c.Add<HasManyConvention>();
c.Add<TableNameConvention>();
c.Add<PropertyNameConvention>();
};
}
public AutoPersistenceModel Generate()
{
var model =
new AutoPersistenceModel()
.AddEntityAssembly(Assembly.GetAssembly(typeof(User)))
.Where(
this.GetAutoMappingFilter).Conventions.Setup(this.GetConventions()).Setup(this.GetSetup()).
UseOverridesFromAssemblyOf<AutoPersistenceModelGenerator>();
return model;
}
My Domain auto mapping was working but now as I updated my NHibernate stack I'm getting mapping exception when Session Factory is building the Configuration:
"Can't figure out what the other side
of the many-to-many property 'Users'
should be."
The exception is thrown on a many to many map
The whole stack trace is this one:
at
FluentNHibernate.Cfg.FluentConfiguration.BuildConfiguration()
in
c:\hornget.horn\orm\fluentnhibernate\Working-2.1\src\FluentNHibernate\Cfg\FluentConfiguration.cs:line
119 at
WebApplication1.NHibernateManager.SessionFactoryManager.BuildConfiguration(AutoPersistenceModel
persistanceModel) in
C:\WebProgramming\Projects\WebApplication1\WebApplication1\NHibernateManager\SessionFactoryManager.cs:line
116 at
WebApplication1.NHibernateManager.SessionFactoryManager.GetSessionFactory()
in
C:\WebProgramming\Projects\WebApplication1\WebApplication1\NHibernateManager\SessionFactoryManager.cs:line
71 at
WebApplication1.NHibernateManager.SessionManager.CloseSession()
in
C:\WebProgramming\Projects\WebApplication1\WebApplication1\NHibernateManager\SessionManager.cs:line
47 at
WebApplication1.Global.Application_EndRequest(Object sender, EventArgs e) in
C:\WebProgramming\Projects\WebApplication1\WebApplication1\Global.asax.cs:line
36 at
System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at
System.Web.HttpApplication.ExecuteStep(IExecutionStep
step, Boolean& completedSynchronously)
The Question is how to handle the many to many relationship properly using automapping.
Note: the mapping was working before i updated the NHibernate/FluentNHibernate stack...
Relevant definitions are pasted below:
The Domain classes (in Dll 1):
public class User : Entity
{
// ... removed properties
public virtual IList<Role> Roles { get; protected set; }//has many
// ... removed methods
}
public class Role : Entity
{
// ... removed properties
public virtual IList<User> Users { get; protected set; }//has many
// ... removed methods
}
Entity class (in DLL 2):
/// <summary>
/// Base Entity deffinition
/// </summary>
public abstract class Entity : IEquatable<Entity>
{
private int _Id = 0;
public virtual int Id { get { return _Id; } }
//... removed methods
}
Conventions:
public class PrimaryKeyConvention : IIdConvention
{
public void Apply(IIdentityInstance instance)
{
instance.Column(instance.EntityType.Name + "Id");
instance.GeneratedBy.HiLo("100");
instance.Access.ReadOnlyPropertyThroughPascalCaseField(PascalCasePrefix.Underscore);
}
}
public class ClassConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
instance.Table(Inflector.Pluralize(instance.EntityType.Name));
instance.LazyLoad();
instance.Cache.NonStrictReadWrite();
}
}
Many to Many Convention:
public class ManyToManyConvention : IHasManyToManyConvention
{
public void Apply(IManyToManyCollectionInstance instance)
{
if (instance.OtherSide == null)
{
instance.Table(
string.Format(
"{0}To{1}",
Inflector.Pluralize(instance.EntityType.Name),
Inflector.Pluralize(instance.ChildType.Name)));
instance.Cascade.AllDeleteOrphan();
}
else
{
instance.Inverse();
}
}
}
Model:
var persistanceModel = AutoMap.AssemblyOf<DataModelPaceholder>()
.AddEntityAssembly(typeof(Entity).Assembly)
.Conventions.AddFromAssemblyOf<ClassConvention>()
.UseOverridesFromAssemblyOf<DataModelPaceholder>()
.Setup(s =>
{
s.SubclassStrategy = t => SubclassStrategy.JoinedSubclass;
s.IsComponentType = type => type == typeof(MoneyComponent);
})
.IgnoreBase<Entity>()
.IgnoreBase<EntityAudit>()
//.IncludeBase<Product>()
.Where
(
type => typeof(Entity).IsAssignableFrom(type) && !type.IsAbstract
);
Do you have a junction table named UsersToRoles with the appropriately named columns as per AutoMap default conventions? e.g. User_id,Role_id ?
I'm trying to get the AutoPersistence model to map several composite elements. However, it seems that either I end up mapping it as an entity, dropping down to manual mapping or it just doesn't plain work. Here's some code that demonstrates my problem:
using System;
using System.Collections.Generic;
using FluentNHibernate.AutoMap;
using FluentNHibernate.Cfg;
using FluentNHibernate.Conventions.Helpers;
using NHibernate.Cfg;
namespace Scanner {
public class Root {
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Component> Components { get; set; }
}
public class Component {
public string Name { get; set; }
}
class Example {
public void DoesntGetComponents()
{
Configuration configuration = new Configuration();
configuration.SetProperty("ConnectionString", "");
configuration.SetProperty("dialect", "NHibernate.Dialect.MsSql2005Dialect");
var config = Fluently.Configure(configuration)
.Mappings(m => m.AutoMappings.Add(AutoMapping))
.BuildConfiguration();
var sql2005 = new NHibernate.Dialect.MsSql2005Dialect();
foreach (var line in config.GenerateSchemaCreationScript(sql2005))
{
Console.WriteLine(line);
}
}
static AutoPersistenceModel AutoMapping() {
AutoPersistenceModel model = new AutoPersistenceModel();
return model
.AddEntityAssembly(typeof(Root).Assembly)
.WithSetup(e => e.IsComponentType = t => t == typeof(Component))
.Where(t => t == typeof(Root))
.MergeWithAutoMapsFromAssemblyOf<Root>()
.ConventionDiscovery.Add(ForeignKey.Format((p, t) => (p == null ? t.Name : p.Name) + "Id"))
.ConventionDiscovery.Add(Table.Is(t => t.EntityType.Name))
;
}
}
}
(Sorry it's so long, but it's the minimal code required to demonstrate the problem. This particular version of the code fails to register the component type at all.
So, what am I doing wrong?
It seems that the component in itself is not the problem, but the mapping of a collection of components. If you would map the component directly onto the Root class, this would not be any problem.
A possible workaround is making the Component class an entity (adding an ID) and overriding the mapping of Components to cascade + auto delete orphans:
AutoPersistenceModel
.ForTypesThatDeriveFrom<Root>(map => map.HasMany(root => root.Components).Cascade.AllDeleteOrphan())