FluentNHibernate and custom TableNameConvention - fluent-nhibernate

FluentNHibernate version 1.3.0.727
I have the following custom TableNameConvention:
public class TableNameConvention : IClassConvention, IClassConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
{
criteria.Expect(x => x.TableName, Is.Not.Set);
}
public void Apply(IClassInstance instance)
{
instance.Table(instance.EntityType.Name + "s");
}
}
I have the following entity mapping:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.Email).Not.Nullable().Length(200);
Map(x => x.FirstName).Length(100);
Map(x => x.LastName).Length(100);
Map(x => x.Password).Not.Nullable().Length(30);
}
}
I'm generating database like this:
var configuration = Fluently.Configure()
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<IEntity>()
.Conventions.Add<TableNameConvention>())
.BuildConfiguration();
var schema = new SchemaExport(configuration);
schema.Drop(false, true);
schema.Create(false, true);
Then generating database User entity table is still generated as User but not Users as I want it to be. It seams that Accept method fails. Is this FluentNHibernate bug?

Remove this code
public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
{
criteria.Expect(x => x.TableName, Is.Not.Set);
}
because it is not needed and maybe even not what you want. FNH has three values internally for each property (e.g. tablename)
value set explicitly in classmap
value set by convention
defaultvalue
and it uses what it finds in this order valueInEffect = explicitValue ?? conventionValue ?? defaultValue

Related

Method FindByLoginAsync doesn't work correctly with AspNetCore.Identity

I have used the AspNetCore.Identity in my Asp.Net Core application and I want to call the method called FindByLoginAsync but result is always NULL.
Versions:
Microsoft.AspNetCore.Identity.EntityFrameworkCore (1.1.1)
Microsoft.AspNetCore.Identity (1.1.1)
Code:
var loginProvider = "Github"
var providerKey = "1234567";
var user = await _userManager.FindByLoginAsync(loginProvider, providerKey);
This record exists in the database, but this method returns always NULL.
I've tried trace the SQL query and I've got this:
exec sp_executesql N'SELECT TOP(1) [e].[ProviderKey], [e].[LoginProvider], [e].[ProviderDisplayName], [e].[UserId]
FROM [UserLogins] AS [e]
WHERE ([e].[ProviderKey] = #__get_Item_0) AND ([e].[LoginProvider] = #__get_Item_1)',N'#__get_Item_0 nvarchar(450),#__get_Item_1 nvarchar(450)',#__get_Item_0=N'Github',#__get_Item_1=N'1234567'
My SQL query is like [e].[LoginProvider] the value providerKey and [e].[ProviderKey] the value loginProvider.
Application DbContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUser>(i =>
{
i.ToTable("Users");
i.HasKey(x => x.Id);
});
builder.Entity<ApplicationRole>(i =>
{
i.ToTable("Roles");
i.HasKey(x => x.Id);
});
builder.Entity<IdentityUserRole<int>>(i =>
{
i.ToTable("UserRoles");
i.HasKey(x => new { x.RoleId, x.UserId });
});
builder.Entity<IdentityUserLogin<int>>(i =>
{
i.ToTable("UserLogins");
i.HasKey(x => new { x.ProviderKey, x.LoginProvider });
});
builder.Entity<IdentityRoleClaim<int>>(i =>
{
i.ToTable("RoleClaims");
i.HasKey(x => x.Id);
});
builder.Entity<IdentityUserClaim<int>>(i =>
{
i.ToTable("UserClaims");
i.HasKey(x => x.Id);
});
builder.Entity<IdentityUserToken<int>>(i =>
{
i.ToTable("UserTokens");
i.HasKey(x => x.UserId);
});
}
}
Implementation of IdentityUser, IdentityRole
public class ApplicationUser : IdentityUser<int>
{
}
public class ApplicationRole : IdentityRole<int>
{
}
How can I fix this? How is this behaviour possible?
You have incorrect order of primary keys in registration of entity IdentityUserLogin. Change it to this
builder.Entity<IdentityUserLogin<int>>(i =>
{
i.ToTable("UserLogins");
i.HasKey(x => new { x.LoginProvider, x.ProviderKey });
});
That's the fix, now the rationale behind.
In version 1.1.1 the method UserStore.FindByLoginAsync used method DbSet.FindAsync, which accepts ordered array of values for primary keys. The order must follow the order used in entity registration.
public async virtual Task<TUser> FindByLoginAsync(string loginProvider, string providerKey,
CancellationToken cancellationToken = default(CancellationToken))
{
...
var userLogin = await UserLogins.FindAsync(new object[] { loginProvider, providerKey }, cancellationToken);
...
}
In the default implementation the primary keys are registered in correct order
builder.Entity<TUserLogin>(b =>
{
b.HasKey(l => new { l.LoginProvider, l.ProviderKey });
b.ToTable("AspNetUserLogins");
});

