让2个不同的映射文件使用相同的域

时间:2011-04-04 09:59:03

标签: c# nhibernate mapping

我正在处理的应用程序需要支持3个数据库。 Sybase,SQL和Oracle。

我们现在遇到Oracle的问题。至于oracle,我们使用序列作为id生成器,对于Sybase和Oracle,我们使用自己的实现来生成ID。

我们遇到一个由双主键组成的表的问题。我的映射文件由复合ID组成。但是在复合ID中,您无法使用生成的值。对于Oracle和Sybase,这是没有问题的,因为我手动创建新的id。但是对于Oracle,我确实遇到了问题,因为需要使用序列。

所以我正在考虑的是创建一个特定于Oracle的第二个映射文件,它只使用1个字段作为PK。我的查询不正确,但我没有看到任何其他选项。

所以我有2个映射文件:

Sybase和SQL:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-cascade="none" xmlns="urn:nhibernate-mapping-2.2"
    namespace="VS3.Domain.Address" assembly="VS3.Domain">

<class name="Top_Address" table="TOP_ADDRESS">
<composite-id name="ID" class="IDComposite_TopAddress">
  <key-property name="TnrAddress" column="TNR_ADDRESS" type="Int32"/>
  <key-many-to-one name="TopIdentity" class="Top_Identity" column="TNR_ID"/>
</composite-id>

<property name="nmAddress" column="NM_ADDRESS" type="String" not-null="true"/>
<property name="dPosx" column="D_POSX" type="Decimal" not-null="false"/>
<property name="dPosy" column="D_POSY" type="Decimal" not-null="false"/>

<property name="nmStreetAdd" column="NM_STREET_ADD" type="String" not-null="false"/>
<property name="nmStreetAdd2" column="NM_STREET_ADD2" type="String" not-null="false"/>
<property name="nmStreetAdd3" column="NM_STREET_ADD3" type="String" not-null="false"/>

<!-- Added By Transics -->
<property name="HouseNumber" column="CNR_HOUSE_ADD" type="String" not-null="false" />
<property name="BusNumber" column="CNR_BUS_ADD" type="String" not-null="false" />
<property name="PostalCode" column="CNR_ZIP" type="String" not-null="false" />
<property name="City" column="NM_ZIP" type="String" not-null="false" />
<property name="Country" column="COD_COUNTRY" type="String" not-null="false" />
<property name="Email" column="CNR_EMAIL_ADD" type="String" not-null="false" />
<property name="Phone" column="CNR_PHONE_ADD" type="String" not-null="false" />
<property name="CellPhone" column="CNR_GSM_ADD" type="String" not-null="false" />
<property name="NmAddress2" column="NM_ADDRESS2" type="String" not-null="false" />

<property name="Active" column="BOL_ACTIVE_ADD" type="Int32" not-null="false" />
<property name="Language" column="COD_LANG" type="String" not-null="false" />
<property name="AmFrom" column="T_TRB_AM_FROM" type="DateTime" not-null="false" />
<property name="AmUntil" column="T_TRB_AM_UNTIL" type="DateTime" not-null="false" />
<property name="PmFrom" column="T_TRB_PM_FROM" type="DateTime" not-null="false" />
<property name="PmUntil" column="T_TRB_PM_UNTIL" type="DateTime" not-null="false" />
<property name="Remark" column="DES_TRB_REMARK" type="String" not-null="false" />
<property name="ExtraInformation" column="TXT_ADDRESS" type="String" not-null="false" />
<property name="AddressType" column ="COD_ADDRESSTYPE" type="Char" not-null="false" />
<property name="AddressOrPlace" column ="COD_TRB_ADDRESSTYPE" type="String" not-null="false" />

</class>
</hibernate-mapping>

甲骨文:

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping default-cascade="none" xmlns="urn:nhibernate-mapping-2.2"
    namespace="VS3.Domain.Address" assembly="VS3.Domain">

<class name="Top_Address_Oracle" table="TOP_ADDRESS">
<id name="TnrAddress" column="TNR_ADDRESS" type="Int32">
  <generator class="sequence" />
</id>

<property name="nmAddress" column="NM_ADDRESS" type="String" not-null="true"/>
<property name="dPosx" column="D_POSX" type="Decimal" not-null="false"/>
<property name="dPosy" column="D_POSY" type="Decimal" not-null="false"/>

<property name="nmStreetAdd" column="NM_STREET_ADD" type="String" not-null="false"/>
<property name="nmStreetAdd2" column="NM_STREET_ADD2" type="String" not-null="false"/>
<property name="nmStreetAdd3" column="NM_STREET_ADD3" type="String" not-null="false"/>
<property name="HouseNumber" column="CNR_HOUSE_ADD" type="String" not-null="false" />
<property name="BusNumber" column="CNR_BUS_ADD" type="String" not-null="false" />
<property name="PostalCode" column="CNR_ZIP" type="String" not-null="false" />
<property name="City" column="NM_ZIP" type="String" not-null="false" />
<property name="Country" column="COD_COUNTRY" type="String" not-null="false" />
<property name="Email" column="CNR_EMAIL_ADD" type="String" not-null="false" />
<property name="Phone" column="CNR_PHONE_ADD" type="String" not-null="false" />
<property name="CellPhone" column="CNR_GSM_ADD" type="String" not-null="false" />
<property name="NmAddress2" column="NM_ADDRESS2" type="String" not-null="false" />

