Best place to implement this business rule? - c#

I have a service call that returns to me an IEnumerable of CustomObject, this is a third party call that I don't have the liberty to modify. CustomObject can be assumed to have a definition like below:
public class CustomObject
{
public int Id { get; set; }
public string Name { get; set; }
...
...
...
public int Points { get; set; }
public bool IsPrivate { get; set; }
}
Among the list of objects returned, I could have special CustomObject objects. I need to implement some special rules such as:
If elements with Ids 1 and 3 both exist in the list, only render one of them based on rules
a. If either one of them has IsPrivate flagged to true, display the one that has IsPrivate set to false
b. If neither have IsPrivate set to true, display the one with higher points
... and so on
What would be the best place to implement these rules. I thought about implementing an IEqualityComparer and do a .Distinct() on my service call, doesn't seem like what IEqualityComparer is meant to do.
Suggestions?

Since you cannot modify the class 'CustomObject' I would add the business logic into the 'render' pipeline and just call some function such as below where you have a specific business rule that applies. Then just remove id1 and id3 from your list of objects to render and only render the object that is returned from the following function.
CustomObject BizRule3293(IEnumerable<CustomObject> objects)
{
CustomObject id1 = objects.SingleOrDefault(t => t.Id == 1);
CustomObject id3 = objects.SingleOrDefault(t => t.Id == 3);
if (id1 != null && id3 !=null)
{
if (!id1.IsPrivate && !id3.IsPrivate)
return id1.Points > id3.Points ? id1 : id3;
return id1.IsPrivate ? id3 : id1;
// No logic stated if both are private
}
return id1 ?? id3;
}

Related

How to expose values which depend on another value

