2013-09-18 2 views
1

Пусть этот простой домен в моей основной сборке:Nhibernate домена Ojbect/View Model Mapping (один-ко-многим)

public class Country 
{ 
    protected ICollection<Province> _provinces = null; 

    public virtual int Id { get; protected set; } 
    public virtual string Name { get; set; } 
    public virtual string IsoCode2 { get; set; } 
    public virtual string IsoCode3 { get; set; } 
    public virtual int IsoCodeNumeric { get; set; } 
    public virtual ICollection<Province> Provinces 
    { 
     get { return _provinces ?? (_provinces = new List<Province>()); } 
     set { _provinces = value; } 
    } 
} 

public class Province 
{ 
    public virtual int Id { get; protected set; } 
    public virtual Country Country { get; set; } 
    public virtual string Name { get; set; } 
    public virtual string Abbreviation { get; set; } 
} 

Модели вид в моем представлении слоя являются почти то же самое:

public class CountryModel 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public string IsoCode2 { get; set; } 
    public string IsoCode3 { get; set; } 
    public int IsoCodeNumeric { get; set; } 
    public int NumberOfProvinces { get; set; } 
} 

public class ProvinceModel 
{ 
    public int Id { get; set; } 
    public int CountryId { get; set; } 
    public string Name { get; set; } 
    public string Abbreviation { get; set; } 
} 

Я создаю некоторые методы расширения для отображения вперед и назад между объектами домена/просмотр моделей:

public static class Extensions 
{ 
    public static Country ToEntity(this CountryModel model, Country entity = null) 
    { 
     if (entity == null) 
      entity = new Country(); 
     entity.Name = model.Name; 
     entity.IsoCode2 = model.IsoCode2; 
     entity.IsoCode3 = model.IsoCode3; 
     entity.IsoCodeNumeric = model.IsoCodeNumeric; 
     entity.AddressFormat = model.AddressFormat; 
     entity.CanBillTo = model.CanBillTo; 
     entity.CanShipTo = model.CanShipTo; 
     entity.IsPublished = model.IsPublished; 
     return entity; 
    } 

    public static CountryModel ToModel(this Country entity, bool includeProvinceCount = false, CountryModel model = null) 
    { 
     if (model == null) 
      model = new CountryModel(); 
     model.Id = entity.Id; 
     model.Name = entity.Name; 
     model.IsoCode2 = entity.IsoCode2; 
     model.IsoCode3 = entity.IsoCode3; 
     model.IsoCodeNumeric = entity.IsoCodeNumeric; 
     model.AddressFormat = entity.AddressFormat; 
     model.CanBillTo = entity.CanBillTo; 
     model.CanShipTo = entity.CanShipTo; 
     model.IsPublished = entity.IsPublished; 
     if (includeProvinceCount) 
      model.NumberOfProvinces = entity.Provinces.Count; 
     return model; 
    } 

    public static Province ToEntity(this ProvinceModel model, Province entity = null) 
    { 
     if (entity == null) 
      entity = new Province(); 
     //entity.Country = LoadCountryById(model.CountryId); ???? <-- HERE 
     entity.Name = model.Name; 
     entity.Abbreviation = model.Abbreviation; 
     entity.CanBillTo = model.CanBillTo; 
     entity.CanShipTo = model.CanShipTo; 
     entity.IsPublished = model.IsPublished; 
     return entity; 
    } 

    public static ProvinceModel ToModel(this Province entity, ProvinceModel model) 
    { 
     if (model == null) 
      model = new ProvinceModel(); 
     model.Id = entity.Id; 
     model.CountryId = entity.Country.Id; 
     model.Name = entity.Name; 
     model.Abbreviation = entity.Abbreviation; 
     model.CanBillTo = entity.CanBillTo; 
     model.CanShipTo = entity.CanShipTo; 
     model.IsPublished = entity.IsPublished; 
     return model; 
    } 
} 

С Ent что в доменном объекте провинции были бы как Страна, так и соответствующие свойства CountryId. Я мог бы назначить страну, просто установив CountryId.

С NHibernate идентификатор внешнего ключа не требуется при создании домена. Итак, как вы нанесете карту RegionModel CountryId обратно в объект Country?

