我有一个GenericService Add方法,如下所示: -
public bool Add(T entity, Expression<Func<T, bool>> filter = null)
{
try
{
_genericRepository.Add(entity, filter);
}
catch (Exception e)
{
return false;
}
return true;
}
和GenericRepository Add方法如下: -
public void Add(T entity, Expression<Func<T, bool>> filter = null)
{
var existing = Get<T>(filter);
if (existing.Result != null) return;
Context.Add(entity);
Save();
}
这是我在ProductsController中进行的调用: -
[HttpPost]
public IActionResult Create([FromBody] Product product)
{
if (product == null)
return BadRequest();
var result = _productsService.Add(product, m => m.Name == product.Name);
if (result)
{
return CreatedAtRoute("GetProducts", new { id = product.Id }, product);
}
return BadRequest("Item not added");
}
我是通过集成测试创建的,如下所示: -
testBrand = new Brand { Name = "testBrand" };
testImage = new Image { Name = "testImage", Url = "/Brands/adidas_logo_test.png" };
testBrand.Image = testImage;
testCategory = new Category {Name = "testCategory"};
testProduct = new Product
{
Category = testCategory,
Name = "testProduct",
Brand = testBrand,
BrandId = testBrand.Id,
CategoryId = testCategory.Id,
Deal = false,
Description = "testDescription",
Discount = "50% Discount",
Image = testImage,
ImageId = testImage.Id,
Price = new decimal(50.00),
Stock = 5
};
[Test]
public async Task Create_CreateAProduct_NewBrandNewCategoryNewImageProductsController()
{
//Arrange
//Act
//create new image
var requestImage = "api/Images/";
var postResponseImage = await _client.PostAsJsonAsync(requestImage, testImage);
var created = await postResponseImage.Content.ReadAsStringAsync();
var createdImage = JsonConvert.DeserializeObject<Image>(created);
//Act
testBrand.Image = createdImage;
testBrand.ImageId = createdImage.Id;
testImage.Id = createdImage.Id;
var postResponseProduct = await _client.PostAsJsonAsync(requestProduct, testProduct);
var createdProduct = await postResponseProduct.Content.ReadAsStringAsync();
var createdProductObj = JsonConvert.DeserializeObject<Product>(createdProduct);
var getResponse = await _client.GetAsync(requestProduct + "Get/" + createdProductObj.Id);
var fetched = await getResponse.Content.ReadAsStringAsync();
var fetchedProduct = JsonConvert.DeserializeObject<Product>(fetched);
// Assert
Assert.IsTrue(postResponseProduct.IsSuccessStatusCode);
Assert.IsTrue(getResponse.IsSuccessStatusCode);
Assert.AreEqual(testProduct.Name, createdProductObj.Name);
Assert.AreEqual(testProduct.Name, fetchedProduct.Name);
Assert.AreNotEqual(Guid.Empty, createdProductObj.Id);
Assert.AreEqual(createdProductObj.Id, fetchedProduct.Id);
}
一切正常,直到我尝试插入具有多个相关实体的实体。让我举个例子。
假设我有一个产品,它有一个FK ImageId,一个用于BrandId的FK,一个用于CategoryId的FK。 Brands实体已经为Image实体提供了FK ImageId。
现在,当我尝试插入新产品时,它会插入2张图片,其中一张是品牌附带的图片,另一张是产品本身的图片。所以在图像表中,当我只想要一个新的产品图像条目时,我得到2个条目。此外,当我想将现有图像用于新产品时,这会导致问题。
所以我正在考虑为从通用服务/存储库继承的产品创建一个新的服务/存储库,并为其添加更多逻辑。但是有更好的方法吗?
感谢您的帮助和时间
答案 0 :(得分:2)
现在我明白了。
当使用客户端进行测试时,mvc会使用json数据接收您的请求,并正确创建模型。
但是,mvc不知道你想要产品和品牌的Image
,它会为每一个创建一个实例,就像这样(我为了示例目的而简化):
var product = new Product();
var brand = new Brand();
product.Image = new Image();
product.Brand = brand;
brand.Image = new Image(); // new image with same info...
同样,实体框架将假设它们是具有相同数据的两个不同图像。只是让它知道它是一样的,通过在你的行动中做这样的事情(当然你会创建一个更好的代码,这只是一个快速的样本):
[HttpPost]
public IActionResult Create([FromBody] Product product)
{
if (product == null)
return BadRequest();
// If the image already exists...nullify image so EF won't try to insert a new one...
if (product.ImageId > 0)
product.Image = null;
// If the image already exists...and the brand doesn't have an existing image, use the same image and nullify the brand's image as well...
if (product.ImageId > 0 && product.Brand != null && !(product.Brand.ImageId > 0))
{
product.Brand.ImageId = product.ImageId;
product.Brand = null;
}
// If product is reveiving a new image...and the brand doesn't have an existing image, use the same new image...
if (product.Image != null && product.Brand != null && !(product.Brand.ImageId > 0))
product.Brand.Image = product.Image;
var result = _productsService.Add(product, m => m.Name == product.Name);
if (result)
{
return CreatedAtRoute("GetProducts", new { id = product.Id }, product);
}
return BadRequest("Item not added");
}
只是为了在控制台应用程序中进行测试,我将其复制如下。一些课程:
public class Brand
{
public int Id { get; set; }
public virtual Image Image { get; set; }
public int ImageId { get; set; }
}
public class Image
{
public int Id { get; set; }
}
public class Product
{
public int Id { get; set; }
public virtual Image Image { get; set; }
public virtual Brand Brand { get; set; }
public int ImageId { get; set; }
public int BrandId { get; set; }
}
DbContext
配置:
public class MyDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Brand> Brands { get; set; }
public DbSet<Image> Images { get; set; }
public MyDbContext()
: base("name=MyDbContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Properties<int>().Where(p => p.Name == "Id").Configure(p => p.IsKey());
modelBuilder.Entity<Product>().HasRequired(p => p.Brand).WithMany().HasForeignKey(p => p.BrandId);
modelBuilder.Entity<Product>().HasRequired(p => p.Image).WithMany().HasForeignKey(p => p.ImageId);
modelBuilder.Entity<Brand>().HasRequired(p => p.Image).WithMany().HasForeignKey(p => p.ImageId);
}
}
最后,代码本身。
第一种情况我使用相同的实例:
class Program
{
static void Main(string[] args)
{
using (var db = new MyDbContext())
{
var image = new Image();
var product = new Product();
var brand = new Brand();
product.Image = image;
product.Brand = brand;
brand.Image = image; // same instance
db.Products.Add(product);
db.SaveChanges();
}
}
}
我的结果是:
然后我又跑了,现在使用一个新实例:
class Program
{
static void Main(string[] args)
{
using (var db = new MyDbContext())
{
var image = new Image();
var product = new Product();
var brand = new Brand();
product.Image = image;
product.Brand = brand;
brand.Image = new Image();
db.Products.Add(product);
db.SaveChanges();
}
}
}
现在我们有两个新图片:
答案 1 :(得分:0)
您必须先保存图像 ,然后保存引用它的实体。否则,实体框架会将每个实例视为应单独保存的新实例。