2014-11-24 120 views
1

我在ASP.NET C#MVC應用程序使用實體框架。如何訪問數據映射到業務邏輯對象實體框架

我有由EF數據訪問層中生成的對象:

namespace Project1.DataAccess 
{ 
    using System; 
    using System.Collections.Generic; 

    public partial class User 
    { 
     public User() 
     { 
      this.Files = new HashSet<File>(); 
      this.Folders = new HashSet<Folder>(); 
     } 
     //... 

    } 
} 

現在,我想創建業務邏輯對象,然後與數據訪問的人將它們映射:

namespace Project1.Logic 
{ 
    public class User 
    { 
     public int Id { get; set; } 
    } 
} 

我在數據庫中的表的數量非常少。我需要使用Automapper嗎?如果不是,我該如何實現映射?

+0

爲什麼在BLL圖層用戶類中有一個'Id'屬性 - 出於好奇? – wahwahwah 2014-12-04 00:17:18

+0

是的,DA用戶的所有屬性也都在BLL用戶中。 – enb081 2014-12-04 07:50:50

回答

7

我用EF6所有的時間來從MSSQL表我的數據訪問層,然後創建一組對象代表我多麼希望我的代碼交互(或顯示它),這是POCO。 「映射」是通過實施Repository模式來完成的。下面是一個通用的界面,可以幫助我確保所有的回購類遵循相同的形狀。

public interface IDataRepository<T> 
    { 
     IQueryable<T> Get(); 
     T Get(int id); 
     T Add(T obj); 
     T Update(T obj); 
     void Delete(T obj); 
    } 

然後,我創建類似這樣的回購類。 (使用UserBusiness和UserDAL類)

public class NewRepo : IDataRepository<UserBusiness> 
{ 
    YourContext db = new YourContext(); 

    public IQueryable<UserBusiness> Get() 
    { 
     return (from u in db.UserDAL select new UserBusiness() 
     { 
      Id = u.Id, 
      Name = u.Name 
     }); 
    } 

    public UserBusiness Get(int id) 
    { 
     return (from u in db.UserDAL where u.Id == id select new UserBusiness() 
     { 
      Id = u.Id, 
      Name = u.Name 
     }).FirstOrDefault(); 
    } 

    public Order Add(UserBusiness obj) 
    { 
     UserDAL u= new UserDAL(); 
     u.Name = obj.Name; 

     db.UserDAL.Add(u); 
     db.SaveChanges(); 

     //Assuming the database is generating your Id's for you 
     obj.Id = u.Id; 

     return obj; 

    } 

    public Order Update(UserBusiness obj) 
    { 
     UserDAL u= new UserDAL(); 
     u.Id = obj.Id; 
     u.Name = obj.Name; 

     db.Entry(u).State = EntityState.Modified; 
     db.SaveChanges(); 

     return obj; 
    } 

    public void Delete(UserBusiness obj) 
    { 
     UserDAL u = db.UserDAL 
      .Where(o=>o.Id == obj.Id) 
      .FirstOrDefault(); 

     if (u!=Null) { 
      db.Entry(u).State = EntityState.Deleted; 
      db.SaveChanges(); 
     } 

    } 
} 

從你的應用程序中,您現在會使用你的回購類,而不是你的DbContext的方法。

最後,我經常最終添加另一層「服務類」,它與我的回購站進行交互,管理業務類的內部數據......或者您可以通過添加回購方法使您的業務類更「聰明」給他們。我的首選是保持POCO的愚蠢和建立服務類來獲取,設置和編輯屬性。

是的,有一堆左右映射將一個類「轉換」爲另一個類,但是這是後來對內部業務邏輯類的徹底分離。 POCO轉換的直接表首先看起來很愚蠢,但是等到您的DBA想要標準化幾個字段,或者您決定向這些簡單對象添加一個集合。能夠管理你的業務對象而不會破壞你的其他應用程序是無價的。


編輯:下面有資源庫,這使得創建新庫輕鬆很多的仿製版本。

這是基類所有業務邏輯層類:

