C# 将域模型映射到具有不同主键数据类型的不同持久性存储

C# 将域模型映射到具有不同主键数据类型的不同持久性存储,c#,entity-framework,ef-code-first,dynamics-crm-2011,C#,Entity Framework,Ef Code First,Dynamics Crm 2011,我需要为不同的客户开发一个应用程序,该应用程序需要针对不同的现有遗留数据库作为持久性存储,或者也可以使用自己的独立数据库完全独立运行 因此,我的方法是: 独立于最终的持久性存储开发域模型 如果需要,使用EF Code First存储库实现将其映射到自己的独立数据库 如果需要,使用其他存储库实现将其映射到遗留数据库系统 我知道一个事实,现有的目标系统之一是CRM 2011系统。“CRMRepository”实现最好使用MicrosoftCRM SDK,而不是直接针对底层SQL数据库 但是,CR

我需要为不同的客户开发一个应用程序,该应用程序需要针对不同的现有遗留数据库作为持久性存储,或者也可以使用自己的独立数据库完全独立运行

因此,我的方法是:

  • 独立于最终的持久性存储开发域模型
  • 如果需要,使用EF Code First存储库实现将其映射到自己的独立数据库
  • 如果需要,使用其他存储库实现将其映射到遗留数据库系统
我知道一个事实,现有的目标系统之一是CRM 2011系统。“CRMRepository”实现最好使用MicrosoftCRM SDK,而不是直接针对底层SQL数据库

但是,CRM使用GUID作为其所有实体的主键,而其他数据库系统将主要使用整数

因此,我有点困惑,在将域实体映射到存储库实现中的持久性存储时,设计域实体以避免以后遇到问题的最佳方法是什么

一个典型的域实体是

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
  • 对于独立解决方案:使用基于代码优先的EF存储库,没有问题
  • 对于以INT作为主键的现有数据库:将其映射到正确的主键属性,没有问题
  • 但是,如何定位CRM后端?“Person”实体通常会映射到CRM中的“Account”实体,但是PK是accountid,它是一个GUID,与所有其他CRM实体一样
我是否应该将域实体更改为使用字符串作为主键属性,以便它可以容纳所有类型的数据类型,然后在每个存储库实现中执行与正确数据类型之间的转换?还是有其他更好的方法来解决这个问题

我想到的一件事是将我的所有“Id”属性声明为type object,然后使用repository模式和Automapper来实现特定的实现,并将我的域对象映射到EFRepository实现中的例如EF实体(其pk类型为int)

对于存储库的CRM实现,repo将使用CRM SDK并在CRM内部使用的GUID上映射“对象Id”

这是一个可行的解决方案,还是太牵强了

谢谢

编辑1:我对商业解决方案持开放态度。在这里使用LLBLGen会是一种选择吗?没有使用它的经验,但它似乎不允许您将相同的域定义重用到单独的“存储库”实现中,还是我错了

编辑2:当前解决方案结构概述,尝试遵循洋葱架构。不同的存储库实现将进入“基础架构”。然后,存储库实现将由客户通过DI“可插入”


