Breeze: Differences between Entity Framework and NHibernate with many to many

我的梦境 提交于 2019-12-11 05:29:37

问题


Here is the situation:

  • WebApi v1
  • Breeze 1.4.7
  • EF 5.0/NHibernate 3.3.1

What We want: A many to many exposed as a many to one. A client can have multiple countries and a country can have multiple clients. A ClientCountry entity has been created for that purpose.

My mapping looks like this:

Entity Framework:

        modelBuilder.Entity<Client>().HasKey(p => p.Id).Property(p=>p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        modelBuilder.Entity<Client>().Property(p => p.Abbreviation);
        modelBuilder.Entity<Client>().Property(p => p.ClientSinceDate).IsRequired();
        modelBuilder.Entity<Client>().Property(p => p.ClientUntilDate);
        modelBuilder.Entity<Client>().Property(p => p.Name).IsRequired();
        modelBuilder.Entity<Client>().Property(p => p.Website);
        modelBuilder.Entity<Client>().HasMany(p => p.Contacts).WithRequired(p => p.Client).WillCascadeOnDelete(true);
        modelBuilder.Entity<Client>().HasMany(p => p.ClientCountries).WithRequired(p => p.Client).WillCascadeOnDelete(true);

        modelBuilder.Entity<Contact>().HasKey(p => p.Id).Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        modelBuilder.Entity<Contact>().Property(p => p.Username);
        modelBuilder.Entity<Contact>().HasRequired(p => p.Client);

        modelBuilder.Entity<Country>().HasKey(p => p.Id).Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 
        modelBuilder.Entity<Country>().Property(p => p.ValidFrom);
        modelBuilder.Entity<Country>().Property(p => p.ValidTo);
        modelBuilder.Entity<Country>().Property(p => p.Code);
        modelBuilder.Entity<Country>().Property(p => p.DefaultLabel);
        modelBuilder.Entity<Country>().Property(p => p.Description);
        modelBuilder.Entity<Country>().Property(p => p.DisplayOrder);

        modelBuilder.Entity<ClientCountry>().HasKey(p => p.Id).Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 
        modelBuilder.Entity<ClientCountry>().Property(p => p.ValidFrom);
        modelBuilder.Entity<ClientCountry>().Property(p => p.ValidTo);
        modelBuilder.Entity<ClientCountry>().HasRequired(p => p.Client);
        modelBuilder.Entity<ClientCountry>().HasRequired(p => p.Country);

NHibernate:

public class BaseMapping<T> : ClassMapping<T> where T : BaseEntity
{
    public BaseMapping()
    {
        this.Lazy(true);
        Id(x => x.Id, map => { map.Generator(Generators.GuidComb); });
    }
}

public class ClientMap : BaseMapping<Client>
{
    public ClientMap()
    {
        this.Property(x => x.Name);
        this.Property(x => x.Abbreviation);
        this.Property(x => x.ClientSinceDate, map => map.NotNullable(true));
        this.Property(x => x.ClientUntilDate);
        this.Property(x => x.City);
        this.Property(x => x.Website);

        this.Bag<Department>(x => x.Departments, colmap =>
        {
            colmap.Key(x => x.Column("ClientId"));
            colmap.Inverse(true);
            colmap.Cascade(Cascade.All | Cascade.DeleteOrphans);
        }, map =>
        {
            map.OneToMany();
        });

        this.Bag<ClientCountry>(x => x.ClientCountries, colmap =>
        {
            colmap.Cascade(Cascade.All | Cascade.DeleteOrphans);
            colmap.Key(p => p.Column("ClientId"));
            colmap.Inverse(true);
        }, map =>
        { 
            map.OneToMany();
        });

        this.Bag<Contact>(x => x.Contacts, colmap =>
        {
            colmap.Key(x => x.Column("ClientId"));
            colmap.Cascade(Cascade.All | Cascade.DeleteOrphans);
        }, map =>
        {
            map.OneToMany();
        });
    }
}

public class CountryMap : BusinessRefEntityMapping<Country>
{
    public CountryMap()
    {
        Bag<ClientCountry>(x => x.ClientCountries, colmap =>
        {
            colmap.Cascade(Cascade.All);
            colmap.Key(p => p.Column("CountryId"));
        }, map =>
        {
            map.OneToMany();
        });
    }
}

public class ClientCountryMap : BaseMapping<ClientCountry>
{
    public ClientCountryMap()
    {
        Property(x => x.ValidFrom);
        Property(x => x.ValidTo);

        Property(x => x.ClientId, map =>
        {
            map.Column("ClientId");
            map.Insert(false);
            map.Update(false);
            map.NotNullable(true);
        });
        Property(x => x.CountryId, map =>
        {
            map.Column("CountryId");
            map.Insert(false);
            map.Update(false);
            map.NotNullable(true);
        });

        ManyToOne<Client>(x => x.Client, map =>
        {
            map.Column("ClientId");
            map.Cascade(Cascade.All);
            map.Insert(true);
            map.Update(true);
            map.NotNullable(true);
        });

        ManyToOne<Country>(x => x.Country, map =>
        {
            map.Column("CountryId");
            map.Cascade(Cascade.All);
            map.Insert(true);
            map.Update(true);
            map.NotNullable(true);
        });
    }
}

The js code:

 $scope.create = function (index) {

    var c = $scope.clients[index];

    var newClientCountry = breezeService.manager.createEntity('ClientCountry', {
        ValidFrom: Date(2013, 01, 01),
        ValidTo: Date(2015, 01, 01),
        Client: c,
        Country: country,
    });


    breezeService.manager.saveChanges()
    .then(function (data) {
        $log.info('client created');
    })
    .fail(function (dat) {
        $log.error('save client failed:' + data)
    })
}

The issue: With NHibernate, saving a clientcountry results in this error message :"not-null property references a null or transient value CdT.EAI.DAL.ClientCountry.Country". With EF, all works as expected.

Is there something wrong with my code?


回答1:


So, since there is no feedback yet, here's what we did to make it (more or less) work:

  • First, to be able to save a new ClientCountry(many to many with exposed junction table), we must do this:

    public class ClientCountryMap : BaseMapping<ClientCountry>
    {
        public ClientCountryMap()
        {
            Property(x => x.ValidFrom);
            Property(x => x.ValidTo);
            Property(x => x.ClientId, map =>
            {
                map.Column("ClientId");
                map.Insert(true);
                map.Update(true);
                map.NotNullable(true);
            });
            Property(x => x.CountryId, map =>
            {
                map.Column("CountryId");
                map.Insert(true);
                map.Update(true);
                map.NotNullable(true);
            });
            ManyToOne<Client>(x => x.Client, map =>
            {
                map.Column("ClientId");
                map.Cascade(Cascade.All);
                map.Insert(false);
                map.Update(false);
                map.NotNullable(true);
            });
            ManyToOne<Country>(x => x.Country, map =>
            {
                map.Column("CountryId");
                map.Cascade(Cascade.All);
                map.Insert(false);
                map.Update(false);
                map.NotNullable(true);
            });
        }
    }
    

    The difference here is that the insert/update are inverted. Insert(true) and Update(true) must be set on the foreign key mapping and not the association. It differs from the breeze doc on that point.

  • The second is that Inverse(true) must be set on each collection association or all your collections will be deleted when updating the parent entity.

With that,changes, it seems to work as expected.

ps: It is fixed in the latest version (>1.4.8). The comments above don't apply anymore.



来源:https://stackoverflow.com/questions/20684118/breeze-differences-between-entity-framework-and-nhibernate-with-many-to-many

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!