public class BaseEntity 
{ 
    public int Id { get; set; } 
} 

所有數據訪問層類這是基類:

public class BaseEntityDAL 
{ 
    [Key] 
    [Column("Id")] 
    public int Id { get; set; } 
} 

這是庫通用基礎類(注意,我們在這裏使用AutoMapper):

public abstract class BaseRepository<TDAL, TBLL> : IRepository<TBLL> 
    where TDAL : BaseEntityDAL, new() 
    where TBLL : BaseEntity, new() 
{ 
    protected readonly MyDbContext context; 
    protected readonly DbSet<TDAL> dbSet; 

    protected virtual TDAL Map(TBLL obj) 
    { 
     Mapper.CreateMap<TBLL, TDAL>(); 
     return Mapper.Map<TDAL>(obj); 
    } 

    protected virtual TBLL Map(TDAL obj) 
    { 
     Mapper.CreateMap<TDAL, TBLL>(); 
     return Mapper.Map<TBLL>(obj); 
    } 

    protected abstract IQueryable<TBLL> GetIQueryable();    

    public BaseRepository(MyDbContext context, DbSet<TDAL> dbSet) 
    { 
     if (context == null) 
      throw new ArgumentNullException(nameof(context)); 
     if (dbSet == null) 
      throw new ArgumentNullException(nameof(dbSet)); 

     this.context = context; 
     this.dbSet = dbSet; 
    } 

    public TBLL Get(int id) 
    { 
     var entity = dbSet 
      .Where(i => i.Id == id) 
      .FirstOrDefault(); 

     var result = Map(entity); 

     return result; 
    } 

    public IQueryable<TBLL> Get() 
    { 
     return GetIQueryable(); 
    } 

    public TBLL Add(TBLL obj) 
    { 
     var entity = Map(obj); 

     dbSet.Add(entity); 
     context.SaveChanges(); 

     obj.Id = entity.Id; 

     return obj; 
    } 

    public TBLL Update(TBLL obj) 
    { 
     var entity = Map(obj); 

     context.Entry(entity).State = EntityState.Modified; 
     context.SaveChanges(); 

     return obj; 
    } 

    public void Delete(TBLL obj) 
    { 
     TDAL entity = dbSet 
      .Where(e => e.Id == obj.Id) 
      .FirstOrDefault(); 

     if (entity != null) 
     { 
      context.Entry(entity).State = EntityState.Deleted; 
      context.SaveChanges(); 
     } 
    } 
} 

最後,當我們得到以上所有,這是樣本存儲庫的實現,漂亮又幹淨:

public class ContractRepository : BaseRepository<ContractDAL, Contract> 
{ 
    protected override IQueryable<Contract> GetIQueryable() 
    { 
     return dbSet 
      .Select(entity => new Contract() 
      { 
       // We cannot use AutoMapper here, because Entity Framework 
       // won't be able to process the expression. Hence manual 
       // mapping. 
       Id = entity.Id, 
       CompanyId = entity.CompanyId, 
       ProjectId = entity.ProjectId, 
       IndexNumber = entity.IndexNumber, 
       ContractNumber = entity.ContractNumber, 
       ConclusionDate = entity.ConclusionDate, 
       Notes = entity.Notes 
      }); 
    } 

    public ContractRepository(MyDbContext context) 
     : base(context, context.Contracts) 
    { 

    } 
} 
+1

我修改了你的類並創建了一個通用版本,它簡化了創建新的存儲庫。我還編輯了你的文章,以包含這些類,這樣他們可以很容易地被其他人重用。希望你不介意。 – Spook 2015-10-09 19:40:52

+0

@Spook你的通用示例非常感謝,非常感謝。但是我有一個問題:我的印象是你的表示層(控制器)應該不瞭解你的DAL,只能使用BLL。但是在實現它之後,我注意到爲了在我的控制器中實例化ContractRepository並使用它,我需要將它傳遞給我的DbContext實例(這是我的DAL),我認爲我不應該做。您能否添加一個如何從控制器調用ContractRepository的快速示例? – Antrim 2016-02-24 16:00:51