Я прошел всевозможные шаги, чтобы абстрагировать вещи на интерфейсы и использовать инъекции зависимостей. Должен ли я использовать локатор сервисов из расширений отображения и искать его? Должен ли я искать страну за пределами расширения отображения и требовать ее в качестве параметра метода расширения? Каковы рекомендуемые способы передачи этого сценария?


Во-вторых, с NHibernate они рекомендуют добавлять вспомогательные функции для объектов предметной области в целях сохранения ассоциаций (не уверен, но я думаю, что EF обрабатывает это «автомагически» для меня). Например, я бы добавил метод SetCountry на провинцию, и AddProvince и RemoveProvince методов по стране.

Разве это не ухудшает производительность? Вместо того, чтобы просто устанавливать страну для провинции (где находится ассоциация), весь список новых провинций страны загружается, чтобы увидеть, есть ли он в списке перед добавлением в коллекцию, а затем весь список старые провинции страны загружаются, чтобы посмотреть, нужно ли удалить провинцию из коллекции.

ответ

1

[in EF] Я могу назначить страну, просто установив CountryId.

Это неправда, и, на мой взгляд, это серьезный дефект с Entity Framework. Наличие свойств Country и CountryId - это взлом, который позволяет вам установить страну без извлечения из базы данных, установив CountryId. В веб-приложении это работает, потому что запись сохраняется с помощью внешнего ключа CountryId, поэтому при следующем загрузке в нее страна заполняется. Решение NHibernate для этого шаблона - это метод ISession.Load, который создает динамический прокси.

В вашем примере вы могли бы сделать что-то вроде

province.Country = session.Load<Country>(provinceModel.CountryId); 

Что касается вашего второго вопроса, в общем, я только использовать методы инкапсуляции доступа к коллекциям. Это гарантирует, что сама коллекция не будет заменена установщиком и позволит мне поддерживать обе стороны отношений. Я бы это сделал так:

public class Country 
{ 
    private ICollection<Province> _provinces; 

    public Country() 
    { 
     _provinces = new HashSet<Province>(); 
    } 

    public virtual IEnumerable<Province> Provinces 
    { 
     get { return _provinces; } 
    } 

    public virtual void AddProvince(Province province) 
    { 
     province.Country = this; 
     _provinces.Add(province); 
    } 

    public virtual void RemoveProvince(Province province) 
    { 
     province.Country = null; 
     _provinces.Remove(province); 
    } 
} 

Как вы отметили, это требует загрузки коллекции. Вы должны помнить, что NHibernate (и Hibernate) изначально были предназначены для приложений с поддержкой состояния, и многие шаблоны использования не обязательно необходимы в веб-приложениях без состояния.Тем не менее, я бы профиль производительности, прежде чем отклоняться от некоторых из этих шаблонов. Например, вы можете проверить свои объекты перед их выполнением и потребовать, чтобы представления в памяти были согласованными.

+0

Благодарим Вас за отзыв Jamie. Что касается вашего предложения по первой проблеме: он вводит зависимость от NHibernate в моем слое Presentation. Я пытался избежать этого, хотя мне известно о мыслях Айенде о репозиториях и абстрагировании от NH. Единственный способ, с помощью которого я могу предотвратить это, - передать страну как параметр расширения отображения (я могу получить его из хранилища заранее). – Sam

+0

На второй части это то, что я делал. Но тогда я был обеспокоен тем, что установление страны для провинции приведет к загрузке всех провинций для старой страны и новой страны. Хотя это не очень важно в этом сценарии, это может быть для больших коллекций на других объектах домена. Я не был уверен, есть ли способ избежать этого. – Sam

+0

В ответ на ваш 1-й комментарий, я считаю, что контроллер MVC отвечает за управление границами транзакций и поэтому нуждается в знаниях механизма сохранения. На ваш второй комментарий он должен загружать провинции только для 1-й страны. Существует метод утилиты для проверки того, была ли загружена коллекция, и в этом случае вам просто нужно установить ссылку на другом конце, но это вводит зависимость NHibernate в модель домена, и у меня есть свои ограничения. :-) –