I have this method with lot of if statements, in which I'm filtering SharePoint list based on employee position.The result is query string which is passed as parameter to another method QuerySPList.
public List<Phone> GetListOfPhones(Employee emp)
{
List<Phone> records = new List<Phone>();
string query = string.Empty;
if (emp.Positions.Count == 1 && emp.Positions[0] == "Regular")
{
query = "";// some querystring
}
if (emp.Positions.Count == 1 && emp.Positions[0] == "OfficeDirector")
{
query = "";// some querystring
}
if (emp.Positions.Count == 1 && emp.Positions[0] == "Admin")
{
query = "";// some querystring
}
if (emp.Positions.Count == 2 && emp.Positions.Contains("Regular") && emp.Positions.Contains("OfficeDirector"))
{
query = "";// some querystring
}
var rawItems = QuerySPList(query);
foreach (SPListItem item in rawItems)
{
//business logic
}
return records;
}}
I've read that with implementing strategy pattern we can avoid messy code with lot of if's , however , I'm new to developing , and design patterns , so I need little help with this one,so if someone can guide me what should I do and give me advice , feel free to answer my question.I have idea , but I think that I'm going in the wrong direction.
Idea is to create interface IRoleHandler and than implement it to 3 classes , for example RoleAdmin ,RoleRegular,RoleOfficeDirector.Something like this :
public interface IRoleHandler<T>
{
string handleRole(T obj);
}
public class RoleAdmin:IRoleHandler<Employee>
{
public string handleRole(Employee emp)
{
if (emp.Positions.Count == 1 && emp.Positions[0] == "Admin")
{
//return some string query
}
}
}
Then idea is to create dictionary,something like this :
Dictionary<string, IRoleHandler<Employee>> strategyHandlers = new Dictionary<string, IRoleHandler<Employee>>();
strategyHandlers.Add("Admin", new RoleAdmin());
strategyHandlers.Add("Regular", new RoleRegularUser());
Firstly, I think you need to be less concerned with the 'internal structure' of the Strategy Pattern and more concerned with its design goal/use.
https://en.wikipedia.org/wiki/Strategy_pattern
the strategy pattern (also known as the policy pattern) is a software design pattern that enables an algorithm's behavior to be selected at runtime. The strategy pattern
defines a family of algorithms,
encapsulates each algorithm, and
makes the algorithms interchangeable within that family.
To me, it looks like you are actually intending to "create" a string, based on some input value.
Therefore you would likely want to use one of the Creational patterns
My suggestion would be likely be Factory Pattern, or possibly Builder pattern. (Both linked in the creational patterns link)
private string BuildQueryByEmployee(Employee emp) {
// create your builder
EmployeeQueryBuilder mBuilder = new EmployeeQueryBuilder ();
// add # of positions
mBuilder.addPositionCount(emp.Positions.Count);
// add each position
for (int i =0 ; i < emp.Positions.Count; i++ ){
mBuilder.addPosition(emp.Positions[i]);
}
// 'build' your query.
// and return the query as a string.
return mBuilder.buildQuery();
}
Then inside your EmployeeQueryBuilder class, you handle the complexity of how to build your query based on the # of positions, and what they are.
Secondly, Here are some links that might be of use to you.
https://en.wikipedia.org/wiki/Software_design_pattern (General disc. of Patterns)
https://en.wikipedia.org/wiki/Design_Patterns (The "Gang of Four" book, essentially the first book on design patterns, and worth reading a few times, and writing each pattern out by hand a few times until you are comfortable with them, this alone will boost your coding ability by 2~3x if you haven't used patterns before, The examples in this book are somewhat out of date, since its motivating factor is writing a Text Editor... but still a 'classic', well worth knowing)
http://www.cs.wustl.edu/~schmidt/POSA/POSA2/ (One of the first books on patterns that have to deal with Concurrency) (POSA = Pattern Oriented Software Architecture) (might be overkill for now, but 'good to know about' in general)
I don't think strategy pattern is what you want. I would just refactor your code to extract the ifs to another method.
public List<Phone> GetListOfPhones(Employee emp)
{
List<Phone> records = new List<Phone>();
string query = BuildQueryByEmployeePosition(emp);
var rawItems = QuerySPList(query);
foreach (SPListItem item in rawItems)
{
//business logic
}
return records;
}
private string BuildQueryByEmployeePosition(Employee emp)
{
if (emp.Positions.Count == 1 && emp.Positions[0] == "Regular")
return "";// some querystring
if (emp.Positions.Count == 1 && emp.Positions[0] == "OfficeDirector")
return "";// some querystring
if (emp.Positions.Count == 1 && emp.Positions[0] == "Admin")
return "";// some querystring
if (emp.Positions.Count == 2 && emp.Positions.Contains("Regular") && emp.Positions.Contains("OfficeDirector"))
return "";// some querystring
return ""; // some default query
}
Here is a link to a great explanation of strategy pattern
(with a real example of a sort algorithm).
It's great to see that you are new to programming and is studying patterns, but
beware: using them when they are not needed is an anti-pattern.
In my opition it is better you use a kind of STATE PATTERN with COMPOSITE.
Example:
public abstract class Position
{
public abstract List<int> ListOfPhones();
}
public class Employer
{
public virtual IList<Position> CurrentPositions { get; set; }
}
public class Manager : Employer
{
public Manager(List<Position> positions)
{
this.CurrentPositions = positions;
}
public IEnumerable<int> GetNumbers()
{
foreach (Position position in this.CurrentPositions)
foreach (var number in position.ListOfPhones())
yield return number;
}
}
Code above is incomplete, just for you get the ideia.
I agree with mawalker, you don't need strategy pattern as you have the same behavior for all cases. What do you need is some kind of creational patterns. I would refactor your code using Builder and Chain of Responsibility patterns.
1) Implement base class:
public abstract class EmployeeHandler
{
private readonly EmployeeHandler _nextHandler;
protected EmployeeHandler(EmployeeHandler nextHandler)
{
_nextHandler = nextHandler;
}
public string BuildQuery(Employee emp)
{
if (CanHandle(emp))
{
return GetQuery(emp);
}
if (_nextHandler == null)
{
return string.Empty;
}
return _nextHandler.BuildQuery(emp);
}
protected abstract string GetQuery(Employee emp);
protected abstract bool CanHandle(Employee emp);
}
2) Define concrete implementations:
public class RegularEmployeeHandler : EmployeeHandler
{
public RegularEmployeeHandler(EmployeeHandler nextHandler) : base(nextHandler) {
}
protected override bool CanHandle(Employee emp)
{
return emp.Positions.Count == 1 && emp.Positions[0] == "Regular";
}
protected override string GetQuery(Employee emp)
{
return "some regular query";
}
}
public class OfficeDirectorEmployeeHandler : EmployeeHandler
{
public OfficeDirectorEmployeeHandler(EmployeeHandler nextHandler) : base(nextHandler) {
}
protected override bool CanHandle(Employee emp)
{
return emp.Positions.Count == 1 && emp.Positions[0] == "OfficeDirector";
}
protected override string GetQuery(Employee emp)
{
return "some office director query";
}
}
public class AdminEmployeeHandler : EmployeeHandler
{
public AdminEmployeeHandler(EmployeeHandler nextHandler) : base(nextHandler) {
}
protected override bool CanHandle(Employee emp)
{
return emp.Positions.Count == 1 && emp.Positions[0] == "Admin";
}
protected override string GetQuery(Employee emp)
{
return "some admin query";
}
}
public class RegularAndOfficeDirectorEmployeeHandler : EmployeeHandler
{
public RegularAndOfficeDirectorEmployeeHandler(EmployeeHandler nextHandler) : base(nextHandler) {
}
protected override string GetQuery(Employee emp)
{
return "some regular and director query";
}
protected override bool CanHandle(Employee emp)
{
return emp.Positions.Count == 2 && emp.Positions.Contains("Regular") && emp.Positions.Contains("OfficeDirector");
}
}
3) And finally you main class will changed like this:
public class YouMainClass
{
private readonly EmployeeHandler _firstHandler;
public YouMainClass()
{
var regularHandler = new RegularEmployeeHandler(null);
var officeDirectorHandler = new OfficeDirectorEmployeeHandler(regularHandler);
var adminHandler = new AdminEmployeeHandler(officeDirectorHandler);
_firstHandler = new RegularAndOfficeDirectorEmployeeHandler(adminHandler);
}
public List<Phone> GetListOfPhones(Employee emp, IQueryBuilder queryBuilder)
{
List<Phone> records = new List<Phone>();
string query = _firstHandler.BuildQuery(emp);
var rawItems = QuerySPList(query);
foreach (SPListItem item in rawItems)
{
//business logic
}
return records;
}
}
Related
I have a bit of logic in my code and I'm wondering if there's a better way of re-writing it. I have two user-defined server objects (serverA and serverB) and I want to proceed if both of them are of enum ServerType Web or Database.
Current code is clumsy:
if((serverA.ServerType == ServerType.Web || serverA.ServerType == ServerType.Database) && (serverB.ServerType == ServerType.Web || serverB.ServerType == ServerType.Database))
{
// do stuff
}
I'm after an elegant succinct way of writing that.
I would have a new property in your user-defined server object
public class Server
{
public ServerType ServerType { get; set; }
public bool IsDatabaseOrWeb
{
get
{
return ServerType == ServerType.Web || ServerType == ServerType.Database;
}
}
}
then your function would look like this
if (serverA.IsDatabaseOrWeb && serverB.IsDatabaseOrWeb)
{
// do stuff
}
This way spells out more clearly that you are asking the same question about both servers and allows you to change the condition in one place if needed.
if(IsRightType(serverA) && IsRightType(serverB))
{
// do stuff
}
bool IsRightType(Server server)
{
return server.ServerType == ServerType.Web || server.ServerType == ServerType.Database;
}
Maybe following solution would be more elegant. Implement an abstract class so all your server objects would inherit from it.
public enum ServerType
{
Web,
Database,
SomethingElse
}
public class ServerA : ServerObject
{
}
public class ServerB : ServerObject
{
}
public abstract class ServerObject
{
public ServerType ServerType { get; set; }
}
And then implement a method for checking if an object meets your condition.
public bool IfWebOrDatabase(ServerObject so)
{
return so.ServerType == ServerType.Web || so.ServerType == ServerType.Database;
}
And use a much shorter version of the If then.
if (IfWebOrDatabase(serverA) && IfWebOrDatabase(serverB))
{
// do stuff
}
A similar approach is to use extension method for enum ServerType. A possible way to write the code would be the following:
public enum ServerType
{
Web,
Database,
Bi
}
public static class Extensions
{
public static bool IsOperational(this ServerType st)
{
var operationalTypes = new List<ServerType> { ServerType.Web, ServerType.Database };
return operationalTypes.Contains(st);
}
}
class Program
{
static void Main(string[] args)
{
var st1 = ServerType.Web;
var st2 = ServerType.Database;
var st3 = ServerType.Bi;
bool isSt1Operational = st1.IsOperational();
bool isSt2Operational = st2.IsOperational();
bool isSt3Operational = st3.IsOperational();
}
}
Add a property to the class that contains ServerType:
public bool IsDatabaseOrWeb
{
get
{
return (ServerType == ServerType.Web || ServerType == ServerType.Database);
}
}
Then use it like:
if(ServerA.IsDatabaseOrWeb && ServerB.IsDatabaseOrWeb)
{
}
The trend you should be noticing in these answers is that the solution is worse than the problem. If you wanted to use lambda expressions in linq, it gets even worse:
if (new Server[] { serverA, serverB }.All(s => new ServerType[] { ServerType.Web, ServerType.Database }.Contains(s.ServerType)))
{
}
Even if you break it apart:
var badIdea = new Server[] { entity, serverB };
var alsoBad = new ServerType[] { ServerType.Web, ServerType.Database };
if (badIdea.All(s => alsoBad.Contains(s.ServerType)))
{
}
Sometimes the simplest expression is still messy. Your if statement is fine unchanged. Maybe just put a carriage return in the middle so that you can see it on the screen all at once.
Bitflags may help here.
[Flags]
public enum ServerType
{
None = 0,
Web=1,
Database = 2,
Other = 4
}
To use it
var flags = ServerType.Web | ServerType.Database;
var bothWebOrDb = (serverA.ServerType & flags)
& (serverB.ServerType & flags);
if (bothWebOrDb != ServerType.None)
{
}
Which be reduced to the following
var flags = ServerType.Web | ServerType.Database;
var bothWebOrDb = serverA.ServerType & serverB.ServerType & flags;
if (bothWebOrDb != ServerType.None)
{
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I have the below function that works and cycles through 3 possible levels.
Is there a way to do the same as the function below but not have to do the multiple foreach statements?
Basically the reponses list can contain multiple GroupResponseTypes
each of these can contain multiple ElementResponseBaseTypes
which can be a variety of types themselves
I'm interested in finding a value in an ElementResponseType
And each ElementResponseBaseType itself can be a GroupResponseType, it to containing multiple types.
So i'm looking at an easy way to scan through the whole structure for a specific Element.Reference
and return the relevant value
Any help is much appreciated
public static string GetValueFromFormField(List<ResponseBaseType> responses, string fieldref)
{
string fieldvalue = String.Empty;
foreach (GroupResponseType groups in responses)
{
foreach (ElementResponseBaseType firstelements in groups.Responses)
{
if (firstelements.GetType() == typeof(ElementResponseType))
{
if (firstelements.Element.Reference == fieldref)
{
ElementResponseType firstelement = new ElementResponseType();
firstelement = (ElementResponseType)firstelements;
fieldvalue = firstelement.Value;
}
}
else if (firstelements.GetType() == typeof(GroupResponseType))
{
GroupResponseType secondgroup = new GroupResponseType();
secondgroup = (GroupResponseType)firstelements;
foreach (ElementResponseBaseType secondelements in secondgroup.Responses)
{
if (secondelements.GetType() == typeof(ElementResponseType))
{
if (secondelements.Element.Reference == fieldref)
{
ElementResponseType secondelement = new ElementResponseType();
secondelement = (ElementResponseType)secondelements;
fieldvalue = secondelement.Value;
}
}
else if (secondelements.GetType() == typeof(GroupResponseType))
{
GroupResponseType thirdgroup = new GroupResponseType();
thirdgroup = (GroupResponseType)secondelements;
foreach (ElementResponseBaseType thirdelements in thirdgroup.Responses)
{
if (thirdelements.GetType() == typeof(ElementResponseType))
{
if (thirdelements.Element.Reference == fieldref)
{
ElementResponseType thirdelement = new ElementResponseType();
thirdelement = (ElementResponseType)thirdelements;
fieldvalue = thirdelement.Value;
}
}
}
}
}
}
}
}
return fieldvalue;
}
You need to identify which parts of your code are used repeatedly and factor them out into new methods. If you do that over and over, eventually you will obtain something like this:
public static string GetValueFromResponses(IEnumerable<ElementResponseBaseType> responses, string fieldref)
{
foreach (ElementResponseBaseType response in responses)
{
ElementResponseType element = response as ElementResponseType;
if (element != null)
{
string foundValue = CheckElement(element, fieldref);
if (foundValue != null)
{
return foundValue;
}
}
else
{
GroupResponseType group = response as GroupResponseType;
if (group != null)
{
string foundValue = GetValueFromResponses(group.Responses, fieldref);
if (foundValue != null)
{
return foundValue;
}
}
}
}
return string.Empty;
}
private static string CheckElement(ElementResponseType element, string fieldref)
{
if (element.Element.Reference == fieldref)
{
return element.Value;
}
return null;
}
Here's a version that uses Linq (this contains all of the functionality in your original method):
public static string GetValueFromResponses(IEnumerable<ElementResponseBaseType> responses, string fieldref)
{
var foundInElements = responses.OfType<ElementResponseType>()
.Select(e => CheckElement(e, fieldref));
var foundInGroups = responses.OfType<GroupResponseType>()
.Select(g => GetValueFromResponses(g.Responses,
fieldref));
return foundInElements.Concat(foundInGroups)
.FirstOrDefault(s => s != null) ?? string.Empty;
}
private static string CheckElement(ElementResponseType element, string fieldref)
{
if (element.Element.Reference == fieldref)
{
return element.Value;
}
return null;
}
You should give your base type, in this case ResponseBaseType, a member that returns all of it's decedent leaf nodes. You can then implement the behavior of that member separately for each type. The group type can return all of the leaves in all of its own children (recursively), and the single item can return itself.
You can then take any instance of the base type and get all of the leaves, or, in this case, the first leaf. Note that since you're only trying to get the first result here, not all of them, you'd benefit from making your implementation of the group's member use deferred execution, so that you don't need to bother computing all of the values just to get the first.
As complex as that might seem at first, it takes very little code to actually implement.
public abstract class ResponseBaseType
{
public abstract IEnumerable<ElementResponseType> Leaves { get; }
}
public class GroupResponseType : ResponseBaseType
{
public IEnumerable<ResponseBaseType> Children { get; private set; }
public override IEnumerable<ElementResponseType> Leaves
{
get
{
return Children.SelectMany(child => child.Leaves);
}
}
}
public class ElementResponseType : ResponseBaseType
{
public override IEnumerable<ElementResponseType> Leaves
{
get
{
yield return this;
}
}
}
This enables you to take your list of responses, map it to a sequences of all of their leaves, and then get the first/last leaf from that.
responses.SelectMany(response => response.Leaves).Last();
I have a multiple layered application I'm rewriting using Entity Framework 4 w/ Code First. The important things:
In the data layer, on my context, I have:
public DbSet<MobileSerialContainer> Mobiles { get; set; }
This context has a static instance. I know, I know, terrible practice. There are reasons which aren't relevant to this post as to why I'm doing this.
MobileSerialContainer consists of the following:
[Table("Mobiles")]
public sealed class MobileSerialContainer
{
[Key]
public long Serial { get; set; }
[StringLength(32)]
public string Name { get; set; }
public MobileSerialContainer() { }
public MobileSerialContainer(Mobile mobile)
{
Mobile = mobile;
LeContext.Instance.Mobiles.Add(this);
}
[StringLength(1024)]
public string FullClassName
{
get { return Mobile == null ? "" : Mobile.GetType().AssemblyQualifiedName; }
set
{
if (string.IsNullOrEmpty(value) || value == FullClassName)
return;
Mobile = null;
var type = Type.GetType(value);
if (type == null)
return;
if (!type.IsSubclassOf(typeof(Mobile))
&& type != typeof(Mobile))
return;
var constructor = type.GetConstructor(new [] { GetType() });
// The problem here is that Person ( which extends mobile ) does not have a constructor that takes a MobileSerialContainer.
// This is a problem of course, because I want to make this entire layer transparent to the system, so that each derivative
// of Mobile does not have to implement this second constructor. Blasphemy!
if (constructor == null)
return;
Mobile = (Mobile)constructor.Invoke(new object[] { this });
}
}
public string SerializedString
{
get
{
return Mobile == null ? "" : Mobile.Serialize();
}
set
{
if (Mobile == null)
return;
if (string.IsNullOrEmpty(value))
return;
Mobile.Deserialize(value);
}
}
[NotMapped]
public Mobile Mobile { get; set; }
public void Delete()
{
LeContext.Instance.Mobiles.Remove(this);
}
}
Now... I know this is a long post bear with me. Mobile is this:
public class Mobile
{
public long Serial { get { return Container.Serial; } }
public string Name { get { return Container.Name; } set { Container.Name = value; } }
public Mobile()
{
Container = new MobileSerialContainer(this);
}
public Mobile(MobileSerialContainer container)
{
Container = container;
}
public void Delete()
{
Container.Delete();
}
private MobileSerialContainer Container { get; set; }
protected static string MakeSafeString(string value)
{
if (string.IsNullOrEmpty(value))
return value;
return value.Replace("&", "&")
.Replace(",", ",")
.Replace("=", "&eq;");
}
protected static string MakeUnsafeString(string value)
{
if (string.IsNullOrEmpty(value))
return value;
return value.Replace("&eq;", "=")
.Replace(",", ",")
.Replace("&", "&");
}
public virtual string Serialize()
{
string result = "";
var properties = PersistentProperties;
foreach (var property in properties)
{
string name = MakeSafeString(property.Name);
var value = property.GetValue(this, null);
string unsafeValueString = (string)Convert.ChangeType(value, typeof(string));
string valueString = MakeSafeString(unsafeValueString);
result += name + "=" + valueString + ",";
}
return result;
}
public virtual void Deserialize(string serialized)
{
var properties = PersistentProperties.ToList();
var entries = serialized.Split(',');
foreach (var entry in entries)
{
if (string.IsNullOrEmpty(entry))
continue;
var keyPair = entry.Split('=');
if (keyPair.Length != 2)
continue;
string name = MakeUnsafeString(keyPair[0]);
string value = MakeUnsafeString(keyPair[1]);
var property = properties.FirstOrDefault(p => p.Name == name);
if (property == null)
continue;
object rawValue = Convert.ChangeType(value, property.PropertyType);
property.SetValue(this, rawValue, null);
}
}
protected IEnumerable<PropertyInfo> PersistentProperties
{
get
{
var type = GetType();
var properties = type.GetProperties().Where(p => p.GetCustomAttributes(typeof(PersistAttribute), true).Any());
return properties;
}
}
}
Several layers above this, I have the System layer, in which I have the class Person:
public class Person : Mobile
{
[Persist]
public string LastName { get; set; }
}
The basic idea is this: I want the System layer to have almost no knowledge of the Data layer. It creates anything that extends "Mobile", which is automatically saved to the database. I don't want to have a table for Person, hence the weird serialization stuff, because there are literally hundreds of classes that extend Mobile. I don't want hundreds of tables. All of this serialization stuff works perfectly, the SerializedString bit, saving everything, reloading, etc etc. The only thing I haven't come up with a solution for is:
I don't want to have to implement the two constructors to Person:
public Person() : base() { }
public Person(MobileSerialContainer container)
: base(container) { }
as that requires the System layer to have more knowledge of the Data layer.
The weird serialization string thing stays. The reflection business stays. I know it's slow, but database writes and reads are very rare, and asynchronous anyway.
Besides that, I'm looking for any cool ideas about how to resolve this. Thanks!
[edit]
Changed a miswritten line of code in the MobileSerialContainer class pasted here.
If you are rewriting your application, you could reconsider all the design of your system to keep your domain layer (System layer) independent from your Data Access layer using :
A repository pattern to handle access to your database (dataContext)
A domain layer for your business objects (mobile and stuff)
Inversion Of Control pattern (IOC) to keep your layers loosely coupled
The inheritance stuff is definitively not the good way to go to keep a system loosely coupled.
What you want is type.GetConstructors() not type.GetConstructor(), this will let you get the base constructors and pass the type you are looking for.
This is really a generic (and probably a more subjective too) question. I have some classes where I use an interface to define a standard approach to validating the object state. When I did this, I got to scratching my head... is it best to
1.) allow the constructor (or initializing method) to silently filter out the errant information automatically or...
2.) allow the client to instantiate the object however and let the client also call the interface's IsValid property or Validate() method before moving forward?
Basically one approach is silent but could be misleading in that the client may not be aware that certain pieces of information were filtered away due to it not meeting the validation criteria. The other approach then would be more straight forward, but also adds a step or two? What's typical here?
Okay, after a long day of trying to keep up with some other things, I finally did come up with an example. Please for me for it as it's not ideal and by no means something wonderful, but hopefully should serve well enough to get the point across. My current project is just too complicated to put something simple out for this, so I made something up... and trust me... totally made up.
Alright, the objects in the example are this:
Client: representing client-side code (Console App btw)
IValidationInfo: This is the actual interface I'm using in my current project. It allows me to create a validation framework for the "back-end" objects not necessarily intended for the Client to use since the business logic could be complicated enough. This also allowed me to separate validation code and call as-needed for the business logic.
OrderManager: This is an object the client-side code can use to manage their orders. It's client-friendly so-to-speak.
OrderSpecification: This is an object the client-side code can use to request an order. But if the business logic doesn't work out, an exception can be raised (or if necessary the order not added and exceptions ignored...) In my real-world example I actually have an object that's not quite so black-and-white as to which side of this fence it goes... thus my original question when I realized I could push validation request (calling IsValid or Validate()) to the cilent.
CustomerDescription: represents customers to which I've classified (pretending to have been read from a DB.
Product: Represents a particular product which is classified also.
OrderDescription: Represents the official order request.The business rule is that the Customer cannot order anything to which they've not been classified (I know.. that's not very real-world, but it gave me something to work with...)
Ok... I just realized I can't attach a file here, so here's the code. I apologize for it's lengthy appearance. That was the best I could do to create a client-friendly front-end and business logic back-end using my Validation interface:
public class Client
{
static OrderManager orderMgr = new OrderManager();
static void Main(string[] args)
{
//Request a new order
//Note: Only the OrderManager and OrderSpecification are used by the Client as to keep the
// Client from having to know and understand the framework beyond that point.
OrderSpecification orderSpec = new OrderSpecification("Customer1", new Product(IndustryCategory.FoodServices, "Vending Items"));
orderMgr.SubmitOrderRequest(orderSpec);
Console.WriteLine("The OrderManager has {0} items for {1} customers.", orderMgr.ProductCount, orderMgr.CustomerCount);
//Now add a second item proving that the business logic to add for an existing customer works
Console.WriteLine("Adding another valid item for the same customer.");
orderSpec = new OrderSpecification("Customer1", new Product(IndustryCategory.FoodServices, "Sodas"));
orderMgr.SubmitOrderRequest(orderSpec);
Console.WriteLine("The OrderManager now has {0} items for {1} customers.", orderMgr.ProductCount, orderMgr.CustomerCount);
Console.WriteLine("Adding a new valid order for a new customer.");
orderSpec = new OrderSpecification("Customer2", new Product(IndustryCategory.Residential, "Magazines"));
orderMgr.SubmitOrderRequest(orderSpec);
Console.WriteLine("The OrderManager now has {0} items for {1} customers.", orderMgr.ProductCount, orderMgr.CustomerCount);
Console.WriteLine("Adding a invalid one will not work because the customer is not set up to receive these kinds of items. Should get an exception with message...");
try
{
orderSpec = new OrderSpecification("Customer3", new Product(IndustryCategory.Residential, "Magazines"));
orderMgr.SubmitOrderRequest(orderSpec);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
}
public interface IValidationInfo
{
string[] ValidationItems { get; }
bool IsValid { get; }
void Validate();
List<string> GetValidationErrors();
string GetValidationError(string itemName);
}
public class OrderManager
{
private List<OrderDescription> _orders = new List<OrderDescription>();
public List<OrderDescription> Orders
{
get { return new List<OrderDescription>(_orders); }
private set { _orders = value; }
}
public int ProductCount
{
get
{
int itemCount = 0;
this.Orders.ForEach(o => itemCount += o.Products.Count);
return itemCount;
}
}
public int CustomerCount
{
get
{
//since there's only one customer per order, just return the number of orders
return this.Orders.Count;
}
}
public void SubmitOrderRequest(OrderSpecification orderSpec)
{
if (orderSpec.IsValid)
{
List<OrderDescription> orders = this.Orders;
//Since the particular customer may already have an order, we might as well add to an existing
OrderDescription existingOrder = orders.FirstOrDefault(o => string.Compare(orderSpec.Order.Customer.Name, o.Customer.Name, true) == 0) as OrderDescription;
if (existingOrder != null)
{
List<Product> existingProducts = orderSpec.Order.Products;
orderSpec.Order.Products.ForEach(p => existingOrder.AddProduct(p));
}
else
{
orders.Add(orderSpec.Order);
}
this.Orders = orders;
}
else
orderSpec.Validate(); //Let the OrderSpecification pass the business logic validation down the chain
}
}
public enum IndustryCategory
{
Residential,
Textile,
FoodServices,
Something
}
public class OrderSpecification : IValidationInfo
{
public OrderDescription Order { get; private set; }
public OrderSpecification(string customerName, Product product)
{
//Should use a method in the class to search and retrieve Customer... pretending here
CustomerDescription customer = null;
switch (customerName)
{
case "Customer1":
customer = new CustomerDescription() { Name = customerName, Category = IndustryCategory.FoodServices };
break;
case "Customer2":
customer = new CustomerDescription() { Name = customerName, Category = IndustryCategory.Residential };
break;
case "Customer3":
customer = new CustomerDescription() { Name = customerName, Category = IndustryCategory.Textile };
break;
}
//Create an OrderDescription to potentially represent the order... valid or not since this is
//a specification being used to request the order
this.Order = new OrderDescription(new List<Product>() { product }, customer);
}
#region IValidationInfo Members
private readonly string[] _validationItems =
{
"OrderDescription"
};
public string[] ValidationItems
{
get { return _validationItems; }
}
public bool IsValid
{
get
{
List<string> validationErrors = GetValidationErrors();
if (validationErrors != null && validationErrors.Count > 0)
return false;
else
return true;
}
}
public void Validate()
{
List<string> errorMessages = GetValidationErrors();
if (errorMessages != null && errorMessages.Count > 0)
{
StringBuilder errorMessageReported = new StringBuilder();
errorMessages.ForEach(em => errorMessageReported.AppendLine(em));
throw new Exception(errorMessageReported.ToString());
}
}
public List<string> GetValidationErrors()
{
List<string> errorMessages = new List<string>();
foreach (string item in this.ValidationItems)
{
string errorMessage = GetValidationError(item);
if (!string.IsNullOrEmpty(errorMessage))
errorMessages.Add(errorMessage);
}
return errorMessages;
}
public string GetValidationError(string itemName)
{
switch (itemName)
{
case "OrderDescription":
return ValidateOrderDescription();
default:
return "Invalid item name.";
}
}
#endregion
private string ValidateOrderDescription()
{
string errorMessage = string.Empty;
if (this.Order == null)
errorMessage = "Order was not instantiated.";
else
{
if (!this.Order.IsValid)
{
List<string> orderErrors = this.Order.GetValidationErrors();
orderErrors.ForEach(ce => errorMessage += "\n" + ce);
}
}
return errorMessage;
}
}
public class CustomerDescription : IValidationInfo
{
public string Name { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public int ZipCode { get; set; }
public IndustryCategory Category { get; set; }
#region IValidationInfo Members
private readonly string[] _validationItems =
{
"Name",
"Street",
"City",
"State",
"ZipCode",
"Category"
};
public string[] ValidationItems
{
get { return _validationItems; }
}
public bool IsValid
{
get
{
List<string> validationErrors = GetValidationErrors();
if (validationErrors != null && validationErrors.Count > 0)
return false;
else
return true;
}
}
public void Validate()
{
List<string> errorMessages = GetValidationErrors();
if (errorMessages != null && errorMessages.Count > 0)
{
StringBuilder errorMessageReported = new StringBuilder();
errorMessages.ForEach(em => errorMessageReported.AppendLine(em));
throw new Exception(errorMessageReported.ToString());
}
}
public List<string> GetValidationErrors()
{
List<string> errorMessages = new List<string>();
foreach (string item in this.ValidationItems)
{
string errorMessage = GetValidationError(item);
if (!string.IsNullOrEmpty(errorMessage))
errorMessages.Add(errorMessage);
}
return errorMessages;
}
public string GetValidationError(string itemName)
{
//Validation methods should be called here... pretending nothings wrong for sake of discussion & simplicity
switch (itemName)
{
case "Name":
return string.Empty;
case "Street":
return string.Empty;
case "City":
return string.Empty;
case "State":
return string.Empty;
case "ZipCode":
return string.Empty;
case "Category":
return string.Empty;
default:
return "Invalid item name.";
}
}
#endregion
}
public class Product
{
public IndustryCategory Category { get; private set; }
public string Description { get; private set; }
public Product(IndustryCategory category, string description)
{
this.Category = category;
this.Description = description;
}
}
public class OrderDescription : IValidationInfo
{
public CustomerDescription Customer { get; private set; }
private List<Product> _products = new List<Product>();
public List<Product> Products
{
get { return new List<Product>(_products); }
private set { _products = value; }
}
public OrderDescription(List<Product> products, CustomerDescription customer)
{
this.Products = products;
this.Customer = customer;
}
public void PlaceOrder()
{
//If order valid, place
if (this.IsValid)
{
//Do stuff to place order
}
else
Validate(); //cause the exceptions to be raised with the validate because business rules were broken
}
public void AddProduct(Product product)
{
List<Product> productsToEvaluate = this.Products;
//some special read, validation, quantity check, pre-existing, etc here
// doing other stuff...
productsToEvaluate.Add(product);
this.Products = productsToEvaluate;
}
#region IValidationInfo Members
private readonly string[] _validationItems =
{
"Customer",
"Products"
};
public string[] ValidationItems
{
get { return _validationItems; }
}
public bool IsValid
{
get
{
List<string> validationErrors = GetValidationErrors();
if (validationErrors != null && validationErrors.Count > 0)
return false;
else
return true;
}
}
public void Validate()
{
List<string> errorMessages = GetValidationErrors();
if (errorMessages != null && errorMessages.Count > 0)
{
StringBuilder errorMessageReported = new StringBuilder();
errorMessages.ForEach(em => errorMessageReported.AppendLine(em));
throw new Exception(errorMessageReported.ToString());
}
}
public List<string> GetValidationErrors()
{
List<string> errorMessages = new List<string>();
foreach (string item in this.ValidationItems)
{
string errorMessage = GetValidationError(item);
if (!string.IsNullOrEmpty(errorMessage))
errorMessages.Add(errorMessage);
}
return errorMessages;
}
public string GetValidationError(string itemName)
{
switch (itemName)
{
case "Customer":
return ValidateCustomer();
case "Products":
return ValidateProducts();
default:
return "Invalid item name.";
}
}
#endregion
#region Validation Methods
private string ValidateCustomer()
{
string errorMessage = string.Empty;
if (this.Customer == null)
errorMessage = "CustomerDescription is missing a valid value.";
else
{
if (!this.Customer.IsValid)
{
List<string> customerErrors = this.Customer.GetValidationErrors();
customerErrors.ForEach(ce => errorMessage += "\n" + ce);
}
}
return errorMessage;
}
private string ValidateProducts()
{
string errorMessage = string.Empty;
if (this.Products == null || this.Products.Count <= 0)
errorMessage = "Invalid Order. Missing Products.";
else
{
foreach (Product product in this.Products)
{
if (product.Category != Customer.Category)
{
errorMessage += string.Format("\nThe Product, {0}, category does not match the required Customer category for {1}", product.Description, Customer.Name);
}
}
}
return errorMessage;
}
#endregion
}
Any reason you wouldn't want the constructor to noisily throw an exception if the information is valid? It's best to avoid ever creating an object in an invalid state, in my experience.
It's completely depends on the client. There's a trade-off as you already mentioned. By default approach number 1 is my favorite. Creating smart classes with good encapsulation and hiding details from client. The level of smartness depends who is going to use the object. If client is business aware you can reveal details according to the level of this awareness. This is a dichotomy and should not be treated as black or white.
Well if I correctly understood, there are basically two question - whether you should fail right away or later and whether you should omit/assume certain information.
1) I always prefer failing as soon as possible - good example is failing at compile time vs failing at run time - you always want to fail at compile time. So if something is wrong with the state of some object, as Jon said - throw exception right away as loudly as you can and deal with it - do not introduce additional complexity down the road as you'll be heading for if/elseif/elseif/elseif/else mumbo jumbo.
2) When it comes to user input, if you are in position to simply filter out errors automatically - just do it. For example, I almost never ask users for country - if I really need it, I automatically detect it from IP and display it in the form. It's way easier if user just needs to confirm/change the data - and I don't need to deal with null situation.
Now, in case we are talking about the data generated by code during some processing - for me situation is drastically different - I always want to know an much as possible (for easier debugging down the road) and ideally you never should destroy any piece of information.
To wrap up, in your case I would recommend that you keep IsValid as simple yes/no (not yes/no/maybe/kindaok/etc). If you can fix some problems automatically - do it, but consider that they keep object in IsValid yes. For everything else, you throw exception and go to IsValid=no.
I am trying to write master detail entry edit in Asp.net MVC model.
Fristly, let me show you two of my model classes.
Class name = Model\OrderDetailRepository.cs
public class OrderDetailRepository : IOrderDetailRepository
{
NorthwindEntities DB = new NorthwindEntities();
public IQueryable<Order_Detail> GetOrderDetailByOrderID(int OrderID)
{
return DB.Order_Details.Where(tableX => tableX.OrderID == OrderID);
}
public void InsertOrderDetail(Order_Detail _Order_Detail)
{
DB.Order_Details.AddObject(_Order_Detail);
}
public void UpdateOrderDetail(Order_Detail _Order_Detail)
{
DB.ObjectStateManager.ChangeObjectState(_Order_Detail, EntityState.Modified);
}
public void DeleteOrderDetailByOrderID(int OrderID)
{
Order_Detail _Order_Detail = DB.Order_Details.SingleOrDefault(x => x.OrderID == OrderID);
if (_Order_Detail != null)
DB.Order_Details.DeleteObject(_Order_Detail);
}
public void DeleteOrderDetailByCurrentRecord(Order_Detail _Order_Detail)
{
DB.Order_Details.DeleteObject(_Order_Detail);
}
public void Commit()
{
DB.SaveChanges();
}
}
Class name = Model\OrderMasterReposity.cs
public class OrderMasterReposity : IOrderMasterRepository
{
NorthwindEntities DB = new NorthwindEntities();
public IQueryable<Order> GetOrderMasterByOrderID(int OrderID)
{
return DB.Orders.Where(tableX => tableX.OrderID == OrderID);
}
public void InsertOrderMaster(Order _Order)
{
DB.Orders.AddObject(_Order);
}
public void UpdateOrderMaster(Order _Order)
{
DB.ObjectStateManager.ChangeObjectState(_Order, EntityState.Modified);
}
public void DeleteOrderMaster(int OrderID)
{
Order _Order = DB.Orders.SingleOrDefault(x => x.OrderID == OrderID);
if (_Order != null)
DB.Orders.DeleteObject(_Order);
}
public void Commit()
{
DB.SaveChanges();
}
}
After all upper code, I need to invoke these two class from my controller class.
So let's assume that below code i will write at controller layer.
OrderMasterReposity _OrderMasterReposity = new OrderMasterReposity();
OrderDetailRepository _OrderDetailRepository = new OrderDetailRepository();
_OrderMasterReposity.InsertOrderMaster(new Order{....});
_OrderDetailRepository.InsertOrderDetail(new Order_Detail{....});
_OrderMasterReposity.Commit();
_OrderDetailRepository.Commit();
What my problem is the way I doing now is not so professional way.
Because I need to invoke Commit function more than one time.
So, please could anyone give me more nicer way to code at Model layer?
You can use Unit of Work, see this example:
http://iridescence.no/post/ASPNET-MVC-DataContext-and-The-Unit-of-Work-Pattern.aspx
Best thing is that u implement Repository pattern properly
Moreover, Order detail has no meaning without order so according to DDD you should not define separate repository for order_detail. i would do it something like
Order _order = OrderMasterRepository.GetOrder(orderID);
_order.Property1 = value1;
_order.Property2 = value2;
_order.Property3 = value3;
foreach(var orderDetail in _order.OrderDetails)
{
orderDetail.Property1 = avalue1;
orderDetail.Property2 = avalue2;
.
.
}
OrderMasterRepository.Commit();
above statements will save data both in order and orderDetail table.