Fluent NHibernate SqlDateTime overflow exception - c#

I'm mapping a very simple Users table, and i have a column named 'LastLoginDate' which is defined as nullable in sql server.
My mapping looks like this :
public Users {
Id(x => x.UserId);
Map(x => x.UserName);
...
...
Map(x => x.LastLoginDate).Nullable();
}
But everytime I try to save this entity programatically, i always get the SqlDateTime overflow exception.
If i try to enter a manual sql statement with 'null' in this column it works.
If i comment out just this property, it will work as well.
What can be the problem ???
Thanks in advance!

Your entity should look like this:
public class User
{
public virtual DateTime? LastLoginDate {get;set;}
// etc
}
Then, your map should work properly.
edit: The ? after DateTime specifies that it is Nullable, and is a short form for Nullable<DateTime>. If this isn't the cause of your error, you may want to check that Fluently.Configure specifies the correct version of SqlServer.

Related

NHibernate read timestamp column from SQL Server

I'm using NHibernate 4.0.0.4000 (with mapping by code) and SQL Server 2012.
I have to access tables which (among others) contain a timestamp (otherwise also known as rowversion) column. Until now I could simply ignore the column by not using it in my Entity/Mapping.
Now I want to use it to see, which rows have changed since the last time I checked the table in question.
Since timestamp may only be changed by SQL Server, any INSERT / UPDATE statements generated by NHibernate must ignore this column. I didn't find a solution for this problem.
My entity:
public class TblAnwendungsbelegposition {
public virtual int ANWBID { get; set; }
// Other properties cut out for readability
public virtual byte[] upsize_ts { get; set; }
}
My mapping:
public class TblAnwendungsbelegpositionMap : ClassMapping<TblAnwendungsbelegposition> {
public TblAnwendungsbelegpositionMap() {
Table("tbl_anwendungsbelegposition");
Schema("dbo");
Lazy(true);
Id(x => x.ANWBID, map => map.Generator(Generators.Identity));
//Other properties cut out for readability
Property(x => x.upsize_ts, map => map.Access(Accessor.ReadOnly));
}
}
Result for this version:
Cannot update a timestamp column
as an error message during reading (on the automatic Session.Flush() on closing the using-statement wrapping the db connection).
When I remove the accessor in the mapping I can read without problems, but when I insert a new line into the table, I get the message
Cannot insert an explicit value into a timestamp column. Use INSERT with a column list to exclude the timestamp column, or insert a DEFAULT into the timestamp column.
When I change the set; to private set;, I get the message during the initial BuildSessionFactory();
The following types may not be used as proxies:
PigTool.Entities.TblAnwendungsbelegposition: method set_upsize_ts should be 'public/protected virtual' or 'protected internal virtual'
When I remove the set; from the entity-property, I get the message during the initial BuildSessionFactory();
Could not find a setter for property 'upsize_ts' in class 'PigTool.Entities.TblAnwendungsbelegposition'
Does anyone have an idea how I can successfully read rows containing this readonly column, while retaining the ability to generate new rows? I'd prefer not to have two entities/mappings per relevant table, where one doesn't contain the timestamp property for everyday work and one which has it, but is only ever used for reading.
I finally found a solution.
In the mapping exchange the
Property(x => x.upsize_ts, map => map.Access(Accessor.ReadOnly));
with
Version(x => x.upsize_ts, map =>
{
map.Column(c =>
{
c.SqlType("timestamp");
c.NotNullable(false);
});
map.Type(new BinaryBlobType());
map.Generated(VersionGeneration.Always);
map.UnsavedValue(null);
});

Fluent NHibernate mapping for DateTime with default value

