web-dev-qa-db-ja.com

流暢なnhibernateの1対1のマッピングを行う方法は?

これを行うにはどうすればよいですか。1対1のマッピングを作成しようとしています。

public class Setting
{
    public virtual Guid StudentId { get; set; }
    public virtual DateFilters TaskFilterOption { get; set; }
    public virtual string TimeZoneId { get; set; }
    public virtual string TimeZoneName { get; set; }
    public virtual DateTime EndOfTerm { get; set; }
    public virtual Student Student { get; set; }

}

//クラスマップ

 public SettingMap()
        {
           /// Id(Reveal.Member<Setting>("StudentId")).GeneratedBy.Foreign("StudentId");
            //Id(x => x.StudentId);
            Map(x => x.TaskFilterOption).Default(DateFilters.All.ToString()).NvarcharWithMaxSize().Not.Nullable();
            Map(x => x.TimeZoneId).NvarcharWithMaxSize().Not.Nullable();
            Map(x => x.TimeZoneName).NvarcharWithMaxSize().Not.Nullable();
            Map(x => x.EndOfTerm).Default("5/21/2011").Not.Nullable();
            HasOne(x => x.Student);
        }

//学生マップ

public class StudentMap : ClassMap<Student>
    {
        public StudentMap()
        {
            Id(x => x.StudentId);
            HasOne(x => x.Setting).Cascade.All();

        }
    }

  public class Student
    {
        public virtual Guid StudentId { get; private set; }
        public virtual Setting Setting { get; set; }
    }

これで、設定オブジェクトを作成してデータベースに保存しようとすると、クラッシュします。

   Setting setting = new Setting
                                          {
                                              TimeZoneId = viewModel.SelectedTimeZone, 
                                              TimeZoneName = info.DisplayName, 
                                              EndOfTerm =  DateTime.UtcNow.AddDays(-1),
                                              Student = student
                                          };

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Settings_Students". The conflict occurred in database "Database", table "dbo.Students", column 'StudentId'.
The statement has been terminated.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Data.SqlClient.SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Settings_Students". The conflict occurred in database "Database", table "dbo.Students", column 'StudentId'.
The statement has been terminated.

何が足りないのですか?

編集

public class StudentMap : ClassMap<Student>
{
    public StudentMap()
    {
        Id(x => x.StudentId).GeneratedBy.Guid();
        HasOne(x => x.Setting).PropertyRef("Student").Cascade.All();
    }
}

public class SettingMap : ClassMap<Setting>
{
    public SettingMap()
    {
        Id(x => x.StudentId).GeneratedBy.Guid();
        Map(x => x.TaskFilterOption).Default(DateFilters.All.ToString()).NvarcharWithMaxSize().Not.Nullable();
        Map(x => x.TimeZoneId).NvarcharWithMaxSize().Not.Nullable();
        Map(x => x.TimeZoneName).NvarcharWithMaxSize().Not.Nullable();
        Map(x => x.EndOfTerm).Default("5/21/2011").Not.Nullable();
        References(x => x.Student).Unique();
    }
}

// try 1

      Setting setting = new Setting
                                          {
                                              TimeZoneId = viewModel.SelectedTimeZone, 
                                              TimeZoneName = info.DisplayName, 
                                              EndOfTerm =  DateTime.UtcNow.AddDays(-1),
                                              Student = student
                                          };
     studentRepo.SaveSettings(setting);
     studentRepo.Commit();

// try 2

  Setting setting = new Setting
                                          {
                                              TimeZoneId = viewModel.SelectedTimeZone, 
                                              TimeZoneName = info.DisplayName, 
                                              EndOfTerm =  DateTime.UtcNow.AddDays(-1),
                                              Student = student
                                          };

student.Setting = setting
studentRepo.CreateStudent(student);
studentRepo.Commit();

両方の方法でこれらのエラーが発生します

Invalid index 5 for this SqlParameterCollection with Count=5. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.IndexOutOfRangeException: Invalid index 5 for this SqlParameterCollection with Count=5.

Source Error:

Line 76:             using (ITransaction transaction = session.BeginTransaction()) Line 77:   { Line 78:                 transaction.Commit(); Line 79:         } Line 80:         }
24
chobo2

NHで双方向の1対1の関連付けをマッピングする方法は2つあります。クラスが次のようになっているとしましょう。

public class Setting
{
    public virtual Guid Id { get; set; }
    public virtual Student Student { get; set; }
}

public class Student
{
    public virtual Guid Id { get; set; }
    public virtual Setting Setting { get; set; }
}

設定クラスは、関連付けのマスターです(「集約ルート」)。それは非常に珍しいですが、それは問題のドメインに依存します...

主キーの関連付け

