带有EF的泛型Repo中嵌套对象的自动映射-返回null



我再次尝试进入.NET,我在过去的10年里一直在开发,尽管我是从.NET 1.0开始的,但现在有点不同了。我已经习惯了使用ADO.NET的N层模型,或者我更喜欢的是ASP中的记录集。我正试图了解这个存储库模式、Genneric模式和实体框架。

请耐心等待我的知识,因为我刚刚回来一个月。我正在构建一个产品/订单服务,只是为了好玩,如果我愿意的话,我试图将其分解为微服务和MVC或WinForms。我只想学习做这件事的正确方法。

问题是,我没有得到嵌套对象,我确实理解这一点,也许我需要将它们正确地连接在一起,但话说回来,如果我没有免费得到,为什么我要使用EF?我知道你从框架等中得到了所有的建模,但你明白我的意思。

我有两种型号的

指出外键的订单模型——在我的世界里,它会自动映射

public class Orders
{
[Key]
public int OrderId { get; set; }
public int ProductId { get; set; }
[ForeignKey("ProductId")]
public IEnumerable<Product> Product { get; set; }
public DateTime Datetime { get; set; }
}

产品型号

public class Product
{
[Key]
public int ProductId { get; set; }
public string ProductName { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
public decimal Price { get; set; }
}

我有一个通用存储库接口

public interface IGenericRepository <T> : IDisposable where T : class
{
IEnumerable<T> GetAllRecords();
IEnumerable<T> FindRecord(Expression<Func<T,bool>> predicate);
T GetRecordById(int objId);
void AddRecord(T obj);
void DeleteRecord(T obj);
void UpdateRecord(T obj);
}

I通过实现此接口

public class GenericRepository<T> : IGenericRepository<T>, IDisposable where T : class
{
private readonly DBContext _context;
private readonly DbSet<T> _DbSet;
public GenericRepository(DBContext context)
{
this._context = context;
this._DbSet = this._context.Set<T>();
}
public IEnumerable<T> GetAllRecords()
{
return _DbSet.ToList();
}
public IEnumerable<T> FindRecord(Expression<Func<T, bool>> predicate)
{
throw new NotImplementedException();
}
public T GetRecordById(int objId)
{
return  _DbSet.Find(objId);
}
public void AddRecord(T obj)
{
_DbSet.Add(obj);
}
public void DeleteRecord(T obj)
{
_DbSet.Remove(obj);
}
public void UpdateRecord(T obj)
{
_DbSet.Attach(obj);
_context.Entry(obj).State = EntityState.Modified;
}
public void Save()
{
throw new NotImplementedException();
}
void IDisposable.Dispose()
{
throw new NotImplementedException();
}


}

最后我有一个UnitOfWork接口,我通过实现

public class UnitOfWork : IUnitOfWork
{
private DBContext _context;
//  public ProductRepository productRepository { get; private set; }
//public OrderRepository OrderReposity  { get; private set; }
public IGenericRepository<Product> productRepository { get; set; }
public IGenericRepository<Orders> OrderRepository { get; set; }
public UnitOfWork(DBContext context)
{
this._context = context;
this.productRepository = new GenericRepository<Product>(this._context);
this.OrderRepository = new GenericRepository<Orders>(this._context);
}
public void SaveChanges()
{
_context.SaveChanges();
}
}

在WebAPI控制器中,我通过调用unitOfWork

[路由("api/[控制器]"(][ApiController]

public class OrdersController : ControllerBase
{
public readonly UnitOfWork UoW;
public OrdersController(IUnitOfWork _uow)
{
this.UoW = _uow as UnitOfWork;
}

并指出API方法GET/Orders

// GET: api/<OrdersController>
[HttpGet]
public IEnumerable<Orders> Get()
{
return UoW.OrderRepository.GetAllRecords();
}

它就像一种魅力,我认为这是实现这一点的好方法。我可以很容易地创建另一个应用程序来使用后端,我可以很好地模拟和测试它。所有内容都在解决方案中的4个不同项目中。

但问题是在products对象上返回一个null。请随时向我提供您所能提供的所有反馈,如果我的解决方案不是首选的,或者如果我也在这样做,我应该如何构建它;"去耦合";。

感谢一个伟大的论坛和学习的巨大灵感

致以最良好的问候Martin

我假设您在这里使用.NET Core,所以如果您没有,请留下评论,我会根据您的需求重新编写解决方案。=(

首先,我认为你的数据类建模有误,因为um在产品x订单之间有一个1-n,它的构建方式意味着一个产品只能通过一个订单相关,这是不正确的,所以我们需要一个表来表示这两个实体之间的n-n关系,并对原始的2个实体进行一点更改,以支持它们之间的映射。

产品.cs

public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
public decimal Price { get; set; }
public List<OrderProduct> OrderProducts { get; set; }
}

订单.cs

public class Order
{
public int OrderId { get; set; }
public DateTime Datetime { get; set; }
public List<OrderProduct> OrderProducts { get; set; }
}

OrderProduct.cs(将事情联系起来的一个(

public class OrderProduct
{
public int OrderProductId { get; set; }
public int ProductId { get; set; }
public int OrderId { get; set; }
public Product Product { get; set; }
public Order Order { get; set; }
}

随着实体的设置,我们可以为零件运行。实体将自动为您加载相关实体=(

对于实体框架来说,除了约定命名(同样地命名(之外,它还知道什么和我们需要的东西有关,实体需要显式配置。

您使用DataAnnotations配置了这种关系,这并没有错,但就我个人而言,我更喜欢使用Fluent API,因为DataAnnotation在实体模型和实体框架之间创建了一个硬边界,这使得将来更难更改DDD模型的架构。

因此,让我们配置我们的实体。为了实现这一点,我们将为每个实体创建一个配置文件。

产品配置.cs

public class ProductConfig : IEntityTypeConfiguration<Product>
{
public void Configure(EntityTypeBuilder<Product> builder)
{
// Tells entity that ProductId is your primary key
builder.HasKey(b => b.ProductId);
}
}

OrderConfig.cs

public class OrderConfig : IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
// Tells entity that OrderId is your primary key
builder.HasKey(b => b.OrderId);
}
}

OrderProductConfig.cs

public class OrderProductConfig : IEntityTypeConfiguration<OrderProduct>
{
public void Configure(EntityTypeBuilder<OrderProduct> builder)
{
// Tells entity that OrderProductId is your primary key
builder.HasKey(b => b.OrderProductId);

// Configure the navigation property Order telling that an Order may be related to a bunch of OrderProducts
// setting up the OrderId as your foreign key constraint and telling Entity that is a required field (not null)
builder.HasOne(b => b.Order)
.WithMany(b => b.OrderProducts)
.HasForeignKey(b => b.OrderId)
.IsRequired();
// Configure the navigation property Product telling that a Product may be related to a bunch of OrderProducts
// setting up the ProductId as your foreign key constraint and telling Entity that is a required field (not null)
builder.HasOne(b => b.Product)
.WithMany(b => b.OrderProducts)
.HasForeignKey(b => b.ProductId)
.IsRequired();
}
}

现在我们需要一个地方来应用这些配置,所以我们需要为您的应用程序创建一个上下文,创建一个也是一个很好的做法=(

销售软件上下文.cs

// This class inherits DbContext class, and will be your new "DbContext"
public class SalesSoftwareContext : DbContext
{
// During runtime this method will be called so Entity will load the configurations and knows how to handle things
protected override void OnModelCreating(ModelBuilder builder)
{
builder.ApplyConfiguration(new OrderConfig());
builder.ApplyConfiguration(new ProductConfig());
builder.ApplyConfiguration(new OrderProductConfig());
}
}

GenericRepository.csUnitOfWork.cs中,用SalesSoftwareContext替换对DBContext的引用并运行您的代码!

现在,当您查询订单时,您将有一个OrderProduct列表,您将能够与之交互并获取产品信息,例如:

public class OrderViewModel 
{
public int OrderId {get;set;}
List<OrderItemViewModel> OrderItems {get;set;}
}
public class OrderItemViewModel
{
public int ProductId {get;set;}
public string Name {get;set;}
public decimal Price {get;set;}
}
[HttpGet]
public IEnumerable<OrderViewModel> Get()
{
List<OrderViewModel> returnValue = new List<OrderViewModel>();
List<Order> orders = UoW.OrderRepository.GetAllRecords();
foreach(var order in orders)
{
OrderViewModel orderToReturn = new OrderViewModel();
orderToReturn.OrderId = order.OrderId;
orderToReturn.OrderItems = order.OrderProducts
.Select(x => new OrderItemViewModel
{
ProductId = x.Product.ProductId,
Name = x.Product.ProductName,
Price = x.Product.Price
}).ToList());
returnValue.Add(orderToReturn);
}

return returnValue;
}

我希望这能有所帮助

由于我没有你的"完整代码",我最终在黑暗中打字,很抱歉发布的解决方案中出现了任何拼写错误或错误

很高兴有另一位开发人员回到.NET世界,.NET在过去几年里发展了很多,特别是当.NET Core出现时,但我相信所有的变化都是为了更好。

如果您有具体问题或需要任何指导,请在电子邮件中留言,以便我们与您取得联系。

节日快乐!

最新更新