使用数据库实体与业务逻辑实体时,如何避免重复定义属性?
解决方案
针对实体类与业务类属性重复的问题,提供三种可行方案,你可以根据项目架构需求选择:
方案1:业务类继承实体类
让RoomBL直接继承Room实体,复用所有数据属性,仅添加业务专属的属性与方法:
public class RoomBL : Room { public IRoomUserRepository UserRepository { get; } public RoomExtraData ExtraData { get; } public RoomBL(Room room, IRoomUserRepository userRepository, RoomExtraData extraData) { // 复制基类实体属性 Id = room.Id; Name = room.Name; Layout = room.Layout; OwnerId = room.OwnerId; MaxUsersAllowed = room.MaxUsersAllowed; Description = room.Description; IsMuted = room.IsMuted; Settings = room.Settings; PaintSettings = room.PaintSettings; PlayerRights = room.PlayerRights; Tags = room.Tags; Likes = room.Likes; FurnitureItems = room.FurnitureItems; // 初始化业务专属内存状态 ChatMessages = []; UserRepository = userRepository; ExtraData = extraData; } public async Task RunPeriodicCheckAsync() { var users = UserRepository.GetAll(); foreach (var roomUser in users) { await roomUser.RunPeriodicCheckAsync(); } var statusWriter = new RoomUserStatusWriter(users).GetAllBytes(); var dataWriter = new RoomUserDataWriter(users).GetAllBytes(); await UserRepository.BroadcastDataAsync(statusWriter); await UserRepository.BroadcastDataAsync(dataWriter); } public async ValueTask DisposeAsync() { await UserRepository.DisposeAsync(); } }
优点:完全消除属性重复,代码简洁;业务类可直接使用实体的所有属性。
缺点:实体与业务类耦合度高,实体结构变更时业务类需同步调整;实体上的数据库特性会被业务类继承,不符合单一职责。
方案2:实体类添加非持久化属性
在Room实体中用[NotMapped]标记不需要存入数据库的属性,同时将业务方法整合到实体类中:
public class Room { public int Id { get; set; } public string Name { get; set; } public RoomLayout Layout { get; } public int OwnerId { get; set; } public int MaxUsersAllowed { get; set; } public string Description { get; set; } public bool IsMuted { get; set; } public RoomSettings Settings { get; set; } public RoomPaintSettings PaintSettings { get; set; } public List<RoomPlayerRight> PlayerRights { get; set; } public List<RoomChatMessage> ChatMessages { get; set; } = []; public List<RoomTag> Tags { get; set; } public List<PlayerRoomLike> Likes { get; set; } public List<RoomFurnitureItem> FurnitureItems { get; set; } // 非持久化属性,标记NotMapped避免数据库映射 [NotMapped] public IRoomUserRepository UserRepository { get; set; } [NotMapped] public RoomExtraData ExtraData { get; set; } public async Task RunPeriodicCheckAsync() { var users = UserRepository.GetAll(); foreach (var roomUser in users) { await roomUser.RunPeriodicCheckAsync(); } var statusWriter = new RoomUserStatusWriter(users).GetAllBytes(); var dataWriter = new RoomUserDataWriter(users).GetAllBytes(); await UserRepository.BroadcastDataAsync(statusWriter); await UserRepository.BroadcastDataAsync(dataWriter); } public async ValueTask DisposeAsync() { await UserRepository.DisposeAsync(); } }
优点:无需额外业务类,所有逻辑集中在一个类,调用便捷。
缺点:违反单一职责原则,实体类同时承担数据持久化与业务逻辑职责,后期代码会臃肿难维护;依赖注入放到实体类中,会增加实体创建的复杂度。
方案3:组合模式(推荐)
让RoomBL持有Room实体的实例,通过包装属性复用数据,彻底分离业务逻辑与数据模型:
public class RoomBL { private readonly Room _room; public IRoomUserRepository UserRepository { get; } public RoomExtraData ExtraData { get; } public List<RoomChatMessage> ChatMessages { get; } = []; public RoomBL(Room room, IRoomUserRepository userRepository, RoomExtraData extraData) { _room = room; UserRepository = userRepository; ExtraData = extraData; } // 包装实体属性,对外暴露 public int Id => _room.Id; public string Name { get => _room.Name; set => _room.Name = value; } public RoomLayout Layout => _room.Layout; public int OwnerId => _room.OwnerId; public int MaxUsersAllowed { get => _room.MaxUsersAllowed; set => _room.MaxUsersAllowed = value; } public string Description { get => _room.Description; set => _room.Description = value; } public bool IsMuted => _room.IsMuted; public RoomSettings Settings => _room.Settings; public RoomPaintSettings PaintSettings => _room.PaintSettings; public List<RoomPlayerRight> PlayerRights => _room.PlayerRights; public List<RoomTag> Tags => _room.Tags; public List<PlayerRoomLike> Likes => _room.Likes; public List<RoomFurnitureItem> FurnitureItems => _room.FurnitureItems; public async Task RunPeriodicCheckAsync() { var users = UserRepository.GetAll(); foreach (var roomUser in users) { await roomUser.RunPeriodicCheckAsync(); } var statusWriter = new RoomUserStatusWriter(users).GetAllBytes(); var dataWriter = new RoomUserDataWriter(users).GetAllBytes(); await UserRepository.BroadcastDataAsync(statusWriter); await UserRepository.BroadcastDataAsync(dataWriter); } public async ValueTask DisposeAsync() { await UserRepository.DisposeAsync(); } }
优点:严格遵循单一职责,Room仅负责数据持久化,RoomBL仅负责业务逻辑;两者耦合度低,修改其中一个不会影响另一个;依赖注入逻辑更清晰。
缺点:需要手动包装实体属性,初期代码量稍大,可通过代码生成工具(如T4模板、Roslyn源码生成)自动生成包装代码,减少重复劳动。
内容的提问来源于stack exchange,提问作者user5405648
相关产品推荐
相关产品推荐