I receive a certain category of products from a server. These products all have an ID. The relevant part is something like this:
public class Product
{
public Product(int id)
{
Id = id;
}
public int Id { get; private set; }
}
In the app itself, I am also using an ID of -1 to indicate a default/null option. There are several options like this, e.g. an ID of -2 for "use the same as parent". At first I checked this in the most primitive way.
if(product.Id == -1)
That is not really clean code, so I changed it to a bool property on the product:
public bool IsDefault
{
get { return Id == -1; }
}
Then I noticed that, as the whole class is immutable, this bool can just be an auto-property which can already be assigned on construction.
bool isDefault = id == -1;
Product product = new Product(id, isDefault);
Right at the moment I tried an approach of using an interface and having an own implementation for the default product, like this:
public interface IProduct
{
int Id{get;}
bool IsDefault{get;}
}
public class Product : IProduct
{
public Product(int id)
{
Id = id;
}
public int Id { get; private set; }
public bool IsDefault { get{ return false; } }
}
public class DefaultProduct : IProduct
{
public int Id { get{ return -1; }
public bool IsDefault { get{ return true; } }
}
Now, the last three examples (check in property / constructor parameter / interface implementations) all seem equally "clean" to me and I don't see any advantage one would have over the others.
Is there any good argument (apart from personal opinion) to prefer one over the others?
You don't need the Default version of a product.
You can create constructor without parameters to create default object.
You're overthinking it, and overcomplicating it with the interface.
I would set IsDefault in the constructor based on the passed Id parameter. And maybe even add a default value to that, so that you can instantiate the class without a parameter, and it should be a default product.
public class Product
{
public Product(int id = -1)
{
Id = id;
IsDefault = id == -1;
}
public int Id { get; private set; }
public bool IsDefault { get; private set; }
}
Although this is always easier said than done, the answer came with reevaluating basic assumptions about what certain things actually are and/or represent.
An ID X points to a product with the ID X. That is the purpose of that ID and using it for a different purpose is abusing it. So
if(product.Id == -1)
And
public bool IsDefault
{
get { return Id == -1; }
}
Are both semantically wrong. isDefault is a different part of the configuration of the class.
It being part of the configuration of the class is also the reason to dismiss the last option. A new class should encapsulate new behaviour. In order to achieve the same behaviour, just for a different configuration, the class should be created with just that, a different configuration.
Now, there is the tempting solution of putting IsDefault = id == -1; in the constructor. However, this would violate SRP. The one reason why the Product class should change is if what a product does changes. If tomorrow the ID for a default product changes to -32, there is no change in what the product does.
However, I have a class ProductParser where Products are created. The one reason why that class should change is if how products are parsed from the server to my app changes - and that is exactly the change we would have then.
So, according to clean code, there is only one correct approach here: Determine isDefault wherever it is created and pass it to the constructor of Product, which keeps it as an immutable value.

Distinct List of object in C#

I have to distinct list of object but NOT only by ID because sometimes two different objects have same ID.
I have class:
public class MessageDTO
{
public MessageDTO(MessageDTO a)
{
this.MsgID = a.MsgID;
this.Subject = a.Subject;
this.MessageText = a.MessageText;
this.ViewedDate = a.ViewedDate;
this.CreatedDate = a.CreatedDate;
}
public int? MsgID { get; set; }
public string Subject { get; set; }
public string MessageText { get; set; }
public System.DateTime? ViewedDate { get; set; }
public System.DateTime? CreatedDate { get; set; }
}
How I can distinct list of:
List<MessageDTO> example;
Thanks
Use LINQ.
public class MessageDTOEqualityComparer : EqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO a, MessageDTO b)
{
// your logic, which checks each messages properties for whatever
// grounds you need to deem them "equal." In your case, it sounds like
// this will just be a matter of iterating through each property with an
// if-not-equal-return-false block, then returning true at the end
}
public int GetHashCode(MessageDTO message)
{
// your logic, I'd probably just return the message ID if you can,
// assuming that doesn't overlap too much and that it does
// have to be equal on the two
}
}
Then
return nonDistinct.Distinct(new MessageDTOEqualityComparer());
You can also avoid the need for an extra class by overriding object.Equals(object) and object.GetHashCode() and calling the empty overload of nonDistinct.Distinct(). Make sure you recognize the implications of this decision, though: for instance, those will then become the equality-testing functions in all non-explicit scopes of their use. This might be perfect and exactly what you need, or it could lead to some unexpected consequences. Just make sure you know what you're getting into.
I you want to use other properties, you should implement IEqualityComparer interface. More on: msdn
class MsgComparer : IEqualityComparer<MessageDTO>
{
public bool Equals(MessageDTO x, MessageDTO Oy)
{
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public int GetHashCode(MessageDTO m)
{
//it must br overwritten also
}
}
Then:
example.Distinct(new MsgComparer());
You could also overwrite Equals in MessageDTO class:
class MessageDTO
{
// rest of members
public override bool Equals(object obj)
{
// your stuff. See: http://msdn.microsoft.com/en-us/library/ms173147%28v=vs.80%29.aspx
}
public override int GetHashCode()
{
}
}
Then it's enough:
example.Distinct();
You could use the extension method DistinctBy from the MoreLinq library:
string[] source = { "first", "second", "third", "fourth", "fifth" };
var distinct = source.DistinctBy(word => word.Length);
See here:
I recommend you using solution of #Matthew Haugen
In case you don't want to create a new class for that, there is a way to use LINQ by grouping you list by distinct field(s) then select the first item on this group. For example:
example.(e => new { e.MsgID, e.Subject }).Select(grp => grp.FirstOrDefault());

Enumeration & IComparable

I have an object having the following structure:
public class StockData
{
public string Name { get; set; }
public double Change { get; set; }
public DateTime LastUpdate { get; set; }
public WorkflowStatus Status { get; set; }
}
The Workflow status enum is defined as following:
public enum WorkflowStatus
{
PendingCoverage,
PendingCompliance,
Approved,
Rejected
}
Issue:
I have a grid (wpf) which binds all StockData to it and I have set a grouping on the Status field. I want the groups to be appearing in the grid as it's defined in the order of WorkflowStatus enum. This works absolutely fine and data is grouped in the order as it's defined inside the enum i.e first group is Pendingcoverage and the last is Rejected.
Now I want to remove this enum and introduce an object graph instead of the enum..which means there will be a base class called WorkflowStatus and 4 derived class called PendingCoverage, PendingCompliance, Approved and Rejected. Each derived class will be overiding the ToString property and returning an appropriate string.
Now, this does't work. For some reason it's not able to establish which group should come first and which should come subsequently. Question is how will I implement IComparable in this scenario. Should I implement IComparable (or something else) on StockData or on each individual WorkflowStatus object, and yes then how? Also why does this work in the case of enum and not in the case of an object?
Create your base class and add an abstract Order property to it that all sub classes must implement. Basically an integer which specifies their ordering.
You can also implement IComparable on your abstract class so that if compares objects based on their order property.
public abstract class WorkStatus : IComparable<WorkStatus> {
public abstract int Order { get; }
public int CompareTo(WorkStatus w)
{
if(w.Order < this.Order)
return 1;
if(w.Order > this.Order)
return -1;
return 0;
}
}
For each implementation, give them a different Order value.
public class FirstStatus : WorkStatus {
public override int Order {get { return 1; } }
}
public class SecondStatus : WorkStatus {
public override int Order { get { return 2; } }
}
Assuming your WPF grid is just applying a standard OrderBy query, then if should work as follows.
//LINQPAD SNIPPET
void Main()
{
List<WorkStatus> list = new List<WorkStatus>();
list.Add(new SecondStatus()); //out of order initially.
list.Add(new FirstStatus());
Console.WriteLine(list.OrderBy(x => x));
}
I'm confused as to why IComparable is required here. You have two problems. One is getting a sorted list, the other is getting the appropriate graph:
// Takes a work status and returns the appropriate graph.
static GenericBaseGraphClass GetGraph(WorkStatus input)
{
select(input.Status)
{
// Concrete derived classes go here.
}
}
// Test data.
var someWork = new List<WorkStatus>()
{
new SecondStatus(),
new FirstStatus()
};
// Sort it.
var sortedWork = someWork.Sort((x,y) => x.Status > y.Status);
// Get your object graphs.
var objectGraphs = sortedWork.Select(x => GetGraph(x.Status))

Is there a way of comparing all the values within 2 entities?

I'm using EF4.3 so I'm referring to entities, however it could apply to any class containing properties.
I'm trying to figure out if its possible to compare 2 entities. Each entity has properties that are assigned values for clarity let say the entity is 'Customer'.
public partial class Customer
{
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
...
...
}
The customer visits my website and types in some details 'TypedCustomer'. I check this against the database and if some of the data matches, I return a record from the database 'StoredCustomer'.
So at this point I've identified that its the same customer returning but I wan't to valid the rest of the data. I could check each property one by one, but there are a fair few to check. Is it possible to make this comparison at a higher level which takes into account the current values of each?
if(TypedCustomer == StoredCustomer)
{
.... do something
}
If you're storing these things in the database, it is logical to assume you'd also have a primary key called something like Id.
public partial class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
...
...
}
Then all you do is:
if(TypedCustomer.Id == StoredCustomer.Id)
{
}
UPDATE:
In my project, I have a comparer for these circumstances:
public sealed class POCOComparer<TPOCO> : IEqualityComparer<TPOCO> where TPOCO : class
{
public bool Equals(TPOCO poco1, TPOCO poco2)
{
if (poco1 != null && poco2 != null)
{
bool areSame = true;
foreach(var property in typeof(TPOCO).GetPublicProperties())
{
object v1 = property.GetValue(poco1, null);
object v2 = property.GetValue(poco2, null);
if (!object.Equals(v1, v2))
{
areSame = false;
break;
}
});
return areSame;
}
return poco1 == poco2;
} // eo Equals
public int GetHashCode(TPOCO poco)
{
int hash = 0;
foreach(var property in typeof(TPOCO).GetPublicProperties())
{
object val = property.GetValue(poco, null);
hash += (val == null ? 0 : val.GetHashCode());
});
return hash;
} // eo GetHashCode
} // eo class POCOComparer
Uses an extension method:
public static partial class TypeExtensionMethods
{
public static PropertyInfo[] GetPublicProperties(this Type self)
{
self.ThrowIfDefault("self");
return self.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where((property) => property.GetIndexParameters().Length == 0 && property.CanRead && property.CanWrite).ToArray();
} // eo GetPublicProperties
} // eo class TypeExtensionMethods
Most simple seems to use reflexion : get the properties and/or fields you want to compare, and loop through them to compare your two objects.
This will be done with getType(Customer).getProperties and getType(Customer).getFields, then using getValue on each field/property and comparing.
You might want to add custom informations to your fields/properties to define the ones that needs
comparing. This could be done by defining a AttributeUsageAttribute, that would inherit from FlagsAttribute for instance. You'll then have to retrieve and handle those attributes in your isEqualTo method.
I don't think there's much of a purpose to checking the entire object in this scenario - they'd have to type every property in perfectly exactly as they did before, and a simple "do they match" doesn't really tell you a lot. But assuming that's what you want, I can see a few ways of doing this:
1) Just bite the bullet and compare each field. You can do this by overriding the bool Equals method, or IEquatable<T>.Equals, or just with a custom method.
2) Reflection, looping through the properties - simple if your properties are simple data fields, but more complex if you've got complex types to worry about.
foreach (var prop in typeof(Customer).GetProperties()) {
// needs better property and value validation
bool propertyMatches = prop.GetValue(cust1, null)
.Equals(prop.GetValue(cust2, null));
}
3) Serialization - serialize both objects to XML or JSON, and compare the strings.
// JSON.NET
string s1 = JsonConvert.SerializeObject(cust1);
string s2 = JsonConvert.SerializeObject(cust2);
bool match = s1 == s2;