看起来您有两个选择

  • 将Id定义为一个对象,那么存储库使用什么并不重要,它只希望它与保存的时间相同。这样做的代价很小,只需要显式强制转换和处理失败

  • 创建
    int-Id
    Guid
    。这消除了显式强制转换的成本,并可能有助于类型安全。也意味着如果其他系统使用
    字符串
    作为id,您将遇到问题


  • 我也有类似的情况。不同类型的公司拥有不同的信息,处理方式也不同,并映射到以Int和GUI作为主键的旧表中

    我最终得到的是一个带有GUID主键和所有共享公共数据的base(设置为abstract in EF)表,然后每个类型都有一个继承表(这些表的主键是base上的外键)。在这些表中,我能够声明关于特定类型的任何特定信息,并将它们链接到其他旧表或新表

    在您的情况下,您可以有一个基本客户类型,但随后根据需要声明尽可能多的不同类型的客户,并在需要时对其进行扩展和链接。然后,您可以将其作为目标:

    var baseCust = db.Customers.FirstOrDefault(x => x.Id == someId);
    if(baseCust is CustTypeA)
    {
       // Access extended properties and Legacy tables (via lazy loading)
       var custA = (CustTypeA)baseCust;
       custA.SomeExtendedProperty = blah;
       var oldCompletedOrders = custA.Orders.Where(x => x.Completed).ToList();
       //etc
    
    这给了我们前进的灵活性,让我们能够接触到他们想看到的所有旧垃圾


    让我的所有域对象从抽象基类实体继承,然后使用属性“publictkey Id{get;set;}”,怎么样。或者,如果我允许在不同的域对象上使用不同类型的键,甚至是基于每个类。这是一个可行的解决方案吗?请记住,存储库的实现可以是从EF到NHibernate再到像CRM这样的定制回购的任何东西

    您可以实现几个接口和一个基本存储库(我已经阅读了类似的东西)。请考虑下面的LIQPAD脚本(我只使用转储用于验证目的):

    void Main()
    {
    var personList=新列表
    {
    新PersonSql
    {
    Id=1,
    FirstName=“Some”,
    LastName=“个人”
    }
    }.AsQueryable();
    var repo=新的PersonRepo();
    repo.Query=个人列表;
    repo.GetById(1.Dump();
    }
    //在此处定义其他方法和类
    //实体库
    类BaseEntity
    {
    公钥Id{get;set;}
    }
    //常见结构
    类PersonBase:BaseEntity
    {
    公共字符串名{get;set;}
    公共字符串LastName{get;set;}
    }
    //id为int类型的实际实体
    类PersonSql:PersonBase
    {
    }
    //等等
    类PersonNoSql:PersonBase
    {
    }
    //在创建映射类时,您可能会根据数据源生成这些或对它们进行编码。
    //存储库
    界面假定
    其中tenty:BaseEntity
    {
    IQueryable查询{get;set;}
    TEntity GetById(TKeyI键);
    //这里还有其他存储库方法
    }
    抽象类RepoBase:IRep
    
    void Main()
    {
        var personList = new List<PersonSql>
        {
            new PersonSql
            {
                Id = 1,
                FirstName = "Some",
                LastName = "Person"
            }
        }.AsQueryable();
    
        var repo = new PersonRepo();
        repo.Query = personList;
    
        repo.GetById(1).Dump();
    }
    
    // Define other methods and classes here
    
    //entity base
    class BaseEntity<TKey>
    {
        public TKey Id { get; set; }
    }
    
    
    //common constructs
    class PersonBase<Tkey> : BaseEntity<Tkey>
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
    
    //actual entity with type of int as the id
    class PersonSql : PersonBase<int>
    {
    
    }
    
    //and so on
    class PersonNoSql : PersonBase<string>
    {
    
    }
    
    //likley you would generate these or code them based on your data source when creating your mapping classes.
    
    //repositories
    
    interface IRepository<TEntity, TKeyI>
        where TEntity : BaseEntity<TKeyI>
    {
        IQueryable<TEntity> Query {get; set;}
        TEntity GetById(TKeyI key);
        //Other Repository Methods here
    }
    
    
    abstract class RepoBase<TBaseEntity, TKeyB> : IRepository<TBaseEntity, TKeyB>
        where TBaseEntity : BaseEntity<TKeyB>
    {
        //Base Implementations Here
        public IQueryable<TBaseEntity> Query { get; set; }
        public virtual TBaseEntity GetById(TKeyB key)
        {
            throw new NotImplementedException();
        }
    }
    
    abstract class PersonRepoBase<TkeyType> : RepoBase<PersonBase<TkeyType>, TkeyType>
    {
        public override PersonBase<TkeyType> GetById(TkeyType key)
        {
            //Get a person.
            throw new NotImplementedException();
        }
    }
    
    //class PersonRepo : RepoBase<PersonNoSql, string>
    //{
    //  public override PersonNoSql GetById(string id)
    //  {
    //      throw new NotImplementedException();
    //  }
    //}
    
    class PersonRepo : RepoBase<PersonSql, int>
    {
        public override PersonSql GetById(int id)
        {
            return Query.First(x => x.Id == id);
            throw new NotImplementedException();
        }
    }