Advanced further with Repositories and Dynamic-Queries

This commit is contained in:
Cesar De la Torre 2016-09-12 20:28:55 -07:00
parent ec6256ef6e
commit 1ba7087965
26 changed files with 461 additions and 294 deletions

View File

@ -1,7 +1,8 @@
{
"projects": [
"src",
"test"
"test",
"src/Services/Ordering"
],
"sdk": {

View File

@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
{
[Route("api/[controller]")]
public class OrderingController : Controller
{
private OrderingDbContext _context;
private IOrderRepository _orderRepository;
private OrderingQueries _queries;
public OrderingController(IOrderRepository orderRepository,
OrderingQueries orderingQueries,
OrderingDbContext context)
{
//Injected objects from the IoC container
_orderRepository = orderRepository;
_queries = orderingQueries;
_context = context;
}
// GET api/ordering/orders
[HttpGet("orders")]
public async Task<IActionResult> GetAllOrders()
{
dynamic response = await _queries.GetAllOrdersIncludingValueObjectsAndChildEntities();
return Ok(response);
}
// GET api/ordering/orders/xxxGUIDxxxx
[HttpGet("orders/{orderId:Guid}")]
public async Task<IActionResult> GetOrderByGuid(Guid orderId)
{
//var order = await _orderRepository.Get(orderId);
var order = await _context.Orders
.Include(o => o.ShippingAddress)
.Include(o => o.BillingAddress)
.Where(o => o.Id == orderId)
.SingleOrDefaultAsync<Order>();
// Dynamically generated a Response-Model that includes only the fields you need in the response.
// This keeps the JSON response minimal.
// Could also use var
dynamic response = new
{
id = order.Id,
orderDate = order.OrderDate,
shippingAddress = order.ShippingAddress,
billingAddress = order.BillingAddress,
//items = order.Items.Select(i => i.Content)
};
return Ok(response);
}
//Alternate method if using several parameters instead of part of the URL
// GET api/ordering/orders/?orderId=xxxGUIDxxx&otherParam=value
//[HttpGet("orders")]
//public Order GetOrderByGuid([FromUri] Guid orderId, [FromUri] string otherParam)
// POST api/ordering/orders/create
[HttpPut("orders/create")]
public async Task<int> Post([FromBody]Order order)
{
return await _orderRepository.Add(order);
//_context.Orders.Add(order);
//return await _context.SaveChangesAsync();
}
// PUT api/ordering/orders/xxxOrderGUIDxxxx/update
[HttpPut("orders/{orderId:Guid}/update")]
public async Task<int> UpdateOrder(Guid orderID, [FromBody] Order orderToUpdate)
{
return await _orderRepository.Add(orderToUpdate);
//_context.Orders.Update(orderToUpdate);
//return await _context.SaveChangesAsync();
}
// DELETE api/ordering/orders/xxxOrderGUIDxxxx
[HttpDelete("orders/{orderId:Guid}/delete")]
public async Task<int> Delete(Guid id)
{
return await _orderRepository.Remove(id);
//Order orderToDelete = _context.Orders
// .Where(o => o.Id == id)
// .SingleOrDefault();
//_context.Orders.Remove(orderToDelete);
//return await _context.SaveChangesAsync();
}
}
}

View File

@ -1,81 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.Controllers
{
[Route("api/[controller]")]
public class OrdersController : Controller
{
private OrderingContext _context;
public OrdersController(OrderingContext context)
{
//Injected DbContext from the IoC container
_context = context;
}
// GET api/orders
[HttpGet]
public IEnumerable<Order> Get()
{
//Create generic Address ValueObject
Address sampleAddress = new Address("15703 NE 61st Ct.",
"Redmond",
"Washington",
"WA",
"United States",
"US",
"98052",
47.661492,
-122.131309
);
//Create sample Orders
Order order1 = new Order(Guid.NewGuid(), sampleAddress, sampleAddress);
_context.Orders.Add(order1);
_context.SaveChanges();
return _context.Orders.ToList();
}
// GET api/orders/xxxGUIDxxxx
[HttpGet("{id}")]
public string Get(Guid id)
{
return "value TBD";
}
// POST api/orders
[HttpPost]
public void Post([FromBody]Order order)
{
_context.Orders.Add(order);
_context.SaveChanges();
}
// PUT api/orders/xxxGUIDxxxx
[HttpPut("{id}")]
public void Put(int id, [FromBody]Order value)
{
}
// DELETE api/orders/xxxGUIDxxxx
[HttpDelete("{id}")]
public void Delete(Guid id)
{
}
}
}

View File

@ -1,11 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.DTO
{
public class OrderDTO
{
}
}

View File

@ -1,26 +0,0 @@
using System;
using System.Runtime.Serialization;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.DTO
{
[DataContract]
public sealed class OrderItemDTO
{
public OrderItemDTO() { }
[DataMember]
public Guid Id { get; set; }
[DataMember]
public int Quantity { get; set; }
[DataMember]
public int FulfillmentRemaining { get; set; }
public override string ToString()
{
return String.Format("ID: {0}, Quantity: {1}, Fulfillment Remaing: {2}", this.Id, this.Quantity, this.FulfillmentRemaining);
}
}
}

View File

@ -3,11 +3,11 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
namespace Ordering.API.Migrations
{
[DbContext(typeof(OrderingContext))]
[DbContext(typeof(OrderingDbContext))]
[Migration("20160909202620_MyFirstMigration")]
partial class MyFirstMigration
{

View File

@ -3,11 +3,11 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
namespace Ordering.API.Migrations
{
[DbContext(typeof(OrderingContext))]
[DbContext(typeof(OrderingDbContext))]
[Migration("20160909223213_Migration2")]
partial class Migration2
{

View File

@ -3,11 +3,11 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
namespace Ordering.API.Migrations
{
[DbContext(typeof(OrderingContext))]
[DbContext(typeof(OrderingDbContext))]
[Migration("20160909233852_Migration3")]
partial class Migration3
{

View File

@ -3,11 +3,11 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
namespace Ordering.API.Migrations
{
[DbContext(typeof(OrderingContext))]
[DbContext(typeof(OrderingDbContext))]
partial class OrderingContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)

View File

@ -11,7 +11,7 @@
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/orders",
"launchUrl": "api/ordering/orders",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}

View File

@ -8,8 +8,11 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.Repositories;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries;
namespace Microsoft.eShopOnContainers.Services.Ordering.API
{
@ -36,7 +39,11 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.API
//Add EF Core Context (UnitOfWork)
var connection = @"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;";
services.AddDbContext<OrderingContext>(options => options.UseSqlServer(connection));
services.AddDbContext<OrderingDbContext>(options => options.UseSqlServer(connection));
services.AddTransient<IOrderRepository, OrderRepository>();
services.AddTransient<OrderingQueries, OrderingQueries>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

View File

@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
namespace Microsoft.eShopOnContainers.Services.Ordering.API.UnitOfWork
{
public class OrderingContext : DbContext
{
public OrderingContext(DbContextOptions<OrderingContext> options)
: base(options)
{ }
public DbSet<Order> Orders { get; set; }
}
}

View File

@ -18,7 +18,8 @@
"Microsoft.EntityFrameworkCore": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.0",
"Ordering.Domain": "1.0.0-*",
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final"
"Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
"Ordering.SqlData": "1.0.0-*"
},
"tools": {

View File

@ -3,9 +3,13 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts
{
interface IBuyerRepository
public interface IBuyerRepository : IRepository<Order>
{
//TBD - To define Specific Actions Not In Base Repo
}
}

View File

@ -10,8 +10,9 @@ namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContrac
{
public interface IOrderRepository : IRepository<Order>
{
Order Get(int associatedConferenceId);
//TBD - To define Specific Actions Not In Base Repo
void ModifyOrder(Order seatOrder);
Task<int> Remove(Guid id);
}
}

View File

@ -6,88 +6,24 @@ using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
{
/// <summary>
/// Base interface for implement a "Repository Pattern", for
/// Base interface to implement a "Repository Pattern", for
/// more information about this pattern see http://martinfowler.com/eaaCatalog/repository.html
/// or http://blogs.msdn.com/adonet/archive/2009/06/16/using-repository-and-unit-of-work-patterns-with-entity-framework-4-0.aspx
/// </summary>
/// <remarks>
/// Indeed, DbSet is already a generic repository and therefore you would not need a Repo.
/// But using this Repo interface allows us to ensure PI (Persistence Ignorant) principle
/// within our domain model
/// Indeed, DbSet is already a generic repository and for Data-Driven apps
/// you might not need custom Repositories.
/// But using this interface allows us to ensure PI (Persistence Ignorance) principle
/// from the Domain and Application code
/// </remarks>
/// <typeparam name="TEntity">Type of entity for this repository </typeparam>
public interface IRepository<TEntity> : IDisposable
where TEntity : Entity
where TEntity : AggregateRoot //1:1 relationship between Repository and AggregateRoot
{
/// <summary>
/// Get the unit of work in this repository
/// </summary>
IUnitOfWork UnitOfWork { get; }
/// <summary>
/// Add item into repository
/// </summary>
/// <param name="item">Item to add to repository</param>
void Add(TEntity item);
/// <summary>
/// Delete item
/// </summary>
/// <param name="item">Item to delete</param>
void Remove(TEntity item);
/// <summary>
/// Set item as modified
/// </summary>
/// <param name="item">Item to modify</param>
void Modify(TEntity item);
/// <summary>
///Track entity into this repository, really in UnitOfWork.
///In EF this can be done with Attach and with Update in NH
/// </summary>
/// <param name="item">Item to attach</param>
void TrackItem(TEntity item);
/// <summary>
/// Sets modified entity into the repository.
/// When calling Commit() method in UnitOfWork
/// these changes will be saved into the storage
/// </summary>
/// <param name="persisted">The persisted item</param>
/// <param name="current">The current item</param>
void Merge(TEntity persisted, TEntity current);
/// <summary>
/// Get element by entity key
/// </summary>
/// <param name="id">Entity key value</param>
/// <returns></returns>
TEntity Get(Guid id);
/// <summary>
/// Get all elements of type TEntity in repository
/// </summary>
/// <returns>List of selected elements</returns>
IEnumerable<TEntity> GetAll();
/// <summary>
/// Get all elements of type TEntity in repository
/// </summary>
/// <param name="pageIndex">Page index</param>
/// <param name="pageCount">Number of elements in each page</param>
/// <param name="orderByExpression">Order by expression for this query</param>
/// <param name="ascending">Specify if order is ascending</param>
/// <returns>List of selected elements</returns>
IEnumerable<TEntity> GetPaged<KProperty>(int pageIndex, int pageCount, Expression<Func<TEntity, KProperty>> orderByExpression, bool ascending);
/// <summary>
/// Get elements of type TEntity in repository
/// </summary>
/// <param name="filter">Filter that each element do match</param>
/// <returns>List of selected elements</returns>
IEnumerable<TEntity> GetFiltered(Expression<Func<TEntity, bool>> filter);
Task<int> Add(TEntity item);
Task<int> Remove(TEntity item);
Task<int> Update(TEntity item);
Task<TEntity> Get(Guid id);
}
}

View File

@ -1,46 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
{
/// <summary>
/// Contract for UnitOfWork pattern. For more
/// related info see http://martinfowler.com/eaaCatalog/unitOfWork.html or
/// http://msdn.microsoft.com/en-us/magazine/dd882510.aspx
/// In this Microservice, the Unit Of Work is implemented using the out-of-box
/// Entity Framework Context (EF Core DbContext) persistence engine. But in order to
/// comply the PI (Persistence Ignorant) principle in our Domain, we implement this interface/contract.
/// This interface/contract should be complied by any UoW implementation to be used with this Domain.
/// </summary>
public interface IUnitOfWork
: IDisposable
{
/// <summary>
/// Commit all changes made in a container.
/// </summary>
///<remarks>
/// If the entity have fixed properties and any optimistic concurrency problem exists,
/// then an exception is thrown
///</remarks>
void Commit();
/// <summary>
/// Commit all changes made in a container.
/// </summary>
///<remarks>
/// If the entity have fixed properties and any optimistic concurrency problem exists,
/// then 'client changes' are refreshed - Client wins
///</remarks>
void CommitAndRefreshChanges();
/// <summary>
/// Rollback tracked changes. See references of UnitOfWork pattern
/// </summary>
void RollbackChanges();
}
}

View File

@ -0,0 +1,45 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.Queries
{
public class OrderingQueries
{
private OrderingDbContext _dbContext;
public OrderingQueries(OrderingDbContext orderingDbContext)
{
_dbContext = orderingDbContext;
}
public async Task<dynamic> GetAllOrdersIncludingValueObjectsAndChildEntities()
{
var orders = await _dbContext.Orders
.Include(o => o.ShippingAddress)
.Include(o => o.BillingAddress)
//.Include(o => o.Items)
.ToListAsync<Order>();
// Dynamically generated a Response-Model that includes only the fields you need in the response.
// This keeps the JSON response minimal.
// Could also use var
dynamic response = orders.Select(o => new
{
id = o.Id,
orderDate = o.OrderDate,
shippingAddress = o.ShippingAddress,
billingAddress = o.BillingAddress,
//items = o.Items.Select(i => i.Content)
});
return response;
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.SeedWork;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.Repositories
{
//1:1 relationship between Repository and Aggregate (i.e. OrderRepository and Order)
public class OrderRepository
: Repository<Order>, IOrderRepository
{
public OrderRepository(OrderingDbContext unitOfWork)
: base(unitOfWork) { }
//TBD - To define Specific Actions Not In Base Repository class
public async Task<int> Remove(Guid orderId)
{
if (orderId == null)
return 0;
Order orderToDelete = await this.Get(orderId);
//attach item if not exist
_unitOfWork.Attach(orderToDelete);
//set as "removed"
_unitOfWork.Remove(orderToDelete);
return await _unitOfWork.SaveChangesAsync();
}
}
}

View File

@ -1,11 +1,82 @@
using System;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopOnContainers.Services.Ordering.Infrastructure.SeedWork
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.SeedWork
{
public class Repository
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : AggregateRoot //1:1 relationship between Repository and AggregateRoot
{
protected readonly DbContext _unitOfWork;
//DbContext injected thru DI from ASP.NET Core bootstrap
public Repository(DbContext unitOfWork)
{
if (unitOfWork == null)
throw new ArgumentNullException("unitOfWork");
_unitOfWork = unitOfWork;
}
public DbContext UnitOfWork
{
get
{
return _unitOfWork;
}
}
public virtual async Task<int> Add(TEntity item)
{
if (item == null)
return 0;
_unitOfWork.Set<TEntity>().Add(item); // add new item in this set
return await _unitOfWork.SaveChangesAsync();
}
public virtual async Task<int> Remove(TEntity item)
{
if (item == null)
return 0;
//attach item if not exist
_unitOfWork.Set<TEntity>().Attach(item);
//set as "removed"
_unitOfWork.Set<TEntity>().Remove(item);
return await _unitOfWork.SaveChangesAsync();
}
public virtual async Task<int> Update(TEntity item)
{
if (item == null)
return 0;
_unitOfWork.Set<TEntity>().Update(item);
return await _unitOfWork.SaveChangesAsync();
}
public virtual async Task<TEntity> Get(Guid id)
{
if (id != Guid.Empty)
return await _unitOfWork.Set<TEntity>().FirstOrDefaultAsync(o => o.Id == id);
else
return null;
}
public void Dispose()
{
if (_unitOfWork != null)
_unitOfWork.Dispose();
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
namespace Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork
{
public class OrderingDbContext : DbContext
{
public OrderingDbContext(DbContextOptions<OrderingDbContext> options)
: base(options)
{ }
public DbSet<Order> Orders { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//If running from ASP.NET Core, config is done at StartUp.cs --> ConfigureServices() outside
//and injected through DI later on. The following config is used when running Tests or similar contexts
if (!optionsBuilder.IsConfigured)
{
//SQL LOCALDB
optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;");
}
}
}
}

View File

@ -1,8 +1,11 @@
{
{
"version": "1.0.0-*",
"dependencies": {
"NETStandard.Library": "1.6.0"
"Microsoft.EntityFrameworkCore": "1.0.0",
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
"NETStandard.Library": "1.6.0",
"Ordering.Domain": "1.0.0-*"
},
"frameworks": {

View File

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
using Microsoft.eShopOnContainers.Services.Ordering.SqlData.UnitOfWork;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace DataIntegrationTests
{
//Basic documentation for Testing EF Core classes
// http://ef.readthedocs.io/en/latest/miscellaneous/testing.html
public class Tests
{
private static DbContextOptions<OrderingDbContext> CreateNewContextOptionsForInMemoryDB()
{
// Create a fresh service provider, and therefore a fresh
// InMemory database instance.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Create a new options instance telling the context to use an
// InMemory database and the new service provider.
var builder = new DbContextOptionsBuilder<OrderingDbContext>();
builder.UseInMemoryDatabase()
.UseInternalServiceProvider(serviceProvider);
return builder.Options;
}
private static DbContextOptions<OrderingDbContext> CreateNewContextOptionsForSqlDB()
{
// Create a new options instance telling the context to use a Sql database
var builder = new DbContextOptionsBuilder<OrderingDbContext>();
//SQL LOCALDB
builder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Microsoft.eShopOnContainers.Services.OrderingDb;Trusted_Connection=True;");
return builder.Options;
}
[Fact]
public void Add_orders_to_database()
{
// All contexts that share the same service provider will share the same database
//Using InMemory DB
var options = CreateNewContextOptionsForInMemoryDB();
//Using Sql LocalDB
//var options = CreateNewContextOptionsForSqlDB();
// Run the test against one instance of the context
using (var context = new OrderingDbContext(options))
{
//Create generic Address ValueObject
Address sampleAddress = new Address("15703 NE 61st Ct.",
"Redmond",
"Washington",
"WA",
"United States",
"US",
"98052",
47.661492,
-122.131309
);
//Create sample Orders
Order order1 = new Order(Guid.NewGuid(), sampleAddress, sampleAddress);
context.Orders.Add(order1);
context.SaveChanges();
Assert.True(true);
}
//// Use a separate instance of the context to verify correct data was saved to database
using (var context = new OrderingDbContext(options))
{
var orders = context.Orders
.Include(o => o.ShippingAddress)
.Include(o => o.BillingAddress)
.ToList();
//Could be using .Load() if you don't want to create a List
//SAMPLE
//var company = context.Companies
// .Include(co => co.Employees).ThenInclude(emp => emp.Employee_Car)
// .Include(co => co.Employees).ThenInclude(emp => emp.Employee_Country)
// .FirstOrDefault(co => co.companyID == companyID);
//Assert when running test with a clean In-Memory DB
//Assert.Equal(1, context.Orders.Count());
Assert.Equal("Redmond", orders.First<Order>().ShippingAddress.City);
}
}
}
}

View File

@ -11,9 +11,11 @@
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -1,14 +0,0 @@
using System;
using Xunit;
namespace Tests
{
public class Tests
{
[Fact]
public void Test1()
{
Assert.True(true);
}
}
}

View File

@ -6,7 +6,11 @@
"dependencies": {
"System.Runtime.Serialization.Primitives": "4.1.1",
"xunit": "2.1.0",
"dotnet-test-xunit": "2.2.0-preview2-build1029"
"dotnet-test-xunit": "2.2.0-preview2-build1029",
"Ordering.Domain": "1.0.0-*",
"Ordering.SqlData": "1.0.0-*",
"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
"Microsoft.EntityFrameworkCore.InMemory": "1.0.0"
},
"testRunner": "xunit",
"frameworks": {