Adding new items to a static array - static-array

I am very new to coding and have been asked to do the following:
Create two static arrays that will hold the gross and net income. A user will input the gross income every month and the net must be calculated and saved.
Now what iv have so far is this:
public partial class Form1 : Form
{
public static double[] gross { get; set; }
public static double[] net { get; set; }
public Form1()
{
InitializeComponent();
}
private void btnEnter_Click(object sender, EventArgs e)
{
Gross g = new Gross();
g.ShowDialog();
gross = new double[] { g.grossTemp };
net = new double[] { g.netin };
}
}
But every time i want to add a new gross and net income, it replaces the old one instead of adding the new one to a new index. I think the problem is because i'm creating a new instance but i don't know what else to do.

You can use List as a private type , and the public type can remain an array like this :
public static double[] gross get { return _gross.ToArray() }
private static List<double> _gross;
private void btnEnter_Click(object sender, EventArgs e)
{
Gross g = new Gross();
g.ShowDialog();
_gross.Add(g.grossTemp );
}

Related

Is running a query from a command a violation of Command-Query Separation?

Given a real-world anonymous shopping cart, the "AddToCart" workflow must do the following steps:
Lookup the current product from the database. Get the price from the product or use a service to calculate the price on user selections and other product properties. (query)
Lookup the current shopping cart from the database. (query)
If the current shopping cart doesn't exist in the database, create a new shopping cart entity (in memory).
Add the new item (product) to the shopping cart entity (in memory) along with its price.
Run any discount calculations on the entire shopping cart. (depends on query)
Run any sales tax calculations on the shopping cart. (depends on query)
Run any shipping calculations on the shopping cart. (depends on query)
If this is a new shopping cart, add the entity to the database, otherwise update the shopping cart in the database. (command)
So, although "AddToCart" sounds like it should be a command (since it updates the system state), in practice it depends on many queries.
My Question
What is the generally accepted way to handle workflows like this?
Make an AddToCartCommandHandler that depends on other services that may run queries.
Make a facade CartService that orchestrates the workflow that runs the queries first followed by the commands.
Make the controller action method first run the queries, then run any commands. Seems like some of the query steps could be missed if this needs to be reused.
Other?
Is the reason I can't find an answer about this because it "depends on the design" and this is one of the exceptions where not to apply it?
If the commands and queries are separated, would I pass my real entity framework entity class to the command that adds/updates the cart (so EF can work out whether it is attached or not)? It seems like a DTO won't do in this case.
NOTE: I am implicitly assuming that systems that implement CQS do so with the aim that eventually they could become a full-on CQRS system. If so, this workflow apparently would not be able to make the transition - hence my question.
Background
I am taking my first stab at CQS.
It is clear from the documentation I have read about this pattern that a query must not change the system state.
However, it is unclear whether it is considered okay to run a query from within a command (I can't seem to find any info anywhere).
There are several real-world cases I can think of where this needs to happen. But, given the lack of real-world examples of this pattern online I am uncertain how to proceed. There is lots of theory online, but the only code I can find is here and here.
The answer to this problem came in the form of a comment by qujck.
The solution is to break the application into different query types and command types. The exact purpose of each type remain a mystery (since the blog post doesn't go into the reasons why he made this distinction), but it does make it clear how top-level and mid-level commands can depend on database queries.
Command Types
Command (top-level)
Command Strategy (mid-level)
Data Command (direct data access)
Query Types
Query (top-level)
Query Strategy (mid-level)
Data Query (direct data access)
Command-Query Implementation
// Commands
public interface ICommand
{
}
public interface IDataCommand
{
}
/// <summary>
/// A holistic abstraction, an abstraction that acts as the whole of each transaction
/// </summary>
/// <typeparam name="TCommand"></typeparam>
public interface ICommandHandler<TCommand>
{
void Handle(TCommand command);
}
public interface ICommandStrategyHandler<TCommand> where TCommand : ICommand
{
void Handle(TCommand command);
}
/// <summary>
/// Direct database update
/// </summary>
/// <typeparam name="TCommand"></typeparam>
public interface IDataCommandHandler<TCommand> where TCommand : IDataCommand
{
void Handle(TCommand command);
}
// Queries
public interface IQuery<TResult>
{
}
public interface IDataQuery<TResult>
{
}
/// <summary>
/// A holistic abstraction, an abstraction that acts as the whole of each transaction
/// </summary>
/// <typeparam name="TQuery"></typeparam>
/// <typeparam name="TResult"></typeparam>
public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
TResult Handle(TQuery query);
}
public interface IQueryStrategyHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
TResult Handle(TQuery query);
}
/// <summary>
/// Direct database query
/// </summary>
/// <typeparam name="TQuery"></typeparam>
/// <typeparam name="TResult"></typeparam>
public interface IDataQueryHandler<TQuery, TResult> where TQuery : IDataQuery<TResult>
{
TResult Handle(TQuery query);
}
/// <summary>
/// Generic processor that can run any query
/// </summary>
public interface IQueryProcessor
{
TResult Execute<TResult>(IQuery<TResult> query);
// NOTE: Stephen recommends against using Async. He may be right that it is not
// worth the aggrevation of bugs that may be introduced.
//Task<TResult> Execute<TResult>(IQuery<TResult> query);
TResult Execute<TResult>(IDataQuery<TResult> query);
}
AddToCart Dependency Graph
Using the above implementation, the structure of the AddToCart workflow dependency graph looks like this.
AddToCartCommandHandler : ICommandHandler<AddToCartCommand>
GetShoppingCartDetailsQueryHandler : IQueryHandler<GetShoppingCartDetailsQuery, ShoppingCartDetails>
GetShoppingCartQueryStrategyHandler : IQueryStrategyHandler<GetShoppingCartQueryStrategy, ShoppingCartDetails>
GetShoppingCartDataQueryHandler : IDataQueryHandler<GetShoppingCartDataQuery, ShoppingCartDetails>
ApplicationDbContext
CreateShoppingCartDataCommandHandler : IDataCommandHandler<CreateShoppingCartDataCommand>
ApplicationDbContext
UpdateShoppingCartDataCommandHandler : IDataCommandHandler<UpdateShoppingCartDataCommand>
SetItemPriceCommandStrategyHandler : ICommandStrategyHandler<SetItemPriceCommandStrategy>
GetProductDetailsDataQueryHandler : IDataQueryHandler<GetProductDetailsDataQuery, ProductDetails>
ApplicationDbContext
SetTotalsCommandStrategyHandler : ICommandStrategyHandler<SetTotalsCommandStrategy>
SetDiscountsCommandStrategyHandler : ICommandStrategyHandler<SetDiscountsCommandStrategy>
?
SetSalesTaxCommandStrategyHandler : ICommandStrategyHandler<SetSalesTaxCommandStrategy>
Implementation
DTOs
public class ShoppingCartDetails : IOrder
{
private IEnumerable<IOrderItem> items = new List<ShoppingCartItem>();
public Guid Id { get; set; }
public decimal SubtotalDiscounts { get; set; }
public string ShippingPostalCode { get; set; }
public decimal Shipping { get; set; }
public decimal ShippingDiscounts { get; set; }
public decimal SalesTax { get; set; }
public decimal SalesTaxDiscounts { get; set; }
// Declared twice - once for the IOrder interface
// and once so we can get the realized concrete type.
// See: https://stackoverflow.com/questions/15490633/why-cant-i-use-a-compatible-concrete-type-when-implementing-an-interface
public IEnumerable<ShoppingCartItem> Items
{
get { return this.items as IEnumerable<ShoppingCartItem>; }
set { this.items = value; }
}
IEnumerable<IOrderItem> IOrder.Items
{
get { return this.items; }
set { this.items = value; }
}
//public IEnumerable<ShoppingCartNotification> Notifications { get; set; }
//public IEnumerable<ShoppingCartCoupon> Coupons { get; set; } // TODO: Add this to IOrder
}
public class ShoppingCartItem : IOrderItem
{
public ShoppingCartItem()
{
this.Id = Guid.NewGuid();
this.Selections = new Dictionary<string, object>();
}
public Guid Id { get; set; }
public Guid ShoppingCartId { get; set; }
public Guid ProductId { get; set; }
public int Quantity { get; set; }
public decimal Price { get; set; }
public decimal PriceDiscount { get; set; }
public IDictionary<string, object> Selections { get; set; }
}
public class ProductDetails
{
public Guid Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public decimal Discount { get; set; }
}
Calculating Order Totals
Rather than relying on a string of services to do simple (and required) arithmetic, I opted to put this behavior into extension methods so it is done on the fly against the actual data. Since this logic will need to be shared between the shopping cart, order, and quote, the calculation is done against IOrder and IOrderItem rather than concrete model types.
// Contract to share simple cacluation and other business logic between shopping cart, order, and quote
public interface IOrder
{
decimal SubtotalDiscounts { get; set; }
decimal Shipping { get; set; }
decimal ShippingDiscounts { get; set; }
decimal SalesTax { get; set; }
decimal SalesTaxDiscounts { get; set; }
IEnumerable<IOrderItem> Items { get; set; }
}
public interface IOrderItem
{
Guid ProductId { get; set; }
int Quantity { get; set; }
decimal Price { get; set; }
decimal PriceDiscount { get; set; }
IDictionary<string, object> Selections { get; set; }
}
public static class OrderExtensions
{
public static decimal GetSubtotal(this IOrder order)
{
return order.Items.Sum(x => x.GetTotal());
}
public static decimal GetSubtotalBeforeDiscounts(this IOrder order)
{
return order.Items.Sum(x => x.GetTotalBeforeDiscounts());
}
public static decimal GetTotal(this IOrder order)
{
var subtotal = (order.GetSubtotal() - order.SubtotalDiscounts);
var shipping = (order.Shipping - order.ShippingDiscounts);
var salesTax = (order.SalesTax - order.SalesTaxDiscounts);
return (subtotal + shipping + salesTax);
}
}
public static class OrderItemExtensions
{
public static decimal GetTotalBeforeDiscounts(this IOrderItem item)
{
return (item.Price * item.Quantity);
}
public static decimal GetTotal(this IOrderItem item)
{
return (GetTotalBeforeDiscounts(item) - item.PriceDiscount);
}
public static decimal GetDiscountedUnitPrice(this IOrderItem item)
{
return (item.Quantity > 0) ? (GetTotal(item) / item.Quantity) : 0;
}
}
ShoppingCartController
For brevity, we only show the AddToCart action, but this is where other actions against the shopping cart (i.e. remove from cart) would go as well.
public class ShoppingCartController : Controller
{
private readonly IQueryProcessor queryProcessor;
private readonly IAnonymousIdAccessor anonymousIdAccessor;
private readonly ICommandHandler<AddToCartCommand> addToCartHandler;
public ShoppingCartController(
IQueryProcessor queryProcessor,
IAnonymousIdAccessor anonymousIdAccessor,
ICommandHandler<AddToCartCommand> addToCartHandler)
{
if (queryProcessor == null)
throw new ArgumentNullException("queryProcessor");
if (anonymousIdAccessor == null)
throw new ArgumentNullException("anonymousIdAccessor");
if (addToCartHandler == null)
throw new ArgumentNullException("addToCartHandler");
this.queryProcessor = queryProcessor;
this.anonymousIdAccessor = anonymousIdAccessor;
this.addToCartHandler = addToCartHandler;
}
public ActionResult Index()
{
var command = new GetShoppingCartDetailsQuery
{
ShoppingCartId = this.anonymousIdAccessor.AnonymousID
};
ShoppingCartDetails cart = this.queryProcessor.Execute(command);
return View(cart);
}
public ActionResult AddToCart(ItemViewModel model)
{
var command = new AddToCartCommand
{
ProductId = model.Id,
Quantity = model.Qty,
Selections = model.Selections,
ShoppingCartId = this.anonymousIdAccessor.AnonymousID
};
this.addToCartHandler.Handle(command);
// If we execute server side, it should go to the cart page
return RedirectToAction("Index");
}
}
AddToCartCommandHandler
Here is where the main part of the workflow is executed. This command will be called directly from the AddToCart controller action.
public class AddToCartCommandHandler : ICommandHandler<AddToCartCommand>
{
private readonly IQueryStrategyHandler<GetShoppingCartQueryStrategy, ShoppingCartDetails> getShoppingCartQuery;
private readonly IDataCommandHandler<UpdateShoppingCartDataCommand> updateShoppingCartCommand;
private readonly ICommandStrategyHandler<SetItemPriceCommandStrategy> setItemPriceCommand;
private readonly ICommandStrategyHandler<SetTotalsCommandStrategy> setTotalsCommand;
public AddToCartCommandHandler(
IQueryStrategyHandler<GetShoppingCartQueryStrategy, ShoppingCartDetails> getShoppingCartCommand,
IDataCommandHandler<UpdateShoppingCartDataCommand> updateShoppingCartCommand,
ICommandStrategyHandler<SetItemPriceCommandStrategy> setItemPriceCommand,
ICommandStrategyHandler<SetTotalsCommandStrategy> setTotalsCommand
)
{
if (getShoppingCartCommand == null)
throw new ArgumentNullException("getShoppingCartCommand");
if (setItemPriceCommand == null)
throw new ArgumentNullException("setItemPriceCommand");
if (updateShoppingCartCommand == null)
throw new ArgumentNullException("updateShoppingCartCommand");
if (setTotalsCommand == null)
throw new ArgumentNullException("setTotalsCommand");
this.getShoppingCartQuery = getShoppingCartCommand;
this.updateShoppingCartCommand = updateShoppingCartCommand;
this.setItemPriceCommand = setItemPriceCommand;
this.setTotalsCommand = setTotalsCommand;
}
public void Handle(AddToCartCommand command)
{
// Get the shopping cart (aggregate root) from the database
var shoppingCart = getShoppingCartQuery.Handle(new GetShoppingCartQueryStrategy { ShoppingCartId = command.ShoppingCartId });
// Create a new shopping cart item
var item = new Contract.DTOs.ShoppingCartItem
{
ShoppingCartId = command.ShoppingCartId,
ProductId = command.ProductId,
Quantity = command.Quantity,
// Dictionary representing the option selections the user made on the UI
Selections = command.Selections
};
// Set the item's price (calculated/retrieved from database query)
setItemPriceCommand.Handle(new SetItemPriceCommandStrategy { ShoppingCartItem = item });
// Add the item to the cart
var items = new List<Contract.DTOs.ShoppingCartItem>(shoppingCart.Items);
items.Add(item);
shoppingCart.Items = items;
// Set the shopping cart totals (sales tax, discounts)
setTotalsCommand.Handle(new SetTotalsCommandStrategy { ShoppingCart = shoppingCart });
// Update the shopping cart details in the database
updateShoppingCartCommand.Handle(new UpdateShoppingCartDataCommand { ShoppingCart = shoppingCart });
}
}
GetShoppingCartQueryStrategyHandler
public class GetShoppingCartQueryStrategyHandler : IQueryStrategyHandler<GetShoppingCartQueryStrategy, ShoppingCartDetails>
{
private readonly IDataQueryHandler<GetShoppingCartDataQuery, ShoppingCartDetails> getShoppingCartDataQuery;
private readonly IDataCommandHandler<CreateShoppingCartDataCommand> createShoppingCartDataCommand;
public GetShoppingCartQueryStrategyHandler(
IDataQueryHandler<GetShoppingCartDataQuery, ShoppingCartDetails> getShoppingCartDataQuery,
IDataCommandHandler<CreateShoppingCartDataCommand> createShoppingCartDataCommand)
{
if (getShoppingCartDataQuery == null)
throw new ArgumentNullException("getShoppingCartDataQuery");
if (createShoppingCartDataCommand == null)
throw new ArgumentNullException("createShoppingCartDataCommand");
this.getShoppingCartDataQuery = getShoppingCartDataQuery;
this.createShoppingCartDataCommand = createShoppingCartDataCommand;
}
public ShoppingCartDetails Handle(GetShoppingCartQueryStrategy query)
{
var result = this.getShoppingCartDataQuery.Handle(new GetShoppingCartDataQuery { ShoppingCartId = query.ShoppingCartId });
// If there is no shopping cart, create one.
if (result == null)
{
this.createShoppingCartDataCommand.Handle(new CreateShoppingCartDataCommand { ShoppingCartId = query.ShoppingCartId });
result = new ShoppingCartDetails
{
Id = query.ShoppingCartId
};
}
return result;
}
}
GetShoppingCartDataQueryHandler
/// <summary>
/// Data handler to get the shopping cart data (if it exists)
/// </summary>
public class GetShoppingCartDataQueryHandler : IDataQueryHandler<GetShoppingCartDataQuery, ShoppingCartDetails>
{
private readonly IAppContext context;
public GetShoppingCartDataQueryHandler(IAppContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
}
public ShoppingCartDetails Handle(GetShoppingCartDataQuery query)
{
return (from shoppingCart in context.ShoppingCarts
where shoppingCart.Id == query.ShoppingCartId
select new ShoppingCartDetails
{
Id = shoppingCart.Id,
SubtotalDiscounts = shoppingCart.SubtotalDiscounts,
ShippingPostalCode = shoppingCart.ShippingPostalCode,
Shipping = shoppingCart.Shipping,
ShippingDiscounts = shoppingCart.ShippingDiscounts,
SalesTax = shoppingCart.SalesTax,
SalesTaxDiscounts = shoppingCart.SalesTaxDiscounts,
Items = shoppingCart.Items.Select(i =>
new Contract.DTOs.ShoppingCartItem
{
Id = i.Id,
ShoppingCartId = i.ShoppingCartId,
ProductId = i.ProductId,
Quantity = i.Quantity,
Price = i.Price,
PriceDiscount = i.PriceDiscount
// TODO: Selections...
})
}).FirstOrDefault();
}
}
CreateShoppingCartDataCommandHandler
public class CreateShoppingCartDataCommandHandler : IDataCommandHandler<CreateShoppingCartDataCommand>
{
private readonly IAppContext context;
public CreateShoppingCartDataCommandHandler(IAppContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
}
public void Handle(CreateShoppingCartDataCommand command)
{
var cart = new ShoppingCart
{
Id = command.ShoppingCartId
};
this.context.ShoppingCarts.Add(cart);
this.context.SaveChanges();
}
}
UpdateShoppingCartDataCommandHandler
This updates the shopping cart with all of the changes that the business layer applied.
For the time being, this "command" does a query so it can reconcile the differences between the database and in memory copy. However, it is obviously a violation of the CQS pattern. I plan to make a follow-up question to determine what the best course of action is for change tracking since change tracking and CQS appear to be intimately linked.
public class UpdateShoppingCartDataCommandHandler : IDataCommandHandler<UpdateShoppingCartDataCommand>
{
private readonly IAppContext context;
public UpdateShoppingCartDataCommandHandler(IAppContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
}
public void Handle(UpdateShoppingCartDataCommand command)
{
var cart = context.ShoppingCarts
.Include(x => x.Items)
.Single(x => x.Id == command.ShoppingCart.Id);
cart.Id = command.ShoppingCart.Id;
cart.SubtotalDiscounts = command.ShoppingCart.SubtotalDiscounts;
cart.ShippingPostalCode = command.ShoppingCart.ShippingPostalCode;
cart.Shipping = command.ShoppingCart.Shipping;
cart.ShippingDiscounts = command.ShoppingCart.ShippingDiscounts;
cart.SalesTax = command.ShoppingCart.SalesTax;
cart.SalesTaxDiscounts = command.ShoppingCart.SalesTaxDiscounts;
ReconcileShoppingCartItems(cart.Items, command.ShoppingCart.Items, command.ShoppingCart.Id);
// Update the cart with new data
context.SaveChanges();
}
private void ReconcileShoppingCartItems(ICollection<ShoppingCartItem> items, IEnumerable<Contract.DTOs.ShoppingCartItem> itemDtos, Guid shoppingCartId)
{
// remove deleted items
var items2 = new List<ShoppingCartItem>(items);
foreach (var item in items2)
{
if (!itemDtos.Any(x => x.Id == item.Id))
{
context.Entry(item).State = EntityState.Deleted;
}
}
// Add/update items
foreach (var dto in itemDtos)
{
var item = items.FirstOrDefault(x => x.Id == dto.Id);
if (item == null)
{
items.Add(new ShoppingCartItem
{
Id = Guid.NewGuid(),
ShoppingCartId = shoppingCartId,
ProductId = dto.ProductId,
Quantity = dto.Quantity,
Price = dto.Price,
PriceDiscount = dto.PriceDiscount
});
}
else
{
item.ProductId = dto.ProductId;
item.Quantity = dto.Quantity;
item.Price = dto.Price;
item.PriceDiscount = dto.PriceDiscount;
}
}
}
}
SetItemPriceCommandStrategyHandler
public class SetItemPriceCommandStrategyHandler : ICommandStrategyHandler<SetItemPriceCommandStrategy>
{
private readonly IDataQueryHandler<GetProductDetailsDataQuery, ProductDetails> getProductDetailsQuery;
public SetItemPriceCommandStrategyHandler(
IDataQueryHandler<GetProductDetailsDataQuery, ProductDetails> getProductDetailsQuery)
{
if (getProductDetailsQuery == null)
throw new ArgumentNullException("getProductDetailsQuery");
this.getProductDetailsQuery = getProductDetailsQuery;
}
public void Handle(SetItemPriceCommandStrategy command)
{
var shoppingCartItem = command.ShoppingCartItem;
var product = getProductDetailsQuery.Handle(new GetProductDetailsDataQuery { ProductId = shoppingCartItem.ProductId });
// TODO: For products with custom calculations, need to use selections on shopping cart item
// as well as custom formula and pricing points from product to calculate the item price.
shoppingCartItem.Price = product.Price;
}
}
GetProductDetailsDataQueryHandler
public class GetProductDetailsDataQueryHandler : IDataQueryHandler<GetProductDetailsDataQuery, ProductDetails>
{
private readonly IAppContext context;
public GetProductDetailsDataQueryHandler(IAppContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
}
public ProductDetails Handle(GetProductDetailsDataQuery query)
{
return (from product in context.Products
where product.Id == query.ProductId
select new ProductDetails
{
Id = product.Id,
Name = product.Name,
Price = product.Price
}).FirstOrDefault();
}
}
SetTotalsCommandStrategyHandler
public class SetTotalsCommandStrategyHandler : ICommandStrategyHandler<SetTotalsCommandStrategy>
{
private readonly ICommandStrategyHandler<SetDiscountsCommandStrategy> setDiscountsCommand;
private readonly ICommandStrategyHandler<SetSalesTaxCommandStrategy> setSalesTaxCommand;
public SetTotalsCommandStrategyHandler(
ICommandStrategyHandler<SetDiscountsCommandStrategy> setDiscountsCommand,
ICommandStrategyHandler<SetSalesTaxCommandStrategy> setSalesTaxCommand
)
{
if (setDiscountsCommand == null)
throw new ArgumentNullException("setDiscountsCommand");
if (setSalesTaxCommand == null)
throw new ArgumentNullException("setSalesTaxCommand");
this.setDiscountsCommand = setDiscountsCommand;
this.setSalesTaxCommand = setSalesTaxCommand;
}
public void Handle(SetTotalsCommandStrategy command)
{
var shoppingCart = command.ShoppingCart;
// Important: Discounts must be calculated before sales tax to ensure the discount is applied
// to the subtotal before tax is calculated.
setDiscountsCommand.Handle(new SetDiscountsCommandStrategy { ShoppingCart = shoppingCart });
setSalesTaxCommand.Handle(new SetSalesTaxCommandStrategy { ShoppingCart = shoppingCart });
}
}
SetDiscountsCommandStrategyHandler
public class SetDiscountsCommandStrategyHandler : ICommandStrategyHandler<SetDiscountsCommandStrategy>
{
public void Handle(SetDiscountsCommandStrategy command)
{
var shoppingCart = command.ShoppingCart;
// TODO: Set discounts according to business rules
foreach (var item in shoppingCart.Items)
{
item.PriceDiscount = 0;
}
shoppingCart.SubtotalDiscounts = 0;
shoppingCart.SalesTaxDiscounts = 0;
shoppingCart.ShippingDiscounts = 0;
}
}
SetSalesTaxCommandStrategyHandler
public class SetSalesTaxCommandStrategyHandler : ICommandStrategyHandler<SetSalesTaxCommandStrategy>
{
public void Handle(SetSalesTaxCommandStrategy command)
{
var shoppingCart = command.ShoppingCart;
var postalCode = command.ShoppingCart.ShippingPostalCode;
bool isInCalifornia = !string.IsNullOrEmpty(postalCode) ?
// Matches 90000 to 96200
Regex.IsMatch(postalCode, #"^9(?:[0-5]\d{3}|6[0-1]\d{2}|6200)(?:-?(?:\d{4}))?$") :
false;
if (isInCalifornia)
{
var subtotal = shoppingCart.GetSubtotal();
// Rule for California - charge a flat 7.75% if the zip code is in California
var salesTax = subtotal * 0.0775M;
shoppingCart.SalesTax = salesTax;
}
}
}
Do note that there is no shipping calculation in this workflow. This is primarily because the shipping calculation may depend on external APIs and it may take some time to return. Therefore, I am planning to make the AddToCart workflow a step that runs instantaneously when an item is added and make a CalculateShippingAndTax workflow that happens after the fact that updates the UI again after the totals have been retrieved from their (possibly external) sources, which might take time.
Does this solve the problem? Yes, it does fix the real-world problems I was having when commands need to depend on queries.
However, it feels like this really only separates queries from commands conceptually. Physically, they still depend on one another unless you only look at the IDataCommand and IDataQuery abstractions that only depend on ApplicationDbContext. I am not sure if this is the intent of qujck or not. I am also uncertain if this solves the bigger issue of the design being transferable to CQRS or not, but since it is not something I am planning for I am not that concerned about it.
There are always trade offs to consider between conflicting design principles. The way to resolve it is to look at the underlying reasons behind the principles. In this case, being unable to run a query without running the command is problematic, but being unable to run a command without running the query is generally harmless. As long as there's a way to run the query standalone, I see no reason not to add the query result to the command, especially if done something like this:
QueryResult command()
{
// do command stuff
return query();
}

Raven paging queries in a specific way

I'm developing an ASP.NET MVC application using RavenDB 3. I don't have a lot of experience with raven.
In general, when executing queries to display data, the first 128 items are returned on the page. More records are added in an "infinite scroll"-manner by using paged queries.
Now, however, I have the requirement that items are loaded in 'groups'.
Assume the following class:
public class Item {
public string Id { get; set; }
public int Group { get; set; }
public string text { get; set; }
}
Assume the database contains 40 items having group='1', 40 items having group='2' and 50 items having group='3'.
This is a total of 130 items. This would mean that the last 'group' fetched would not be complete. It would be missing 2 items.
I would like a mechanism that is aware of this, so that it would fetch at least 128 AND would fetch 'extra' if the last group is not completely included.
Afterwards, when I fetch the next page, I would like it to start with the next group.
Is there any way I can make this work without 'fabricating' a single page myself by doing more than one call?
EDIT: I cannot assume the groups are perfectly equal in size, but I can assume the sizes will be 'simular'
Also, I cannot change the design to store all items in a single 'group'-object for instance.
Okay so basically what you will need to do is calculate the number of results that were in the previous pages and the number of results that are in the current page. Below is a quick example app to give you an idea of how to do it. One caveat, if the number of results for the current group range exceeds the MaxNumberOfRequestsPerSession than an error will be thrown, so you might want to put some handling in there for that.
Note for running this example:
Make sure your platform is set to x64 in visual studio if you are using the most recent versions of RavenDB. Otherwise, this example will thrown an error about Voron not being stable in 32 bit mode.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Raven.Client;
using Raven.Client.Embedded;
using Raven.Client.Listeners;
namespace ConsoleApplication
{
internal class Program
{
private static void Main(string[] args)
{
using (var gdm = new GroupDataManager())
{
Console.WriteLine("Started Seeding");
gdm.Seed().Wait();
Console.WriteLine("Finished Seeding");
Console.WriteLine("===============================================================");
Console.WriteLine("Get First Page");
Console.WriteLine("===============================================================");
var firstPage = gdm.GetPagedGroupResults(1, 3).Result;
foreach (var item in firstPage)
{
Console.WriteLine(item.Text);
}
Console.WriteLine("===============================================================");
Console.WriteLine("Get Second Page");
Console.WriteLine("===============================================================");
var secondPage = gdm.GetPagedGroupResults(2, 3).Result;
foreach (var item in secondPage)
{
Console.WriteLine(item.Text);
}
}
Console.ReadLine();
}
}
public class GroupDataManager : IDisposable
{
private readonly IDocumentStore _documentStore = new EmbeddedRavenDatabase().Store;
public void Dispose()
{
_documentStore.Dispose();
}
public async Task Seed()
{
var rnd = new Random();
using (var session = _documentStore.OpenAsyncSession())
{
for (var groupNumber = 1; groupNumber < 15; groupNumber++)
{
for (var i = 0; i < rnd.Next(5, 25); i++)
{
var item = new Item
{
Group = groupNumber,
Text = string.Format("Group: {0} Item:{1}", groupNumber, i)
};
await session.StoreAsync(item);
}
}
await session.SaveChangesAsync();
}
}
public async Task<IList<Item>> GetPagedGroupResults(int page, int numberOfGroupsPerPage)
{
var startingGroup = ((page - 1) * numberOfGroupsPerPage) + 1;
using (var session = _documentStore.OpenAsyncSession())
{
// Calculate the number of items that were contained in the previous groups
var numberToSkip = await session.Query<Item>().CountAsync(item => item.Group < startingGroup);
var endGroup = startingGroup + numberOfGroupsPerPage;
// Calculate the number of items that are in the current range of groups
var numberToTake = await session.Query<Item>().CountAsync(item => item.Group >= startingGroup && item.Group < endGroup);
var results = await session.Query<Item>().Skip(numberToSkip).Take(numberToTake).ToListAsync();
return results;
}
}
}
public class Item
{
public string Id { get; set; }
public int Group { get; set; }
public string Text { get; set; }
}
/// <summary>
/// For Testing Only. Prevents stale queries
/// </summary>
public class NoStaleQueriesAllowed : IDocumentQueryListener
{
public void BeforeQueryExecuted(IDocumentQueryCustomization queryCustomization)
{
queryCustomization.WaitForNonStaleResults();
}
}
public class EmbeddedRavenDatabase
{
private static bool _configured = false;
private static readonly Lazy<IDocumentStore> _lazyDocStore = new Lazy<IDocumentStore>(() =>
{
var docStore = new EmbeddableDocumentStore
{
RunInMemory = true
};
docStore.RegisterListener(new NoStaleQueriesAllowed());
docStore.Initialize();
return docStore;
});
public IDocumentStore Store
{
get { return _lazyDocStore.Value; }
}
}
}

MvvmCross & MvxAdapter & Polymorphic Types with custom controls

I am currently trying to implement my own version of the polymorphic types demo that is located here:
https://github.com/MvvmCross/MvvmCross-Tutorials/tree/master/Working%20With%20Collections
And I have it working as the demo shows. However, I am looking to extend that demo to have more complex controls inside of the MvxListView. I am wanting to have each of the list items control a fragment that has a View and a core ViewModel for additional processing.
I am unsure of the correct way of implementing this.
The code that I am using to create the custom view is this:
protected override View GetBindableView(View convertView, Object source, Int32 templateId)
{
var listItem = (TodayPanel) source;
if (listItem != null)
templateId = (Int32) typeof (Resource.Layout).GetField(listItem.View).GetValue(null);
return base.GetBindableView(convertView, source, templateId);
}
As always, it's probably something simple that I am missing, but any help would be appreciated.
Thanks!
I hate it when this happens, but after posting my question, I stepped away from the computer for a little bit and started to do something else. At that point, everything clicked into place. Stuart, in response to your question, the TodayPanel was NOT an MvxModelView, and therein was the crux of the problem. What I was doing was passing a list of TodayPanels into the listview, which was an SQLite entity object and not an MvxModelView object.
For others that might be struggling with this, I am going to post my solution here.
So here is what I ended up doing. I first created a class for each of the TodayPanel entity objects that inherited from an abstract base class that inherited from MvxModelView.
public abstract class TodayBaseViewModel : MvxViewModel
{
protected TodayViewModel TodayViewModel { get; set; }
protected IDataService DataService { get; set; }
public String Name { get; set; }
public String Title { get; set; }
public Boolean CanHide { get; set; }
public Boolean Visible { get; set; }
public Int32 SortOrder { get; set; }
public String View { get; set; }
protected abstract void SetEventHandlers();
protected BaseViewModel(IDataService dataService)
{
DataService = dataService;
}
public void Init(TodayViewModel todayViewModel)
{
TodayViewModel = todayViewModel;
SetEventHandlers();
}
}
I made it abstract as I wanted 0 or more event handlers to be attached in the final class. which is done through the abstract SetEventHandlers() method:
public class CoachSaysViewModel : TodayBaseViewModel
{
public CoachSaysViewModel(IDataService dataService)
: base(dataService)
{
}
protected override void SetEventHandlers()
{
TodayViewModel.ConnectionUpdated += TodayViewModelConnectionUpdated;
TodayViewModel.NewActivityReceived += TodayViewModelNewActivityReceived;
}
protected void TodayViewModelNewActivityReceived(Object sender, EventArgs.ActivityReceivedEventArgs e)
{
}
protected void TodayViewModelConnectionUpdated(Object sender, EventArgs.ConnectionUpdatedEventArgs e)
{
}
}
Then I created an extension method that converts the TodayPanel entity to one of the classes that inherits from TodayBaseViewModel.
public static BaseViewModel ToBaseViewModel(this TodayPanel todayPanel, TodayViewModel todayViewModel)
{
BaseViewModel model = null;
switch (todayPanel.View)
{
case "Today_QuickView":
model = Mvx.IocConstruct<QuickViewViewModel>();
break;
case "Today_CoachSays":
model = Mvx.IocConstruct<CoachSaysViewModel>();
break;
}
if (model == null)
return null;
model.CanHide = todayPanel.CanHide;
model.Name = todayPanel.Name;
model.SortOrder = todayPanel.SortOrder;
model.Title = todayPanel.Title;
model.View = todayPanel.View;
model.Visible = todayPanel.Visible;
model.Init(todayViewModel);
return model;
}
That then allowed me to create a list of MvxViewModels that are then bound to the MvxListView and hence are allowed to do the additional processing that I am wanting to do.
I'm sure that there are some improvements that I can do to the end result, and if you see anything feel free to point it out. :)

How to identify what Session Ninject should bind

Using for the first time: Asp.NET MVC, NHibernate(FNH), DI using Ninject. I was able to get everything working with one database, but now I'm trying to use two databases (DB1 and DB2 for sake of the eg). I have a dictionary of SessionFactory(s), that is keyed by a database identifier.
I can't figure out how to select the correct Session based on what is being requested from constructor injection. I have seen this How to inject different NHibernate Sessions (multi-db) to same repository with Controller controling which sesions with Ninject, but I didn't get it to work.
public ProductController(DB1.Model.IRepository<Product> prodRepo, DB2.Model.IRepository<Account> acctRepo)
{
[...]
}
NinjectWebCommon.cs snippet
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ISession>().ToMethod(ctx => NHibernateSessionModule.Provider.GetCurrentSession()).InRequestScope();
kernel.Bind(typeof(DB1.Model.IRepository<>)).To(typeof(NHibernateRepository<>));
kernel.Bind(typeof(DB2.Model.IRepository<>)).To(typeof(NHibernateRepository<>));
}
NHibernateSessionModule.cs: does UOW via Begin/End request
public class NHibernateSessionModule : IHttpModule
{
public static ISessionFactoryProvider Provider = new MultipleSessionFactoryProvider();
public void Dispose() { }
public void Init(HttpApplication context)
{
context.BeginRequest += BeginRequest;
context.EndRequest += EndRequest;
}
public void BeginRequest(object sender, EventArgs e)
{
Provider.BindNew();
}
public void EndRequest(object sender, EventArgs e)
{
Provider.Unbind();
}
}
MultipleSessionFactoryProvider.cs: Not quite sure I'm doing everything here correctly.
public class MultipleSessionFactoryProvider : ISessionFactoryProvider
{
public Dictionary<string, ISessionFactory> SessionFactories { get; private set; }
public static Func<Dictionary<string,ISessionFactory>> InitSessionFactories = GetFactories;
public MultipleSessionFactoryProvider() : this(InitSessionFactories())
{
}
public MultipleSessionFactoryProvider(Dictionary<string, ISessionFactory> factories)
{
SessionFactories = factories;
}
public static Dictionary<string, ISessionFactory> GetFactories()
{
Dictionary<string, ISessionFactory> ret = new Dictionary<string, ISessionFactory>();
Dictionary<string, string> connectionStrings = new Dictionary<string, string>();
connectionStrings.Add(ConfigurationManager.ConnectionStrings["DB1"].Name, ConfigurationManager.ConnectionStrings["DB1"].ConnectionString.ToString());
connectionStrings.Add(ConfigurationManager.ConnectionStrings["DB2"].Name, ConfigurationManager.ConnectionStrings["DB2"].ConnectionString.ToString());
foreach (KeyValuePair<string, string> pair in connectionStrings)
{
//Better way to do the mapping?
ISessionFactory factory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(pair.Value))
.Mappings(cfg => cfg.FluentMappings.Conventions.Setup(x => x.Add(AutoImport.Never()))
.AddFromAssemblyOf<ProductMap>())
.BuildConfiguration()
.CurrentSessionContext<WebSessionContext>().BuildSessionFactory();
ret.Add(pair.Key, factory);
}
return ret;
}
public void BindNew()
{
foreach (KeyValuePair<string, ISessionFactory> factory in SessionFactories)
{
Bind(OpenSession(factory.Key));
}
}
public void Bind(ISession session)
{
CurrentSessionContext.Bind(session);
}
public void Unbind()
{
foreach (KeyValuePair<string, ISessionFactory> factory in SessionFactories)
{
if (CurrentSessionContext.HasBind(factory.Value))
{
var sess = CurrentSessionContext.Unbind(factory.Value);
sess.Dispose();
}
}
}
public ISession OpenSession(string factoryId)
{
return SessionFactories[factoryId].OpenSession();
}
public ISession GetCurrentSession()
{
string factoryId = GetIdentifier(); //<--- How to implement this
return SessionFactories[factoryId].GetCurrentSession();
}
public String GetIdentifier()
{
return "DB1"; //Hardcoded for example
}
So, how can I implement GetIdentifier(), or alter my Ninject binding. I did take a look at Ninject Named binding, but didn't understand how to use that when I bind the Session.
public ProductController([Name("DB1")]DB1.Model.IRepository<Product> prodRepo, [Name("DB2")]DB2.Model.IRepository<Account> acctRepo)
Since this is the first go around with these technologies for me, please let me know if I'm doing anything out of practice, or that might be of concern; I'd like to stay with the HttpModule.
You need two bindings for session, one for each database
kernel.Bind<ISession>().ToMethod(ctx => GetSessionForDB1()).WhenInjectedInto(typeof(DB1.Model.IRepository<>)).InRequestScope();
kernel.Bind<ISession>().ToMethod(ctx => GetSessionForDB2()).WhenInjectedInto(typeof(DB2.Model.IRepository<>)).InRequestScope();