Changing selected objects inside a query

I have a class that needs a property set inside a LINQ-to-SQL query. My first attempt was to have a "setter" method that would return the object instance and could be used in my select, like this:
public partial class Foo
{
public DateTime RetrievalTime { get; set; }
public Foo SetRetrievalTimeAndReturnSelf ( DateTime value )
{
RetrievalTime = value;
return this;
}
}
....
from foo in DataContext.GetTable<Foo> select foo.SetRetrievalTimeAndReturnSelf();
Unfortunately, such a query throws an exception like this: "System.NotSupportedException: Method 'Foo.SetRetrievalTime(System.DateTime)' has no supported translation to SQL".
Is there any alternative to converting the result to a list and iterating over it? The query is used in a custom "Get" method that wraps the DataContext.GetTable method, so will be used as the base for many other queries. Immediately converting a potentially-large result set to a list would not be optimal.
UPDATE
Here's a better example of what I'm trying to do, updated with Jason's proposed solution:
protected IQueryable<T> Get<T>() where T : class, ISecurable
{
// retrieve all T records and associated security records
var query = from entity in DataContext.GetTable<T> ()
from userEntityAccess in DataContext.GetTable<UserEntityAccess> ()
where userEntityAccess.SysUserId == CurrentUser.Id
&& entity.Id == userEntityAccess.EntityId
&& userEntityAccess.EntityClassName == typeof ( T ).Name
select new { entity, userEntityAccess };
return query.AsEnumerable ()
.Select ( item =>
{
item.entity.CanRead = item.userEntityAccess.CanRead;
item.entity.CanWrite = item.userEntityAccess.CanWrite;
item.entity.CanDelete = item.userEntityAccess.CanDelete;
return item.entity;
} ).AsQueryable ();
}
public interface ISecurable
{
int Id { get; set; }
bool CanRead { get; set; }
bool CanWrite { get; set; }
bool CanDelete { get; set; }
}
UserEntityAccess is a cross-reference table between a user and a business object record (i.e. an entity). Each record contains fields like "CanRead", "CanWrite", and "CanDelete", and determines what a specific user can do with a specific record.
ISecurable is a marker interface that must be implemented by any LINQ-to-SQL domain class that needs to use this secured Get method.
var projection = DataContext.GetTable<Foo>
.AsEnumerable()
.Select(f => f.SetRetrievalTimeAndReturnSelf());
This will then perform the invocation of SetRetrievalTimeAndReturnSelf for each instance of Foo in DataContext.GetTable<Foo> when the IEnumerable<Foo> projection is iterated over.
What do you need to know the time that object was yanked of the database for? That's potentially smelly.

Categories