I know that there are many threads about this and I have read most of them. However for me a couple of things remain unclear and still do not work.
If I have on my database schema a field of type DateTime and I like to assign it a default value I would do something like this:
create table [mySchema].[MyTable](
ObjectGuid uniqueidentifier CONSTRAINT Id PRIMARY KEY,
SomeTextToStore nvarchar(128) NULL,
CDate datetime NOT NULL DEFAULT GETDATE(),
CUser nvarchar(64) DEFAULT CURRENT_USER
);
GO
(Don't know if it is important: I am using SQL Server Express 2014. Fluent configuration is for SQL Server 2012.)
This works fine when doing an INSERT from ISQL, inserts a timestamp of the moment when the record was added.
Using fluent I would write something like this:
Domain:
public class MyObject
{
public virtual Guid Id {get; set}
public virtual string SomeTextToStore {get; set;}
public virtual DateTime? CDate {get; set;}
public virtual string CUser {get; set;}
}
NOTE: I made CDate nullable!
And a mapping class like this:
class MyObjectMap : ClassMap<MyObject>
{
public MyObjectMap()
{
Table("MySchema.MyTable");
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.SomeTextToStore).Length(128).Nullable();
Map(x => x.CDate).Not.Nullable().Default("getdate()");
Map(x => x.CUser).Not.Nullable().Default("CURRENT_USER);
}
}
In the program (in my case this is a library that can be called from several type of programs) I do something like:
public void EnterSomeText()
{
using (var session = sessionManager.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
var myObj = new MyObject();
myObj.SomeTextToStore("bla bla bla");
session.SaveOrUpdate(myObj);
transaction.Commit();
}
session.Close();
}
}
This ends always in a DateTime overflow exception! (SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM)
It looks like not passing a value to the file CDate is causing the problem. When I add the default in my library like such it works:
...
myObj.SomeTextToStore("bla bla bla");
myObj.CDate = DateTime.Now; // <---- Set the date here
session.SaveOrUpdate(myObj);
...
But this is not really the solution....
Questions:
What am I doing wrong / missing ?
What is the correct strategy when
defining defaults? Doing it on the database or just in code? (When
starting a plain new vanilla project, I would prefer doing
everything from C# code, even create and later update the schema...)
Regarding domain classes: Is it wise to create constructors,
that fill fields with defaults?
I do this for the field CUser because in CUser I like to add the current user context
public MyObject()
{
var o = System.Security.Principal.WindowsIdentity.GetCurrent();
if (o != null)
{
CUser = o.Name;
}
}
Instead of filling the CDate field with the current date in my DB-access layer library I could do it also in the constructor of the domain-class like such:
public MyObject()
{
var o = System.Security.Principal.WindowsIdentity.GetCurrent();
if (o != null)
{
CUser = o.Name;
}
CDate = DateTime.Now;
}
Many thanks for your help and comments!
Usually for this type of mapping I do the following in my mapping:
DynamicInsert();
DynamicUpdate();
This way if you have nullable types in C# and you don't set them to anything nhibernate will not include them in the insert or update statement. I never really like it when nhibernate is updating columns that weren't changed in the code anyway.
Furthermore when you specify .Not.Nullable(); and .Default("getdate()") in the mapping all this is used for is schema generation. It's not actually used by nhibernate to do any validation or defaulting. That is left up to the database.
You have defined this column as NOT NULL in the database and Nullable() in the mapping. FluentNHibernate is sending DbNull to the database, which is getting rejected, since the database column is not nullable.
You should decide where your logic resides. Is this your code or the database who is the master?
Setting default value in FlientNHibernate was discussed, for example, in this question.
Suggested code from there is:
Map(x => x.SubmitionDate).Default("getdate()").Not.Nullable();
In general, I would always advise to use NHibernate Profiler (paid tool) to see what queries go to the database and why they fail. Invaluable for optimisation, you are using ORM, be careful now!

Fluent NHibernate automap list of strings with nvarchar(max)

I am using Fluent NHibernate 1.2 for NHibernate 3.1. I have a class:
public class Marks
{
public virtual int Id { get; set; }
public virtual IList<string> Answers { get; set; }
}
In the mapping for the Marks class, I have:
HasMany(m => m.Answers).Element("Value");
When the tables are created, an "Answers" table get created with the following columns:
Marks_id (FK, int, not null)
Value (nvarchar(255), null)
What I would like to do is have the Value be nvarchar(max). I'd prefer not doing this for every string in every class, just for this one class.
I have looked at these posts: first, second, third, but haven't found anything yet that helps.
Thanks in advance for any help you can offer. If you need additional information, please let me know.
Edit:
This is the code that resolves the issue:
HasMany(x => x.Answers).Element("Value", x => x.Columns.Single().Length = 4001);
You can force mapping string to longer column at each column level in mapping either by using CustomSqlType("nvarchar(max)") or, bit more universally, by setting Length(4001) (SQL Server magic number, above which it creates nvarchar(max) automatically).
To apply it automatically for all string columns in your entities, you can write your own FluentNHibernate convention:
public class LongStringConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
{
criteria.Expect(x => x.Type == typeof(string));
}
public void Apply(IPropertyInstance instance)
{
instance.Length(4001);
}
}
And register it in mapping container i.e. like that:
Fluently.Configure()
.Mappings(...)
.Conventions.Add<LongStringConvention>()
To apply it for collection of strings, you can use custom element mappings:
HasMany(x => x.Answers).Element("Value", x => x.Columns.Single().Length = 4001);
Came across this issue myself and the above answers were most helpful in pointing me in the right direction...but I'm using a slightly newer version of FluentNH.
For Fluent NHibernate 1.3 and NH 3.3.1, the correct code is:
HasMany(x => x.Answers).Element("Value", x => x.Length(4001));
The answers above only work for older version of nhibernate.
If you try HasMany(x => x.Answers).Element("Value", x => x.Length(4001)); you will get the following:
Error Property or indexer
'FluentNHibernate.MappingModel.ColumnMapping.Length' cannot be
assigned to -- it is read only
The correct way to do this now is (NHibernate 4.0.2.4, FluentNHibernate 2.0.1.0):
HasMany(m => m.Answers).Element("Value", e => e.Length(4001))

