Suppose I have following table:
public class ResourcePossibleUm : EntityBase
{
public virtual bool IsMain { get; set; }
public virtual double UmMass { get; set; }
-------
}
And mapping:
public class ResourcePosibleUMMap : ClassMap<ResourcePossibleUm>
{
public ResourcePosibleUMMap()
{
Id(x => x.Id).GeneratedBy.Identity();
Map(x => x.IsMain);
Map(c => c.UmMass);
}
}
I am working with sql server 2008.
By default sql server 2008 will set my primary key (Id) like clustered index but I really don't want it . I want ["IsMain"] column to be clustered index.I want to set ["IsMain"] column like clustered index s using fluent Nhibernate but I can't figure out how to do this. Thanks for your attention!
SOLUTION:
public class CustomMsSql2008Dialect : MsSql2008Dialect
{
public override string PrimaryKeyString
{
get { return "primary key nonclustered"; }
}
}
Now using CustomMsSql2008Dialect like Dialect in configuration initialization I will not have primary key like clustered index , id will be nonclustered index.
Related
I have two table like below:
[Table("MyFlashCard")]
public partial class MyFlashCard
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MyFlashCard()
{
MyFlashCardPics = new HashSet<MyFlashCardPic>();
}
public int Id { get; set; }
public int? FaslID { get; set; }
public virtual FasleManJdl FasleManJdl { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MyFlashCardPic> MyFlashCardPics { get; set; }
}
[Table("MyFlashCardPic")]
public partial class MyFlashCardPic
{
public int Id { get; set; }
[ForeignKey("MyFlashCard")]
public int MyFlashCardId { get; set; }
public virtual MyFlashCard MyFlashCard { get; set; }
}
and a ModelBuilder:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<MyFlashCard>()
.HasMany(e => e.MyFlashCardPics)
.WithRequired(e => e.MyFlashCard)
.HasForeignKey(e => e.MyFlashCardId)
.WillCascadeOnDelete();
}
and when I add migration it will create the below code:
CreateTable(
"dbo.MyFlashCardPic",
c => new
{
Id = c.Int(nullable: false, identity: true),
MyFlashCardId = c.Int(nullable: false),
MyFlashCard_Id = c.Int(),
MyFlashCard_Id1 = c.Int(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.MyFlashCard", t => t.MyFlashCard_Id)
.ForeignKey("dbo.MyFlashCard", t => t.MyFlashCard_Id1)
.ForeignKey("dbo.MyFlashCard", t => t.MyFlashCardId, cascadeDelete: true)
.Index(t => t.MyFlashCardId)
.Index(t => t.MyFlashCard_Id)
.Index(t => t.MyFlashCard_Id1);
I only have MyFlashCardId column in MyFlashCardPic table but it want to create another column like: MyFlashCard_Id, MyFlashCard_Id1
I want to know why this happens and how prevent it?
If I delete these columns from above migration,after creating database(with update-database command) it will throws below error when I want to use MyFlashCardPic entity
Invalid column name 'MyFlashCard_Id1' , Invalid column name 'MyFlashCard_Id'
and if I don't delete these columns from migration I have problem in editing flashcard that have pics like another question I have recently
How to find out context objects that one entity is attached to?
another point is that without
[ForeignKey("MyFlashCard")]
attribute it will create 3 index column and without
modelBuilder.Entity<MyFlashCard>()
.HasMany(e => e.MyFlashCardPics)
.WithRequired(e => e.MyFlashCard)
.HasForeignKey(e => e.MyFlashCardId)
.WillCascadeOnDelete();
in OnModeling it will create 4 index column
I think your problem is that you have both:
[ForeignKey("MyFlashCard")]
public int MyFlashCardId { get; set; }
and
public virtual MyFlashCard MyFlashCard { get; set; }
When adding the public virtual property you are prompting EF to set a foreign key relationship, which by default it does so with ClassNameId syntax. Because you have already created the FK yourself with the same name, it nevertheless still thinks it has to do something, so it creates another with 1 as a suffix. To get around the problem, remove your own ForeignKey entry and let EF do its stuff!
EDIT
I created a new MVC solution. Did the initial database create, then added a new class containing:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace MVCef.Models
{
[Table("MyFlashCard")]
public class MyFlashCard
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MyFlashCard()
{
MyFlashCardPics = new HashSet<MyFlashCardPic>();
}
public int Id { get; set; }
public int? FaslID { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MyFlashCardPic> MyFlashCardPics { get; set; }
}
[Table("MyFlashCardPic")]
public class MyFlashCardPic
{
public int Id { get; set; }
public virtual MyFlashCard MyFlashCard { get; set; }
}
}
Made appropriate changes in IdentityModel, created new migration and everything worked, as advertised - including foreign key creation. I suggest you try the same with a new solution. I think somewhere along the line, you have confused the hell out of EF!
You should either remove the [ForeignKey("MyFlashCard")] attribute or stop configuring it through model builder. Doing both together is the cause of your troubles.
How does index creation affect you in all this?
I inserted exactly your model (I only added another missing class with Id and Description properties). I inserted also the "double configuration" of the same foreign key.
These are the statements that EF 6.1.3 runs
ExecuteNonQuery==========
CREATE TABLE [FasleManJdls] (
[Id] int not null identity(1,1)
, [Description] varchar(50) null
);
ALTER TABLE [FasleManJdls] ADD CONSTRAINT [PK_FasleManJdls_873b808d] PRIMARY KEY ([Id])
ExecuteNonQuery==========
CREATE TABLE [MyFlashCardPic] (
[Id] int not null identity(1,1)
, [MyFlashCardId] int not null
);
ALTER TABLE [MyFlashCardPic] ADD CONSTRAINT [PK_MyFlashCardPic_873b808d] PRIMARY KEY ([Id])
ExecuteNonQuery==========
CREATE TABLE [MyFlashCard] (
[Id] int not null identity(1,1)
, [FaslID] int null
, [FasleManJdl_Id] int null
);
ALTER TABLE [MyFlashCard] ADD CONSTRAINT [PK_MyFlashCard_873b808d] PRIMARY KEY ([Id])
ExecuteNonQuery==========
CREATE INDEX [IX_MyFlashCardId] ON [MyFlashCardPic] ([MyFlashCardId])
ExecuteNonQuery==========
CREATE INDEX [IX_FasleManJdl_Id] ON [MyFlashCard] ([FasleManJdl_Id])
ExecuteNonQuery==========
ALTER TABLE [MyFlashCardPic] ADD CONSTRAINT [FK_MyFlashCardPic_MyFlashCard_MyFlashCardId] FOREIGN KEY ([MyFlashCardId]) REFERENCES [MyFlashCard] ([Id])
ExecuteNonQuery==========
ALTER TABLE [MyFlashCard] ADD CONSTRAINT [FK_MyFlashCard_FasleManJdls_FasleManJdl_Id] FOREIGN KEY ([FasleManJdl_Id]) REFERENCES [FasleManJdls] ([Id])
No strange columns, exactly as I'm expecting, one foreign key ([MyFlashCardPic].[MyFlashCardId] REFERENCES [MyFlashCard].[Id]).
I'm quite sure that the problem is somewhere else.
Wich EF are you using?
EDIT
Why your classes are marked as partial? Are you sure that you don't have other code around?
I just started learning C# and Nhibernate. I'm trying to solve following problem for hours.
Can anyone see the problem?
Table Line:
CREATE TABLE [dbo].[Line]
(
[ID] UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
[Name] NVARCHAR (45) NOT NULL,
[ID_Color] UNIQUEIDENTIFIER NULL,
PRIMARY KEY CLUSTERED ([ID] ASC),
UNIQUE NONCLUSTERED ([Name] ASC),
FOREIGN KEY ([ID_Color])
REFERENCES [dbo].[Line_Color] ([ID]) ON DELETE SET NULL
);
Table Line_Color:
CREATE TABLE [dbo].[Line_Color]
(
[ID] UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
[Color] NVARCHAR (45) NOT NULL,
PRIMARY KEY CLUSTERED ([ID] ASC),
UNIQUE NONCLUSTERED ([Color] ASC)
);
Entity.cs:
public class Entity<T> where T : Entity<T>
{
public virtual Guid Id { get; set; }
private int? _oldHashCode;
public override Boolean Equals(object obj)
{
var other = obj as T;
if (other == null)
return false;
// handle the case of comparing two NEW objects
var otherIsTransient = Equals(other.Id, Guid.Empty);
var thisIsTransient = Equals(Id, Guid.Empty);
if (otherIsTransient && thisIsTransient)
return ReferenceEquals(other, this);
return other.Id.Equals(Id);
}
public override Int32 GetHashCode()
{
if (_oldHashCode.HasValue)
return _oldHashCode.Value;
var thisIsTransient = Equals(Id, Guid.Empty);
if (thisIsTransient)
{
_oldHashCode = base.GetHashCode();
return _oldHashCode.Value;
}
return Id.GetHashCode();
}
public static Boolean operator ==(Entity<T> x, Entity<T> y)
{
return Equals(x, y);
}
public static Boolean operator !=(Entity<T> x, Entity<T> y)
{
return !(x == y);
}
}
Line.cs:
public class Line : Entity<Line>
{
public virtual String Name { get; set; }
public virtual Color Color { get; set; }
}
LineMap.cs:
public class LineMap : ClassMap<Line>
{
public LineMap()
{
Id(x => x.Id);
Map(x => x.Name);
References(x => x.Color);
}
}
Color.cs:
public class Color : Entity<Color>
{
public virtual String ColorS { get; set; }
}
ColorMap.cs:
public class ColorMap : ClassMap<Color>
{
public ColorMap()
{
Id(x => x.Id);
Map(x => x.ColorS);
}
}
On every query I do, I get something like
An unhandled exception of type
'NHibernate.Exceptions.GenericADOException' occurred in NHibernate.dll
Additional information: could not execute query
or NULL.
There are 2 problems in your code:
An entity is named Color and the corresponding table Line_Color;
A property is named ColorS and the corresponding column Color.
They must correspond, or you have to tell NHibernate the DB names. Do some renaming (preferably) or adjust your mapping.
ColorMap.cs:
public class ColorMap : ClassMap<Color>
{
public ColorMap()
{
Table("Line_Color");
Id(x => x.Id);
Map(x => x.ColorS).Column("Color");
}
}
Entities represents tables (or table likes).
You don't need an table (and entity/map) for "color".
You don't need to have a class for "Color".
Line need to have a property and a map for property "color".
The problem Hibernate acuses is because it can't match the Entity color to any table.
I am using NHibernate 3.3 with FluentNHibernate 1.3.
My database has two tables: InvoiceHeader and InvoiceDetail. The header has a composite key of two columns. The detail has a single column primary key. The problem is that the detail does not have both parts of the composite key; it only has one part of it. No foreign key constraints exist.
public class TInvoiceHeader
{
public virtual TInvoiceHeaderIdentifier Identifier { get; set; }
public virtual long Dwinvoiceid { get; set; }
public virtual int InvTimeID { get; set; }
public virtual IList<TInvoiceDetail> Details { get; set; }
....
}
public class TInvoiceHeaderIdentifier
{
public virtual long Dwinvoiceid { get; set; }
public virtual int InvTimeID { get; set; }
....
}
public TInvoiceHeaderMap()
{
CompositeId<TInvoiceHeaderIdentifier>(x => x.Identifier)
.KeyProperty(x => x.Dwinvoiceid, "DWInvoiceID")
.KeyProperty(x => x.InvTimeID, "InvTimeID");
HasMany<TInvoiceDetail>(x => x.Details).KeyColumn("DWInvoiceID");
....
}
public class TInvoiceDetail
{
public virtual TInvoiceHeader Header { get; set; }
public virtual long Dwinvoicedetailid { get; set; }
public virtual long Dwinvoiceid { get; set; }
....
}
public TInvoiceDetailMap()
{
Table("tInvoiceDetail");
LazyLoad();
Id(x => x.Dwinvoicedetailid).GeneratedBy.Identity().Column("DWInvoiceDetailId");
References<TInvoiceHeader>(x => x.Dwinvoiceid);
....
}
I have no idea how to make this work since the detail row doesn't have the InvTimeID key. I'm getting:
NHibernate.Cfg.Configuration - Foreign key (FKAD4039AB666D3F7:tInvoiceDetail [DWInvoiceID])) must have same number of columns as the referenced primary key (tInvoiceHeader [DWInvoiceID, InvTimeID])
NHibernate.FKUnmatchingColumnsException: Foreign key (FKAD4039AB666D3F7:tInvoiceDetail [DWInvoiceID])) must have same number of columns as the referenced primary key (tInvoiceHeader [DWInvoiceID, InvTimeID])
at NHibernate.Mapping.ForeignKey.AlignColumns(Table referencedTable)
at NHibernate.Mapping.ForeignKey.AlignColumns()
at NHibernate.Cfg.Configuration.SecondPassCompileForeignKeys(Table table, ISet done)
NHibernate.Cfg.Configuration - Foreign key (FKAD4039AB666D3F7:tInvoiceDetail [DWInvoiceID])) must have same number of columns as the referenced primary key (tInvoiceHeader [DWInvoiceID, InvTimeID])
NHibernate.FKUnmatchingColumnsException: Foreign key (FKAD4039AB666D3F7:tInvoiceDetail [DWInvoiceID])) must have same number of columns as the referenced primary key (tInvoiceHeader [DWInvoiceID, InvTimeID])
at NHibernate.Mapping.ForeignKey.AlignColumns(Table referencedTable)
at NHibernate.Mapping.ForeignKey.AlignColumns()
at NHibernate.Cfg.Configuration.SecondPassCompileForeignKeys(Table table, ISet done)
Can this work? My queries will always be going from header-to-detail, not vice versa. I cannot modify the tables.
I experienced this issue after upgrading nHibernate and Fluent nHibernate. It looked like it was getting confused with the column names.
The solution was to manually name my columns and the issue went away.
public void Override(AutoMapping<Record> mapping)
{
mapping.Id(x => x.Id).Column("RecordId");
mapping.Map(x => x.Name).Not.Nullable();
mapping.References(x => x.Parent).Not.Nullable().Column("ParentRecordId");
mapping.References(x => x.Type).Not.Nullable().Column("TypeId");
}
I'm trying to generate db schema using fluent nhibernate, nhibernate 3.0, spring.net 1.3.1 and SQLite. The create/update script generated by NHibernate is
create table LogEntries (Id UNIQUEIDENTIFIER not null, Hostname TEXT not null, LoggerName TEXT not null, LogLevel INTEGER not null, Message TEXT not null, primary key (Id))
create table Properties (Id INTEGER not null, Key TEXT, Value TEXT, LogEntry_id UNIQUEIDENTIFIER, Index INTEGER, primary key (Id))
But it fails with the following error
System.Data.SQLite.SQLiteException: SQLite error
near "Index": syntax error
The entities:
public class LogEntry
{
public virtual Guid Id { get; set; }
public virtual string LoggerName { get; set; }
public virtual string Message { get; set; }
public virtual int LogLevel { get; set; }
public virtual string Hostname { get; set; }
public virtual IList<Property> Properties { get; set; }
}
public class Property
{
public virtual int Id { get; set; }
public virtual string Key { get; set; }
public virtual string Value { get; set; }
}
And the mapping classes
public class LogEntryMap : ClassMap<LogEntry>
{
public LogEntryMap()
{
Table("LogEntries");
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Hostname).Not.Nullable();
Map(x => x.LoggerName).Not.Nullable();
Map(x => x.LogLevel).Not.Nullable();
Map(x => x.Message).Not.Nullable();
HasMany(x => x.Properties).Cascade.AllDeleteOrphan().AsList();
}
}
public class PropertyMap : ClassMap<Property>
{
public PropertyMap()
{
Table("Properties");
Id(x => x.Id).GeneratedBy.Increment();
Map(x => x.Key);
Map(x => x.Value);
}
}
I'm currently learning NHibernate myself (reading NHibernate 3.0 Cookbook), so in no way am I an expert.
I have the same problem at the moment, having a HasMany-map Parent.Children in an SQLite environment. This also crashes on the Index syntax error.
From what I've managed to deduce, Index is a reserved keyword (isn't it in almost every RDBMS?). It seems these keywords are not escaped by default, and hence, the SQL-script is invalid.
According to the book, you can escape the columnnames by adding a backtick to the column-name:
HasMany(x => x.Children).Cascade.AllDeleteOrphan().AsList(p => p.Column("`Index"));
However, even though this "works", it generates the following SQL-query, which seems to have dropped the x:
create table Child (
Id INTEGER not null,
ChildType TEXT not null,
Version INTEGER not null,
Content TEXT,
Title TEXT not null,
Parent_id INTEGER,
"Inde" INTEGER,
primary key (Id)
)
So, either consider:
specifying a custom index columnname which isn't a keyword,
rely on the backtick auto-escape (no clue what's happening here, no time to check)
use a different collection type if you don't actually need an ordered list. See List vs Set vs Bag in NHibernate
I have these classes:
public class Entity
{
public virtual Guid Id { get; set; }
public virtual string EntityName { get; set; }
public virtual IDictionary<string, Property> { get; set; }
// ...
}
public class Property
{
public virtual int? IntValue { get; set; }
public virtual decimal? DecimalValue { get; set; }
}
Is it possible to create Fluent NHibernate mappings so that executing the resulting schema will give these tables:
[Entities]
* Id : UNIQUEIDENTIFIER NOT NULL
* EntityName : NVARCHAR(50) NOT NULL
with a clustered index on "Id"
[Properties]
* EntityId : UNIQUEIDENTIFIER NOT NULL
* PropertyName : VARCHAR(50) NOT NULL
* IntValue : INT NULL
* DecimalValue : DECIMAL(12,6) NULL
with a clustered index on "EntityId" and "PropertyName"
Or do I need to change my classes?
An answer more verbose than yes/no will be much appreciated :)
Besides your clustered index which you would need to manually create, yes. Do you absolutely require FNH to generate your schema?
Why don't you just generate the schema yourself specific to your requirements and then map it accordingly.
(not tested or anything, written off the top of my head)
public class EntityMap : ClassMap<Entity>
{
public EntityMap()
{
Table("Entities");
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.Name).CustomSqlType("NVARCHAR").Length(50).Not.Nullable();
HasMany<Property>(x => x.Properties)
.Table("Properties")
.KeyColumn("PropertyName")
.Inverse()
.AsBag();
}
}
public class PropertyMap : ClassMap<Property>
{
public PropertyMap()
{
Table("Properties");
Id(x => x.Id).GeneratedBy.GuidComb();
Map(x => x.PropertyName).Length(50).Not.Nullable();
Map(x => x.IntValue);
Map(x => x.DecimalValue);
}
}
Here's the mapping that I have come up with:
public sealed class EntityMap : ClassMap<Entity>
{
public EntityMap()
{
Table("Entities");
Id(c => c.Id);
Map(c => c.EntityName).CustomSqlType("nvarchar(50)").Not.Nullable();
HasMany(c => c.Properties)
.KeyColumn("EntityId")
.AsMap<string>("PropertyName")
.Component(part =>
{
part.Map(x => x.IntValue);
part.Map(x => x.DecimalValue).Precision(12).Scale(6);
});
}
}
Schema generation yields this:
create table Entities (
Id UNIQUEIDENTIFIER not null,
EntityName nvarchar(50) not null,
primary key (Id)
)
create table Properties (
EntityId UNIQUEIDENTIFIER not null,
IntValue INT null,
DecimalValue DECIMAL(12, 6) null,
PropertyName INT not null,
primary key (EntityId, PropertyName)
)
alter table Properties
add constraint FK63646D8550C14DC4
foreign key (EntityId)
references Entities
Which is pretty much what I need, with an exception of the column order (minor issue) and PropertyName being nvarchar(255) instead of varchar(50) (something I actually care about).