使用EF

时间:2015-07-26 11:47:49

标签: c# entity-framework repository domain-driven-design

我尝试使用Persistance和Domain模型实现存储库。我的主要目标是将域逻辑与其他层分开,例如洋葱架构。

作为我的存储库,我使用Entity Framework 6.2。 要在模型之间进行映射,我使用Automapper。

我不想将实体模型用作我的域模型。

我对DDD中存储库的概念和想法有很多了解,浏览了很多例子,但没有一个能给我一个如何处理更复杂模型的答案。

例如,在我的游乐场控制台应用程序中,我AppUser包含BankAccounts。当我向用户添加新的BankAccount时,我会获得一个额外的行,其中包含未跟踪的BankAccount实体。这是因为从Domain模型映射到Persistance模型(又称实体)时,EF无法确定跟踪哪些BankAccounts并将它们全部视为新的。

+------------------------------------+
|Id |BankAccountNumber   |AppUser_Id |
+------------------------------------+
|1  |Seeded Bank Account |  1        |
+------------------------------------+

+------------------------------------+
|Id |BankAccountNumber   |AppUser_Id |
+------------------------------------+
|1  |Seeded Bank Account |  NULL     |
+------------------------------------+
|2  |Added Bank Account  |  1        |
+------------------------------------+
|3  |Seeded Bank Account |  1        |
+------------------------------------+

那是我的代码。

class Program
{
    static void Main(string[] args)
    {
        CreateMappings();

        var appUserRepository = new AppUserRepository();

        var appUser = appUserRepository.Get(1);

        var bankAccount = new BankAccountDM
        {
            BankAccountNumber = "Added Bank Account"
        };

        var service = new AppUserDomainSerivce(appUserRepository);
        service.AddBankAccount(bankAccount, appUser);

        Console.WriteLine("== END ==");
        Console.ReadKey();
    }

    public static void CreateMappings()
    {
        // Config Automapper
        Mapper.CreateMap<AppUserDM, AppUser>()
            .ForMember(x => x.Id, opt => opt.MapFrom(y => y.Id))
            .ForMember(x => x.Name, opt => opt.MapFrom(y => y.Name))
            .ForMember(x => x.BankAccounts, opt => opt.MapFrom((y => y.BankAccounts)))
            .ReverseMap();

        Mapper.CreateMap<BankAccountDM, BankAccount>()
            .ReverseMap();
    }

    public class AppUserDomainSerivce
    {
        private readonly AppUserRepository _appUserRepository;

        public AppUserDomainSerivce(AppUserRepository appUserRepository)
        {
            _appUserRepository = appUserRepository;
        }

        public void AddBankAccount(BankAccountDM bankAccount, AppUserDM appUser)
        {
            appUser.BankAccounts.Add(bankAccount);

            // Save changes
            _appUserRepository.Save(appUser);
        }
    }

    public class AppUserRepository
    {
        public AppUserDM Get(int id)
        {
            using (var ctx = new AppContext())
            {
                var userEntity = GetAppUser(id, ctx);
                return Mapper.Map<AppUserDM>(userEntity);
            }
        }

        public void Save(AppUserDM user)
        {
            // Save entity in storage
            using (var ctx = new AppContext())
            {
                var userEntity = GetAppUser(user.Id, ctx);
                userEntity = Mapper.Map(user, userEntity);

                ctx.SaveChanges();
            }
        }

        private AppUser GetAppUser(int id, AppContext ctx)
        {
            return ctx.AppUsers
                .Include("BankAccounts")
                .SingleOrDefault(x => x.Id == id);
        }
    }
}


// Entities and Domain Models

public class BankAccountDM
{
    public int Id { get; set; }
    public string BankAccountNumber { get; set; }
}

public class BankAccount
{
    [Key]
    public int Id { get; set; }
    public string BankAccountNumber { get; set; }
}

public class AppUserDM
{
    public int Id { get; set; }
    public List<BankAccountDM> BankAccounts { get; set; }
    public string Name { get; set; }
}

public class AppUser
{
    [Key]
    public int Id { get; set; }
    public virtual ICollection<BankAccount> BankAccounts { get; set; }
    public string Name { get; set; }
}


// Entity DbContext

public class AppContext : DbContext
{
    public AppContext() : base("AppContext")
    {
    }

    public DbSet<AppUser> AppUsers { get; set; }
}

public Configuration()
{
    AutomaticMigrationsEnabled = true;
}

protected override void Seed(ConsoleApplication1.AppContext context)
{
    context.AppUsers.Add(new AppUser()
    {
        // This AppUser will get ID = 1 once saved into DB
        Name = "My Test User",
        BankAccounts = new List<BankAccount>()
        {
            new BankAccount()
            {
                BankAccountNumber = "Seeded Bank Account"
            }
        }
    });
}

1 个答案:

答案 0 :(得分:0)

如果我使用你的方法(没有任何事件);在reposiyory的save方法中,我通常从db读取整个聚合,然后与给定的一个进行比较。

当然,我们检查从db读取的聚合版本是否与我们要保存的聚合相同。因此,存储库中的单个save方法可以处理域实体和值对象中的添加和删除。

或者您也可以在每个聚合实体值对象中保存一个状态(新的,修改的,删除的等)。

关于将聚合存储为json; this post from Vaugh Vernon可以是一个有用的开始。