Error using Autofac.Extras.NHibernate

I'm trying to inject dependencies im my models that NHibernate creates.
What I'm trying to do is the same here: http://fabiomaulo.blogspot.com.br/2008/11/entities-behavior-injection.html
But my container is Autofac.
So, I've found https://www.nuget.org/packages/Autofac.Extras.NHibernate/
I saw the post http://chadly.net/2009/05/dependency-injection-with-nhibernate-and-autofac/ that I've think is the origin of the Autofac.Extras.NHibernate.
My problem is that the code in Autofac.Extras.NHibernate and described in Chad post are different.
Looking at the source code I (think) figured out how to set the BytecodeProvider using:
Cfg.Environment.BytecodeProvider = new AutofacBytecodeProvider(Container, new DefaultProxyFactoryFactory(), new DefaultCollectionTypeFactory());
But now, I'm getting an exception when I tried to retrieve data from database:
[PropertyAccessException: could not set a property value by reflection setter of NHibernate.Autofac2.App_Start.Model.User.Id]
If I comment the line where I set BytecodeProvider the code works.
I created a POC to simulate:
My model:
public class User
{
private readonly ISomeService _someService;
public User(ISomeService someService)
{
this._someService = someService;
}
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual string GetTranslate
{
get { return this._someService != null ? this._someService.T(this.Name) : " No Translate" + this.Name; }
}
}
My mapping:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id);
Map(x => x.Name)
.Length(16)
.Not.Nullable();
}
}
Creation of the Autofac container and SessionFactory using Fluent Nhibernate:
// Create your builder.
var builder = new ContainerBuilder();
builder.RegisterType<SomeService>().As<ISomeService>();
builder.RegisterType<User>().As<IUser>();
Container = builder.Build();
SessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString("Data Source=(local);Initial Catalog=NHibernate.Autofac;User ID=test;Password=102030;Pooling=True"))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<MvcApplication>())
.ExposeConfiguration(config => config.Properties.Add("use_proxy_validator", "false"))
.ExposeConfiguration(config =>
{
//config.Properties.Add("proxyfactory.factory_class", "");
Cfg.Environment.BytecodeProvider = new AutofacBytecodeProvider(Container, new DefaultProxyFactoryFactory(), new DefaultCollectionTypeFactory());
new SchemaExport(config).Drop(false, false);
new SchemaExport(config).Create(false, true);
})
.BuildSessionFactory();
Well, I've found a solution that works for me.
Now, I'm using NHibernate.DependencyInjection.
The IEntityInjector implemenation:
public class EntityInjector : IEntityInjector
{
private readonly IContainer _container;
public EntityInjector(IContainer container)
{
_container = container;
}
public object[] GetConstructorParameters(System.Type type)
{
var constructor = type.GetConstructors().FirstOrDefault();
if (constructor != null)
return constructor.GetParameters().Select(a => a.ParameterType).Select(b => this._container.Resolve(b)).ToArray();
return null;
}
}
And in Global.asax:
Initializer.RegisterBytecodeProvider(new EntityInjector(Container));
SessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString("Data Source=(local);Initial Catalog=NHibernate.Autofac;User ID=XXX;Password=XXXX;Pooling=True"))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<MvcApplication>())
.ExposeConfiguration(config => config.Properties.Add("use_proxy_validator", "false"))
.ExposeConfiguration(config =>
{
new SchemaExport(config).Drop(false, false);
new SchemaExport(config).Create(false, true);
})
.BuildSessionFactory();

nhibernate 3.0 optimistic lock and related entities