public SettingMap()
{
    Id(x => x.Id).GeneratedBy.Guid();
    HasOne(x => x.Student).Cascade.All();
}

public StudentMap()
{
    Id(x => x.Id).GeneratedBy.Foreign("Setting");
    HasOne(x => x.Setting).Constrained();
}

新しい設定インスタンスを保存する必要があります。

        var setting = new Setting();

        setting.Student = new Student();
        setting.Student.Name = "student1";
        setting.Student.Setting = setting;
        setting.Name = "setting1";

        session.Save(setting);

外部キーの関連付け

public SettingMap()
{
    Id(x => x.Id).GeneratedBy.Guid();
    References(x => x.Student).Unique().Cascade.All();
}

public StudentMap()
{
    Id(x => x.Id).GeneratedBy.Guid();
    HasOne(x => x.Setting).Cascade.All().PropertyRef("Student");
}

主キーの関連付けは、ソリューションに近いものです。主キーの関連付けは、関連付けが常に1対1であることが絶対に確実な場合にのみ使用する必要があります。 AllDeleteOrphanカスケードは、NHでは1対1ではサポートされていないことに注意してください。

編集:詳細については、以下を参照してください。

http://fabiomaulo.blogspot.com/2010/03/conform-mapping-one-to-one.html

http://ayende.com/blog/3960/nhibernate-mapping-one-to-one

40
Jakub Linhart

ここに外部キーの関連付けを含む完全なサンプルがあります

using System;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using FluentNHibernate.Mapping;

namespace NhOneToOne
{
    public class Program
    {
        static void Main(string[] args)
        {
            try
            {

                var sessionFactory = Fluently.Configure()
                                             .Database(
                                                    MsSqlConfiguration.MsSql2005
                                                                      .ConnectionString(@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=NHTest;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False")
                                                                      .ShowSql()
                                              )
                                             .Mappings(m => m
                                             .FluentMappings.AddFromAssemblyOf<Program>())
                                             .BuildSessionFactory();

                ISession session = sessionFactory.OpenSession();


                Parent parent = new Parent();
                parent.Name = "test";
                Child child = new Child();
                child.Parent = parent;
                parent.Child = child;
                session.Save(parent);
                session.Save(child);

                int id = parent.Id;
                session.Clear();
                parent = session.Get<Parent>(id);
                child = parent.Child;


            }
            catch (Exception e)
            {
                Console.Write(e.Message);
            }
        }

    }

    public class Child
    {
        public virtual string Name { get; set; }
        public virtual int Id { get; set; }

        public virtual Parent Parent { get; set; }
    }

    public class Parent
    {
        public virtual string Name { get; set; }
        public virtual int Id { get; set; }

        public virtual Child Child { get; set; }

    }

    public class ChildMap : ClassMap<Child>
    {
        public ChildMap()
        {
            Table("ChildTable");
            Id(x => x.Id).GeneratedBy.Native();
            Map(x => x.Name);

            References(x => x.Parent).Column("IdParent");

        }
    }

    public class ParentMap : ClassMap<Parent>
    {
        public ParentMap()
        {
            Table("ParentTable");
            Id(x => x.Id).GeneratedBy.Native();
            Map(x => x.Name);

            HasOne(x => x.Child).PropertyRef(nameof(Child.Parent));
        }

    }
}

そして、テーブルを作成するためのSQL

CREATE TABLE [dbo].[ParentTable] (
    [Id]   INT           IDENTITY (1, 1) NOT NULL,
    [Name] VARCHAR (MAX) NULL
);
CREATE TABLE [dbo].[ChildTable] (
    [Id]       INT          IDENTITY (1, 1) NOT NULL,
    [IdParent] INT          NOT NULL,
    [Name]     VARCHAR (50) NULL
);
ALTER TABLE [dbo].[ChildTable]
    ADD CONSTRAINT [FK_ChildTable_ToTable] FOREIGN KEY ([IdParent]) REFERENCES [dbo].[ParentTable] ([Id]);
2
yonexbat

まず、関係の片側をInverse()として定義します。そうしないと、データベースに冗長な列があり、問題が発生する可能性があります。

これが機能しない場合は、NHibernateによって生成されたSQLステートメントを出力し(ShowSqlまたはlog4netを使用)、外部キー制約に違反している理由を理解してください(または、SQLとともにここに投稿し、の値を忘れないでくださいSQLステートメントの後に表示されるバインド変数)。

0
Ilya Kogan

SesstingsクラスでStudentIdを定義しないでください。 Sesstingクラスにはすでにそれがあります(public virtual Student Student { get; set; }から)。おそらくそれはSesstingIdである必要があり、Idフィールドもマップする必要があります(主キーを定義/マップする必要があります)。

0
VahidN