Self referencing many to many relationships in Fluent NHibernate automapping automapping to 1:n and not n:n

The title pretty much explains it all, I have a Member object that references 'Friends' who are also type Member.
public class Member : Entity
{
public Member()
{
Friends = new List<Member>();
}
public virtual IList<Member> Friends
{
get; set;
}
}
The schema generation tool makes it a 1:n relationship while it should be a n:n relationship i.e. a column is added to the member table called member_id and no connecting table is created.
Is there any way to make a Self referencing many to many relationships in Fluent NHibernate?
I tried using an override that I got as an answer before:
public class MemberOverride : IAutoMappingOverride<Member>
{
public void Override(AutoMapping<Member> mapping)
{
mapping.HasManyToMany(m => m.Friends)
.Table("MemberFriendsLinkTable");
}
}
but I get the error message:
"NHibernate.MappingException: Repeated column in mapping for collection: Proj.BO.Member.Friends column: Member_id"
Thanks
EDIT: I found the answer, it's to put:
mapping.HasManyToMany(m => m.Friends).ParentKeyColumn("Member_Id").ChildKeyColumn("Friend_Id")
.Table("MemberFriendsLinkTable").Inverse().Cascade.SaveUpdate();
So that I don't have to see this question at the top of the "Unanswered NHibernate Questions" list anymore...
Eitan, the asker, discovered the solution to his own problem. He needed to specify the ParentKeyColumn and ChildKeyColumn like so:
EDIT: I found the answer, it's to put:
mapping.HasManyToMany(m => m.Friends)
.ParentKeyColumn("Member_Id")
.ChildKeyColumn("Friend_Id")
.Table("MemberFriendsLinkTable")
.Inverse()
.Cascade.SaveUpdate();
FluentNHibernate by default names foreign key columns like so: {className}_Id. Since both ends of the many-to-many were of the same type, it wanted to use the same column name, Member_Id for both columns. Explicitly naming the columns circumvents this problem.
References(x => x.Parent)
.Class<Parent>()
.Access.Property()
.Cascade.None()
.LazyLoad()
.Not.Insert()
.Not.Update()
.Columns("PARENT_ID");
HasMany(x => x.Children)
.Access.Property()
.AsBag()
.Cascade.SaveUpdate()
.LazyLoad()
.Inverse()
.Generic()
.KeyColumns.Add("PARENT_ID", mapping => mapping.Name("PARENT_ID")
.SqlType("NUMBER")
.Not.Nullable());
Well I understand that, I have similar kind of issue with little bit of change. Can you please try to answer the question to the link
Fluent nhibernate m-to-m mapping with external table

How to set discriminate column type for subclass with FNH?

What is the new SetAttribute() in FNH mapping? I need to set my discriminator value on subclass because String is not preferred - old post
with NH 2.1.2.4000, FNH 1.1.0.689
public class BaseBuildingMap : ClassMap<BaseBuilding>
{
public BaseBuildingMap()
{
Id(x => x.Id);
DiscriminateSubClassesOnColumn<int>("BuildingType", -1);
}
}
public class PowerStationMap : SubclassMap<PowerStation>
{
public PowerStationMap()
{
Map(x => x.ElectricityProduction);
}
}
NHibernate.MappingException: Could not format discriminator value to SQL string of entity Model.Test.PowerStation ---> System.FormatException: Input string was not in a correct format.
I need to set SetAttribute("discriminator-value", "-1"); but there is no such method.
EDIT 1
Question: How to set discriminate column type for subclass with FNH?
public class PowerStationMap : SubclassMap<PowerStation>
{
public PowerStationMap()
{
DiscriminatorValue((int)1);
Map(x => x.ElectricityProduction);
}
}
I've finally found my answer, it's
SubclassMap<T>::DiscriminatorValue(object discriminatorValue);
From Fluent NHibernate 1.0 Release Notes
Removed SetAttribute - SetAttribute was a stop-gap measure to allow people to use Fluent NHibernate when we didn't support the attributes they needed. We've now gone to a great length to support all of the main attributes in the fluent interface, so you shouldn't need this anymore. If there are any attributes you need that we've missed, let us know (or even better, send us a pull request/patch)

Categories