I've noticed in my database (SQL Server) that the column I'm using for versioning is getting updated for an entity whenever that entity is added to a related table.
So, for example, if I have a Person entity and I add a Person to a Club, the Person's record in the database will have a new version. Of course, in the database, it's the Club's record that is added/updated with the Person's primary key.
I'm a bit surprised by that behavior because no other data for the Person entity is changing.
My question is if there is a way to turn that off; that is, to only have the version column updated when data in that row changes. What might the implications of that kind of configuration be?
EDIT Here's the real code
Model
public partial class ObligationProgramFund : IMyObject
{
private System.Int32 _id;
// ... many other properties
private IList<ObligationProgramFundOrganization> _obligationPFOrganizations;
private System.Byte[] _recordVersion;
public ObligationProgramFund() : base()
{
_obligationPFOrganizations = new List<ObligationProgramFundOrganization>();
}
public virtual System.Int32 Id
{
get { return _id; }
set { _id = value; }
}
public virtual IList<ObligationProgramFundOrganization> ObligationPFOrganizations
{
get { return _obligationPFOrganizations; }
set { _obligationPFOrganizations = value; }
}
public virtual System.Byte[] RecordVersion
{
get { return _recordVersion; }
set { _recordVersion = value; }
}
}
public partial class ObligationProgramFundOrganization : IMyObject
{
private ObligationProgramFund _obligationProgramFund;
private System.Int32 _id;
private Organization _organization;
private System.Byte[] _recordVersion;
// .. other properties
public ObligationProgramFundOrganization() : base()
{
}
public virtual System.Int32 Id
{
get { return _id; }
set { _id = value; }
}
public virtual System.Byte[] RecordVersion
{
get { return _recordVersion; }
set { _recordVersion = value; }
}
public virtual ObligationProgramFund ObligationProgramFund
{
get { return _obligationProgramFund; }
set { _obligationProgramFund = value; }
}
public virtual Organization Organization
{
get { return _organization; }
set { _organization = value; }
}
}
public partial class Organization : IMyObject
{
private IList<ObligationProgramFundOrganization> _obligationPFOrganizations;
private System.Int32 _id;
private System.Byte[] _recordVersion;
// other properties
public Organization() : base()
{
_obligationPFOrganizations = new List<ObligationProgramFundOrganization>();
}
public virtual System.Int32 Id
{
get { return _id; }
set { _id = value; }
}
public virtual System.Byte[] RecordVersion
{
get { return _recordVersion; }
set { _recordVersion = value; }
}
public virtual IList<ObligationProgramFundOrganization> ObligationPFOrganizations
{
get { return _obligationPFOrganizations; }
set { _obligationPFOrganizations = value; }
}
}
Mapping
public partial class ObligationProgramFundMap : ClassMap<ObligationProgramFund>
{
public ObligationProgramFundMap()
{
Table("[MySchema2].[dbo].[ObligationProgramFund]");
OptimisticLock.Version();
DynamicUpdate();
LazyLoad();
Id(x=>x.Id)
.Access.CamelCaseField(Prefix.Underscore)
.Column("[Id]")
.GeneratedBy.Identity();
Version(x=>x.RecordVersion)
.Access.CamelCaseField(Prefix.Underscore)
.Column("[RecordVersion]")
.CustomSqlType("timestamp")
.Not.Nullable()
.UnsavedValue("null")
.CustomType("BinaryBlob")
.Generated.Always();
// other properties/components
HasMany(x=>x.ObligationPFOrganizations)
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.AllDeleteOrphan()
.Fetch.Select()
.Inverse()
.LazyLoad()
.KeyColumns.Add("ObligationProgramFundId");
}
}
public partial class ObligationProgramFundOrganizationMap : ClassMap<ObligationProgramFundOrganization>
{
public ObligationProgramFundOrganizationMap()
{
Table("[MySchema2].[dbo].[ObligationProgramFundOrganization]");
OptimisticLock.Version();
DynamicUpdate();
LazyLoad();
Id(x=>x.Id)
.Access.CamelCaseField(Prefix.Underscore)
.Column("[Id]")
.GeneratedBy.Identity();
Version(x=>x.RecordVersion)
.Access.CamelCaseField(Prefix.Underscore)
.Column("[RecordVersion]")
.CustomSqlType("timestamp")
.Not.Nullable()
.UnsavedValue("null")
.CustomType("BinaryBlob")
.Generated.Always();
// other properties
References(x=>x.ObligationProgramFund)
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.SaveUpdate()
.Fetch.Select()
.Columns("ObligationProgramFundId");
References(x=>x.Organization)
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.SaveUpdate()
.Fetch.Select()
.Columns("OrganizationId");
}
}
public partial class OrganizationMap : ClassMap<Organization>
{
public OrganizationMap()
{
Table("[MySchema2].[dbo].[Organization]");
OptimisticLock.Version();
DynamicUpdate();
LazyLoad();
Id(x=>x.Id)
.Access.CamelCaseField(Prefix.Underscore)
.Column("[Id]")
.GeneratedBy.Identity();
Version(x=>x.RecordVersion)
.Access.CamelCaseField(Prefix.Underscore)
.Column("[RecordVersion]")
.CustomSqlType("timestamp")
.Not.Nullable()
.UnsavedValue("null")
.CustomType("BinaryBlob")
.Generated.Always();
// other properties
HasMany(x=>x.ObligationPFOrganizations)
.Access.CamelCaseField(Prefix.Underscore)
.Cascade.AllDeleteOrphan()
.Fetch.Select()
.Inverse()
.LazyLoad()
.KeyColumns.Add("OrganizationId");
}
}
So my entities are
Organization 1 - ∞ ObligationProgramFundOrganization ∞ - 1 ObligationProgramFund
The issue I've observed is when a new ObligationProgramFundOrganization is created (the associative table), the record version for ObligationProgramFund is updated.
The actual senario is that I have a table of ObligationProgramFundOrganizations on the ObligationProgramFund edit form. A ObligationProgramFundOrganization is added via AJAX calls. When I go to then save the ObligationProgramFund, I get a concurrency exception. I am sure no other user actually edited the ObligationProgramFund as it's all running locally.
(I've posted an answer to a similar question: Why NHibernate UPDATE reference entity?)
You have version column specified. Which means that any property change (even collection) trigger version update.
In order to prevent a certain property/collection to change version, optimistic-lock="false" property should be set in xml mapping.

QueryOver<A>().Where(a => a.B.Count() > 0) does not work

I get an exception: Unrecognised method call in epression a.B.Count() when I run:
var query = session.QueryOver<A>()
.Where(a => a.B.Count() > 0)
.List();
The following code works:
var query1 = session.QueryOver<A>().List();
var query2 = query1.Where(a => a.B.Count() > 0);
Any ideas? Thanks.
Edit:
Here is my mappings. I'm using NHibernate 3.1.0.4000:
Models:
public class A
{
public virtual int Id { get; private set; }
public virtual ICollection<B> Bs { get; set; }
}
public class B
{
public virtual int Id { get; private set; }
}
Mappings:
public class AMappings : ClassMap<A>
{
public AMappings()
{
Id(x => x.Id);
HasMany(x => x.Bs).LazyLoad();
}
}
public class BMappings : ClassMap<B>
{
public BMappings()
{
Id(x => x.Id);
}
}
Rest of my code:
class Program
{
static void Main(string[] args)
{
// Create connection string
string connectionString = new System.Data.SqlClient.SqlConnectionStringBuilder()
{
DataSource = #".\r2",
InitialCatalog = "TestNHibernateMappings",
IntegratedSecurity = true
}.ConnectionString;
// Create SessionFactory
ISessionFactory sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration
.MsSql2008.ConnectionString(connectionString)
.ShowSql())
.Mappings(m => m.FluentMappings
.Add(typeof(AMappings))
.Add(typeof(BMappings)))
.ExposeConfiguration(BuildSchema)
.BuildConfiguration()
.BuildSessionFactory();
// Test
var session = sessionFactory.OpenSession();
// This line works OK
var query1 = session.Query<A>()
.Where(a => a.Bs.Count() > 0);
// This line throws exception: Unrecognised method call in epression a.Bs.Count()
var query2 = session.QueryOver<A>()
.Where(a => a.Bs.Count() > 0);
}
static void BuildSchema(Configuration cfg)
{
new SchemaExport(cfg).Create(false, true);
}
}
QueryOver is not LINQ.
Your second code snippet works because it's retrieving ALL THE RECORDS and using LINQ-to-objects in memory.
What you should do is:
session.Query<A>()
.Where(a => a.B.Count() > 0)
.ToList();
or better yet:
session.Query<A>()
.Where(a => a.B.Any())
.ToList();
Query is an extension method, you need to add using NHibernate.Linq;

Map List<Int32> using Fluent Nhibernate

I need to map List<Int32> using Fluent Nhibernate.
Sample code:
public class ReportRequest
{
public List<Int32> EntityIds
{
get { return entityIds; }
set { entityIds = value; }
}
}
Please guide.
Thank you!
I have implemented it as :
public class ReportRequestMap : ClassMap<ReportRequest>
{
public ReportRequestMap()
{
Id(x => x.Id).UnsavedValue(null).GeneratedBy.Native();
HasMany(x => x.EntityIds).Table("ReportEntities").KeyColumn("ReportRequestId").Element("EntityId").AsBag();
}
}