+1

@Antrim使用依賴注入Luke :)如果您教授DI容器如何實例化數據庫上下文,它將自動將其提供給ContractRepository構造函數,而無需從表示層進行任何操作。另外,如果你教授容器如何實例化ContractRepository,你可以簡單地通過一個接口將其傳遞給控制器​​的構造函數,DI將完成剩下的工作。看看這裏:http://www.asp.net/mvc/overview/older-versions/hands-on-labs/aspnet-mvc-4-dependency-injection – Spook 2016-02-25 08:36:52

1

如果你的項目比較少,我會建議不使用的DTO在所有 - 相反,你可以使用實體框架代碼優先和跨多個層重用你的業務實體(只要確保代碼第一次實體放置一些常用的圖書館)。

否則,您可以創建自己的轉換方法或利用類似AutoMapper庫。

1

如果你想使用你的商業模式設計普通老式CLR對象,並將它們映射到數據庫中的表,你可以使用實體框架代碼優先的方法。在代碼中,首先,沒有什麼會爲你生成。但是,您將負責將業務對象映射到數據庫表和字段。您可以basiscally有兩種方式做到這一點:

,這兩種方法將產生相同的映射你,但我更喜歡流暢API的方法,因爲它提供了更強的地圖API,並保持你的BO,將在您的DataContext被集中任何映射邏輯無關。

但是...一旦你生成類,它們將被綁定映射用於你,這是數據庫第一種方法。因此,你可以擴展這些類,因爲它們是部分的。 可以在上EF由不同的工作流程,這個博客帖子的細節,這將有助於您使用您需求的一個發現:http://blog.smartbear.com/development/choosing-the-right-entity-framework-workflow/

0

如果你的DAL和BLL User對象是正是一樣,你可以使用這樣的功能進行映射:

public void SetProperties(object source, object target) 
{ 
    var type = target.GetType(); 
    foreach (var prop in source.GetType().GetProperties()) 
    { 
     var propGetter = prop.GetGetMethod(); 
     var propSetter = type.GetProperty(prop.Name).GetSetMethod(); 
     var valueToSet = propGetter.Invoke(source, null); 
     propSetter.Invoke(target, new[] { valueToSet }); 
    } 
} 

可是,爲什麼你需要在你的DAL和BLL不同,尚未準確最相同User對象?如果您需要更改User對象的屬性,會發生什麼情況?您必須在每個實例中,每個層中,整個應用程序(緊密耦合)中更改它,這首先破壞了創建DAL和BLL的目的。

這是一個使用泛型和接口非常有用的實例。因此,考慮對象User例如:

public class User 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

...您可以創建一個通用的「倉庫」類或你的對象一些其他數據訪問方法,從數據訪問接口(DAL)繼承:

public class DALMethods<T> : IDALMethods<T> where T : class 
    { 
     private UserContext _db; 
     private DbSet<T> _set; 

     public DALMethods(UserContext db) 
     { 
      _db = db; 
      _set = _db.Set<T>(); 
     } 

     public void Create(T entity) 
     { 
      _set.Add(entity); 
      _db.SaveChanges(); 
     } 

     //... Expressly dispose context method needed. 
    } 

..然後你BLL也只是與User業務邏輯有關:

public class UserBLL : IBLLMethods<User> 
{ 
    private DALMethods<User> _repository; 
    private UserContext _db; 

    public UserBLL() 
    { 
     _db = new UserContext(); 
     _repository = new DALMethods<User>(_db); 
    } 

    public bool CreateUserIfNameIsBob(User user) 
    { 
     // Create bob if bob 
     if (user.Name == "Bob") 
     { 
      _repository.Create(user); 
      return true; 
     } 

     // Not bob 
     return false; 
    } 
} 

上面的例子是有目的的通用,但我認爲它們說明了這一點。如果User對象發生更改,它不會阻止BLL和DAL層的工作。您可以使用像IDALMethods<T>這樣的接口強制執行約束,或者使用IoC容器進一步解耦您的代碼。

HTH