1

在我的核心程序集中假设这个简单的域:

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;
    }
}

使用实体框架,Province 域对象将同时具有 Country 和相应的 CountryId 属性。我可以通过简单地设置 CountryId 来分配国家。

使用 NHibernate,创建域时不需要外键的 id。那么如何将 ProvinceModel CountryId 映射回 Country 对象呢?

我已经通过各种步骤将事物抽象为接口并使用依赖注入。我应该使用映射扩展中的服务定位器并进行查找吗?我应该在映射扩展之外查找国家/地区并将其作为扩展方法的参数吗?处理这种情况的推荐方法是什么?


其次,他们建议使用 NHibernate 向域对象添加辅助函数以保持关联(不是积极的,但我认为 EF 会“自动”为我处理这个问题)。例如,我会SetCountry在 Province 上添加一个方法,AddProvinceRemoveProvinceCountry 上添加一个方法。

这不会影响性能吗?不是简单地为一个省设置国家(这是管理协会的地方),而是加载新国家的省份的整个列表,看看它是否已经在列表中,然后再添加到集合中,然后是整个列表加载旧的 Country's Provinces 以查看该省是否需要从集合中删除。

4

1 回答 1

1

[在 EF 中] 我可以通过简单地设置 CountryId 来分配国家。

这不是真的,在我看来这是实体框架的一个主要缺陷。同时拥有 Country 和 CountryId 属性是一种技巧,它允许您设置 Country 而无需通过设置 CountryId 从数据库中检索它。在 Web 应用程序中,这是可行的,因为记录是使用 CountryId 外键集保存的,因此下次加载它时会填充 Country。NHibernate 对这种模式的解决方案是ISession.Load创建动态代理的方法。

在你的例子中,你会做类似的事情

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

至于您的第二个问题,通常我只使用方法来封装对集合的访问。这确保了集合本身不会被 setter 替换,并允许我维护关系的双方。我将其建模为:

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)最初是为有状态应用程序设计的,并且许多使用模式在无状态 Web 应用程序中并不是绝对必要的。但是,我会在偏离其中一些模式之前分析性能。例如,您可能希望在提交对象之前对其进行验证,这要求内存中的表示是一致的。

于 2013-09-19T15:36:25.347 回答