Serialize IList property on model when passed into Html.ActionLink

I'm trying to generate an Html.ActionLink with the following viewmodel:
public class SearchModel
{
public string KeyWords {get;set;}
public IList<string> Categories {get;set;}
}
To generate my link I use the following call:
#Html.ActionLink("Index", "Search", Model)
Where Model is an instance of the SearchModel
The link generated is something like this:
http://www.test.com/search/index?keywords=bla&categories=System.Collections.Generic.List
Because it obviously is only calling the ToString method on every property.
What I would like to see generate is this:
http://www.test.com/search/index?keywords=bla&categories=Cat1&categories=Cat2
Is there any way I can achieve this by using Html.ActionLink
In MVC 3 you're just out of luck because the route values are stored in a RouteValueDictionary that as the name implies uses a Dictionary internally which makes it not possible to have multiple values associated to a single key. The route values should probably be stored in a NameValueCollection to support the same behavior as the query string.
However, if you can impose some constraints on the categories names and you're able to support a query string in the format:
http://www.test.com/search/index?keywords=bla&categories=Cat1|Cat2
then you could theoretically plug it into Html.ActionLink since MVC uses TypeDescriptor which in turn is extensible at runtime. The following code is presented to demonstrate it's possible, but I would not recommend it to be used, at least without further refactoring.
Having said that, you would need to start by associating a custom type description provider:
[TypeDescriptionProvider(typeof(SearchModelTypeDescriptionProvider))]
public class SearchModel
{
public string KeyWords { get; set; }
public IList<string> Categories { get; set; }
}
The implementation for the provider and the custom descriptor that overrides the property descriptor for the Categories property:
class SearchModelTypeDescriptionProvider : TypeDescriptionProvider
{
public override ICustomTypeDescriptor GetTypeDescriptor(
Type objectType, object instance)
{
var searchModel = instance as SearchModel;
if (searchModel != null)
{
var properties = new List<PropertyDescriptor>();
properties.Add(TypeDescriptor.CreateProperty(
objectType, "KeyWords", typeof(string)));
properties.Add(new ListPropertyDescriptor("Categories"));
return new SearchModelTypeDescriptor(properties.ToArray());
}
return base.GetTypeDescriptor(objectType, instance);
}
}
class SearchModelTypeDescriptor : CustomTypeDescriptor
{
public SearchModelTypeDescriptor(PropertyDescriptor[] properties)
{
this.Properties = properties;
}
public PropertyDescriptor[] Properties { get; set; }
public override PropertyDescriptorCollection GetProperties()
{
return new PropertyDescriptorCollection(this.Properties);
}
}
Then we would need the custom property descriptor to be able to return a custom value in GetValue which is called internally by MVC:
class ListPropertyDescriptor : PropertyDescriptor
{
public ListPropertyDescriptor(string name)
: base(name, new Attribute[] { }) { }
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { throw new NotImplementedException(); }
}
public override object GetValue(object component)
{
var property = component.GetType().GetProperty(this.Name);
var list = (IList<string>)property.GetValue(component, null);
return string.Join("|", list);
}
public override bool IsReadOnly { get { return false; } }
public override Type PropertyType
{
get { throw new NotImplementedException(); }
}
public override void ResetValue(object component) { }
public override void SetValue(object component, object value) { }
public override bool ShouldSerializeValue(object component)
{
throw new NotImplementedException();
}
}
And finally to prove that it works a sample application that mimics the MVC route values creation:
static void Main(string[] args)
{
var model = new SearchModel { KeyWords = "overengineering" };
model.Categories = new List<string> { "1", "2", "3" };
var properties = TypeDescriptor.GetProperties(model);
var dictionary = new Dictionary<string, object>();
foreach (PropertyDescriptor p in properties)
{
dictionary.Add(p.Name, p.GetValue(model));
}
// Prints: KeyWords, Categories
Console.WriteLine(string.Join(", ", dictionary.Keys));
// Prints: overengineering, 1|2|3
Console.WriteLine(string.Join(", ", dictionary.Values));
}
Damn, this is probably the longest answer I ever give here at SO.
with linq of course...
string.Join("", Model.Categories.Select(c=>"&categories="+c))

Resources