<property name="Active" column="BOL_ACTIVE_ADD" type="Int32" not-null="false" />
<property name="Language" column="COD_LANG" type="String" not-null="false" />
<property name="AmFrom" column="T_TRB_AM_FROM" type="DateTime" not-null="false" />
<property name="AmUntil" column="T_TRB_AM_UNTIL" type="DateTime" not-null="false" />
<property name="PmFrom" column="T_TRB_PM_FROM" type="DateTime" not-null="false" />
<property name="PmUntil" column="T_TRB_PM_UNTIL" type="DateTime" not-null="false" />
<property name="Remark" column="DES_TRB_REMARK" type="String" not-null="false" />
<property name="ExtraInformation" column="TXT_ADDRESS" type="String" not-null="false" />
<property name="AddressType" column ="COD_ADDRESSTYPE" type="Char" not-null="false" />
<property name="AddressOrPlace" column ="COD_TRB_ADDRESSTYPE" type="String" not-null="false" />

<many-to-one name="Identity" class="Identity" column="TNR_ID" not-null="true"/>
</class>
</hibernate-mapping>

有什么方法可以确保这些映射文件使用相同的Domain类?或者我是否也需要创建一个新域(就像我目前所做的那样)。

但那不是一个真正的选择。会有很多工作。

有人有解决方案吗?

2 个答案:

答案 0 :(得分:1)

对于同一个域类,您可以拥有两个映射文件 - 如果您在执行代码期间只需要一个(我假设,因为同时使用两个不同的数据库同时使用它们将是一场噩梦表和域类)。在创建SessionFactory之前,您可以根据参数筛选hbm文件并仅获取所需的文件。

您需要Oracle的映射文件和其他的映射文件,例如: MyEntity.oracle.hbm.xmlMyEntity.default.hbm.xml。对于那些在两种情况下完全相同的类,您只需将它们保持原样,例如: MyCommonEntity.hbm.xml

这是我在项目中使用的稍微修改过的代码示例。 (我在另一个问题上发布了另一个版本的SO:Dynamically change the id generator to “assigned” in NHibernate class mapping

private ISessionFactory BuildSessionFactory(bool useOracleMapping)
{
    Configuration config = new Configuration();

    config.SetProperty(NHibernate.Cfg.Environment.ConnectionProvider, "...");
    config.SetProperty(NHibernate.Cfg.Environment.Dialect, "...");
    config.SetProperty(NHibernate.Cfg.Environment.ConnectionDriver, "...");
    config.SetProperty(NHibernate.Cfg.Environment.ConnectionString, "...");
    config.SetProperty(NHibernate.Cfg.Environment.Isolation, "Serializable");
    config.SetProperty(NHibernate.Cfg.Environment.ProxyFactoryFactoryClass, "...");
    config.SetProperty(NHibernate.Cfg.Environment.ShowSql, "true");
    config.SetProperty(NHibernate.Cfg.Environment.Hbm2ddlKeyWords, "none");

    // filter hbm Files

    // Set reference to entity assembly
    System.Reflection.Assembly assembly = System.Reflection.Assembly.GetAssembly(typeof(MyEntity));

    // get Resource-files
    string[] resources = assembly.GetManifestResourceNames();

    // scan through all the hbm files and filter them according to the parameter
    foreach (string hbmFile in resources)
    {
        // This filtering here could probably be done simpler, but this is easy to understand
        bool addFile = false;
        // ignore any file that does not end with .hbm.xml
        if (hbmFile.EndsWith(".hbm.xml"))
        {
            if (hbmFile.ToLower().EndsWith(".default.hbm.xml"))
            {
                if (!useOracleMapping)
                {
                    // we want that file for this SessionFactory
                    addFile = true;
                }
            }
            else if (hbmFile.ToLower().EndsWith(".oracle.hbm.xml"))
            {
                if (useOracleMapping)
                {
                    // we want that file for this SessionFactory
                    addFile = true;
                }
            }
            else
            {
                // neither default nor oracle -> we want that file no matter what
                addFile = true;
            }
            if (addFile)
            {
                using (System.IO.StreamReader sr = new System.IO.StreamReader(assembly.GetManifestResourceStream(hbmFile)))
                {
                    string resourceContent = sr.ReadToEnd();
                    config.AddXmlString(resourceContent);
                }
            }
        }
    }

    // create Sessionfactory with the files we filtered
    ISessionFactory sessionFactory = config.BuildSessionFactory();
    return sessionFactory;
}

修改

假设您始终可以访问有关Oracle或其他模式的知识,我会将GetTop_AddressById()方法包装起来并执行以下操作:

public Top_Address GetTop_AddressById(IDComposite_TopAddress id)
{
    if (!oracle)
    {
        return session.CreateCriteria(DB, typeof(Top_Address))
           .Add(Restrictions.Eq("ID.TnrAddress", addressID))
           .Add(Restrictions.Eq("ID.TopIdentity.tnrId", tnrID))
           .Add(Restrictions.Eq("AddressType", 'R')) .UniqueResult<Top_Address>();
    }
    else 
    {
        return session.CreateCriteria(DB, typeof(Top_Address))
           .Add(Restrictions.Eq("TnrAddress", addressID))
           .Add(Restrictions.Eq("AddressType", 'R')) .UniqueResult<Top_Address>();
    }
}

这可能不是很漂亮,但我认为,为同一目的设两个课程会更糟糕。对于使用id的所有CRUD操作,您将需要这样的包装器。当然,您还需要在域类中拥有TnrAddress属性。

答案 1 :(得分:0)

我建议你不要对映射进行分叉,而是: 通过反序列化的映射元数据,在创建SessionFacorty之前,添加所需的修改。我不能给你代码,但在这里你可以抓住这个想法:http://fabiomaulo.blogspot.com/2010/01/map-nhibernate-using-your-api.html

或者

创建一个自定义id生成器,这里面处理两个关键系统之间的区别: http://nhforge.org/wikis/howtonh/creating-a-custom-id-generator-for-nhibernate.aspx