How can I shorten an if without else statement with lambda? - c#

the following is the code and I do not know how to achieve the following using lambda
public void AddClimber(string name, int age, Skill skill)
{
if (GetClimber(name) == null)
{
Climber toAdd = new Climber(name, age, skill);
this.climbers.Add(toAdd);
if (AddedLowLevelClimer != null)
{
if (skill == Skill.LOW) { AddedLowLevelClimer(toAdd); }
}
else if (AddedMediumLevelClimer != null)
{
if (skill == Skill.LOW) { AddedMediumLevelClimer(toAdd); }
}
else if (AddedHighLevelClimer != null)
{
if (skill == Skill.LOW) { AddedHighLevelClimer(toAdd); }
}
else if (AddedProLevelClimer != null)
{
if (skill == Skill.LOW) { AddedProLevelClimer(toAdd); }
}
}
}

You may try to use a null coalescing operator to choose the first non-null handler:
var target = AddedLowLevelClimer
?? AddedMediumLevelClimer
?? AddedHighLevelClimer
?? AddedProLevelClimer;
if (target != null)
{
if (skill == Skill.LOW)
target(toAdd);
}

Related

Track changes between two objects in disconnected scenario using identifier of an object

I have a .NET worker service which will run periodically.
When the service runs, I get an object from data source with identifier
I change the above retrieved object to required format, and store this object through some service with the same identifier
When my service run next time, I get the object from data source with same identifier (object A)
I will fetch the stored object from some service with same identifier (object B)
I want to know what changed between these two objects A & B, any property change, any list item got added/updated/deleted? These objects will contain nested complex data types.
We have a working solution, using reflection, but we are not sure whether this is correct approach
I have given my existing code to track changes, I am aware this not optimized code, but before proceeding to optimize and refactor it, I want to know any other proven approach is available?
public class TrackerService
{
public T TrackChanges<T>(T newObj, T oldObj) where T : class
{
var clonedObj= ((ResourceBase)(object)oldObj).Clone();
TrackChanges(newObj, oldObj, clonedObj);
return (T)clonedObj;
}
private T TrackChanges<T>(T newObj, T oldObj, T clonedObj) where T : class
{
var oldResourceBase = (ResourceBase)(object)oldObj;
var newResourceBase = (ResourceBase)(object)newObj;
if (oldResourceBase.ResourceId != newResourceBase.ResourceId)
{
throw new Exception($"ResourceId not matching, Type: {newObj.GetType()}, Old Id {oldResourceBase.ResourceId}, New Id {newResourceBase.ResourceId}");
}
var properties = newObj.GetType().GetProperties();
foreach (var property in properties)
{
if (!property.PropertyType.IsTrackableType())
{
throw new Exception($"Property is not trackable type, Type: {property.PropertyType}, Name: {property.Name}");
}
if (property.PropertyType.IsSimpleType())
{
TrackSimplePropertyChanges(property, newObj, oldObj, clonedObj);
}
else if (property.PropertyType.IsCollection())
{
TrackCollectionPropertyChanges(property, newObj, oldObj, clonedObj);
}
else if (property.PropertyType.BaseType == typeof(ResourceBase))
{
TrackComplexPropertyChanges(property, newObj, oldObj, clonedObj);
}
}
return clonedObj;
}
private void TrackSimplePropertyChanges<T>(PropertyInfo property, T newObj, T oldObj, T clonedObj) where T : class
{
var clonedResourceBase = (ResourceBase)(object)clonedObj;
var oldPropertyObj = property.GetValue(oldObj, null);
var newPropertyObj = property.GetValue(newObj, null);
if (oldPropertyObj != null && newPropertyObj != null)
{
if (!oldPropertyObj.Equals(newPropertyObj))
{
property.SetValue(clonedObj, newPropertyObj);
clonedResourceBase.State = ResourceState.Modified;
}
}
else if (oldPropertyObj == null && newPropertyObj != null)
{
property.SetValue(clonedObj, newPropertyObj);
clonedResourceBase.State = ResourceState.Modified;
}
else if (newPropertyObj == null && oldPropertyObj != null)
{
property.SetValue(clonedObj, null);
clonedResourceBase.State = ResourceState.Modified;
}
}
private void TrackComplexPropertyChanges<T>(PropertyInfo property, T newObj, T oldObj, T clonedObj) where T : class
{
var clonedResourceBase = (ResourceBase)(object)clonedObj;
var oldPropertyObj = property.GetValue(oldObj, null);
var newPropertyObj = property.GetValue(newObj, null);
var clonedPropertyObj = property.GetValue(clonedObj, null);
if (oldPropertyObj != null && newPropertyObj != null && clonedPropertyObj != null)
{
TrackChanges(oldPropertyObj, newPropertyObj, clonedPropertyObj);
var clonedPropertyResourceBase = (ResourceBase)(object)clonedPropertyObj;
if (clonedPropertyResourceBase.State != ResourceState.UnModified)
{
clonedResourceBase.State = ResourceState.Modified;
}
}
else if (oldPropertyObj != null && clonedPropertyObj != null && newPropertyObj == null)
{
var clonedPropertyResourceBase = (ResourceBase)(object)clonedPropertyObj;
clonedPropertyResourceBase.State = ResourceState.Deleted;
clonedResourceBase.State = ResourceState.Modified;
}
else if (oldPropertyObj == null && clonedPropertyObj == null && newPropertyObj != null)
{
var newPropertyResourceBase = (ResourceBase)(object)newPropertyObj;
var clonedPropertyResourceBase = (ResourceBase)newPropertyResourceBase.Clone();
clonedPropertyResourceBase.State = ResourceState.Added;
property.SetValue(clonedObj, clonedPropertyResourceBase);
clonedResourceBase.State = ResourceState.Modified;
}
}
private void TrackCollectionPropertyChanges<T>(PropertyInfo property, T newObj, T oldObj, T clonedObj) where T : class
{
var clonedResourceBase = (ResourceBase)(object)clonedObj;
if (oldObj != null && newObj != null)
{
if (property.PropertyType.GetGenericICollectionsArgument().IsSimpleType())
{
TrackSimpleCollectionChanges(property, newObj, oldObj, clonedObj, clonedResourceBase);
}
else
{
TrackComplexCollectionChanges(property, newObj, oldObj, clonedObj, clonedResourceBase);
}
}
}
private void TrackSimpleCollectionChanges<T>(PropertyInfo property, T newObj, T oldObj, T clonedObj, ResourceBase clonedResourceBase) where T : class
{
var oldPropertyObj = (IEnumerable<ResourceBase>)property.GetValue(oldObj, null)!;
var newPropertyObj = (IEnumerable<ResourceBase>)property.GetValue(newObj, null)!;
var clonedPropertyCollection = property.GetValue(clonedObj, null)!;
var castedOldPropertyObj = oldPropertyObj.ToList();
var castedNewPropertyObj = newPropertyObj.ToList();
var tobeAdded = castedNewPropertyObj.Except(castedOldPropertyObj);
var tobeRemoved = castedOldPropertyObj.Except(castedNewPropertyObj);
foreach (var item in tobeAdded)
{
clonedPropertyCollection.GetType().GetMethod("Add")!.Invoke(clonedPropertyCollection, new[] { item });
}
foreach (var item in tobeRemoved)
{
clonedPropertyCollection.GetType().GetMethod("Remove")!.Invoke(clonedPropertyCollection, new[] { item });
}
if (tobeAdded.Count() > 0 || tobeRemoved.Count() > 0)
{
clonedResourceBase.State = ResourceState.Modified;
}
}
private void TrackComplexCollectionChanges<T>(PropertyInfo property, T newObj, T oldObj, T clonedObj, ResourceBase clonedResourceBase) where T : class
{
var oldPropertyObj = (IEnumerable<ResourceBase>)property.GetValue(oldObj, null)!;
var newPropertyObj = (IEnumerable<ResourceBase>)property.GetValue(newObj, null)!;
var clonedPropertyObj = (IEnumerable<ResourceBase>)property.GetValue(clonedObj, null)!;
var clonedPropertyCollection = property.GetValue(clonedObj, null)!;
var castedOldPropertyObj = oldPropertyObj.ToList();
var castedNewPropertyObj = newPropertyObj.ToList();
var castedClonedPropertyObj = clonedPropertyObj.ToList();
var tobeAdded = castedNewPropertyObj.Except(castedOldPropertyObj, new ResourceComparer());
var tobeRemoved = castedOldPropertyObj.Except(castedNewPropertyObj, new ResourceComparer());
foreach (ResourceBase item in tobeAdded)
{
var clonedItem = (ResourceBase)item.Clone();
clonedItem.State = ResourceState.Added;
clonedPropertyCollection.GetType().GetMethod("Add")!.Invoke(clonedPropertyCollection, new[] { clonedItem });
}
foreach (ResourceBase item in tobeRemoved)
{
ResourceBase clonedItem = castedClonedPropertyObj.First(r => r.ResourceId == item.ResourceId);
clonedItem.State = ResourceState.Deleted;
}
var matchingItem = castedNewPropertyObj.Intersect(castedOldPropertyObj, new ResourceComparer());
foreach (ResourceBase item in matchingItem)
{
var newCollectionObj = castedNewPropertyObj.First(r => r.ResourceId == item.ResourceId);
var oldCollectionObj = castedOldPropertyObj.First(r => r.ResourceId == item.ResourceId);
var clonedCollectionObj = castedClonedPropertyObj.First(r => r.ResourceId == item.ResourceId);
clonedCollectionObj = TrackChanges(newCollectionObj, oldCollectionObj, clonedCollectionObj);
if (clonedCollectionObj.State != ResourceState.UnModified)
{
clonedResourceBase.State = ResourceState.Modified;
}
}
}
}

Ignoring null parameter in where clause linq to sql

I need to ignore null parameters in the where clause so that i can fetch the appropriate data with the applied filters from the user side. Inorder to acheive this, I am currently using the if..else nested approach which grows in size as the number of parameters grow. I would like to know if there is any other effecient way of handling this scenario by avoiding the number of lines and complexity and improving readablility
public List<Members> GetMembers(int currentPosition, string memberStatus,
string package, string packageStatus, string branch)
{
var members = new List<Members>();
if (package != null)
{
//include package
if (packageStatus != null)
{
//include package, packageStatus
if (branch != null)
{
//include package,packageStatus,branch
members = db.Members.Where(x => x.PackageName == package && x.PackageStatus == packageStatus && x.Branch == branch).ToList();
}
else
{
//include package,packageStatus
members = db.Members.Where(x => x.PackageName == package && x.PackageStatus == packageStatus).ToList();
}
}
else
{
if (branch != null)
{
//include package,branch
members = db.Members.Where(x => x.PackageName == package && x.Branch == branch).ToList();
}
else
{
//include package
members = db.Members.Where(x => x.PackageName == package).ToList();
}
}
}
else
{
if (packageStatus != null)
{
//include packageStatus
if (branch != null)
{
//include packageStatus,branch
members = db.Members.Where(x => x.PackageStatus == packageStatus && x.Branch == branch).ToList();
}
else
{
//include packageStatus
members = db.Members.Where(x => x.PackageStatus == packageStatus).ToList();
}
}
else
{
if (branch != null)
{
//include packageStatus,branch
members = db.Members.Where(x => x.PackageStatus == packageStatus && x.Branch == branch).ToList();
}
else
{
//include nothing
members = db.Members.ToList();
}
}
}
return members;
}
You can add those conditions to the query. It won't make for the most readable SQL, but assuming you find readable code more important and trust SQL Server's optimizer:
members = db.Members.Where(x =>
(package == null || x.PackageName == package) &&
(packageStatus == null || x.PackageStatus == packageStatus) &&
(branch == null || x.Branch == branch)
).ToList();
Alternatively, you could conditionally append Where()s to a variable of type IQueryable<Member>. See for example entity framework: conditional filter.
I would like to suggest a readable version of the method:
public List<Members> GetMembers(int currentPosition, string memberStatus,
string package, string packageStatus, string branch)
{
var members = new List<Members>();
members = db.Members.ToList();
if (package != null)
{
members = members.Where(x => x.PackageName == package);
}
if (packageStatus != null)
{
members = members.Where(x => x.PackageStatus == packageStatus);
}
if (branch != null)
{
members = members.Where(x => x.Branch == branch);
}
return members.ToList();
}
I think this works (haven't tried compiling it but it makes sense in my head)
public List<Members> GetMembers(int currentPosition, string memberStatus,
string package, string packageStatus, string branch)
{
List<Members> ret = db.Members()
if(memberStatus != null || package != null || packageStatus != null || branch != null)
{
ret = db.Members.Where(x =>
(memberStatus == null) ? (true) : (memberStatus == x.MemberStatus)
&& (package == null) ? (true) : (package == x.PackageName)
&& (packageStatus == null) ? (true) : (packageStatus == x.PackageStatus)
&& (branch == null) ? (true) : (branch == x.Branch)
).ToList();
}
return ret;
}

Make 'Code Jumper' extension (its source-code) work with Vb.Net Modules

SCENARIO
Code Jumper is a time-saver extension/plugin for Visual Studio that adds a kind of navigation panel to easily jump between the members of your code with a single click.
For big APIs it's really useful because it avoids scrolling thousands of lines, opening #regions, hidding Xml documentation, searching for a method or member name, etc... its a must-have extension, because It really saves a lot of time:
PROBLEM
Code Jumper has one big problem for my needs, it can't parse Vb.Net Modules, no matter what says in the product description, because the reality is that it can't (its written by a C# guy so maybe he didn't knew the existance of Modules...)
When opening a module, the window of Code jumper is empty, any member is shown.
QUESTION
Here is the downloadable source-code of Code Jumper, written in C#.
What modifications I need to do to make it work with Vb.Net Modules?.
OPTIONAL QUESTION
Someone knows a similar and better extension than Code Jumper?.
RESEARCH
I think this is the most relevant code, in DocumentParser.cs file:
public void RecurseCodeElements(CodeElements elements, CodeElementAggregate aggregate) {
if (elements == null)
return;
_recurseCount++;
if (_recurseCount % 2 == 0) {
if (ParserProgress != null)
_appConfig.elementView.Dispatcher.BeginInvoke((Action)(() => ParserProgress(this)));
}
foreach (CodeElement element in elements) {
System.Threading.Thread.Sleep(10);
if (element.Kind == vsCMElement.vsCMElementClass) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementInterface) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementStruct) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementNamespace) {
RecurseCodeElements(GetMembers(element), aggregate);
}
if (element.Kind == vsCMElement.vsCMElementEnum) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementFunction ||
element.Kind == vsCMElement.vsCMElementProperty ||
element.Kind == vsCMElement.vsCMElementVariable ||
element.Kind == vsCMElement.vsCMElementDelegate ||
element.Kind == vsCMElement.vsCMElementEvent)
{
var func = element as CodeFunction;
aggregate.Add(element);
}
}
}
private CodeElements GetMembers(CodeElement element) {
if (element == null)
return null;
if (element.Kind == vsCMElement.vsCMElementNamespace) {
return (element as CodeNamespace).Members;
}
if (element.Kind == vsCMElement.vsCMElementClass) {
return (element as CodeClass).Members;
}
if (element.Kind == vsCMElement.vsCMElementInterface) {
return (element as CodeInterface).Members;
}
if (element.Kind == vsCMElement.vsCMElementEnum) {
return (element as CodeEnum).Members;
}
if (element.Kind == vsCMElement.vsCMElementStruct) {
return (element as CodeStruct).Members;
}
return null;
}
I tried to add an additional condition for vsCMElement.vsCMElementModule, however, it still don't work, and I 'm not sure whether I'm on the right direction:
public void RecurseCodeElements(CodeElements elements, CodeElementAggregate aggregate) {
if (elements == null)
return;
_recurseCount++;
if (_recurseCount % 2 == 0) {
if (ParserProgress != null)
_appConfig.elementView.Dispatcher.BeginInvoke((Action)(() => ParserProgress(this)));
}
foreach (CodeElement element in elements) {
System.Threading.Thread.Sleep(10);
if (element.Kind == vsCMElement.vsCMElementClass) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementModule) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementInterface) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementStruct) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementNamespace) {
RecurseCodeElements(GetMembers(element), aggregate);
}
if (element.Kind == vsCMElement.vsCMElementEnum) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
if (element.Kind == vsCMElement.vsCMElementFunction ||
element.Kind == vsCMElement.vsCMElementProperty ||
element.Kind == vsCMElement.vsCMElementVariable ||
element.Kind == vsCMElement.vsCMElementDelegate ||
element.Kind == vsCMElement.vsCMElementEvent)
{
var func = element as CodeFunction;
aggregate.Add(element);
}
}
}
private CodeElements GetMembers(CodeElement element) {
if (element == null)
return null;
if (element.Kind == vsCMElement.vsCMElementNamespace) {
return (element as CodeNamespace).Members;
}
if (element.Kind == vsCMElement.vsCMElementModule) {
return (element as CodeClass).Members;
}
if (element.Kind == vsCMElement.vsCMElementClass) {
return (element as CodeClass).Members;
}
if (element.Kind == vsCMElement.vsCMElementInterface) {
return (element as CodeInterface).Members;
}
if (element.Kind == vsCMElement.vsCMElementEnum) {
return (element as CodeEnum).Members;
}
if (element.Kind == vsCMElement.vsCMElementStruct) {
return (element as CodeStruct).Members;
}
return null;
}
I can't get it to compile, but if you look at all references for CodeElementType.Class, almost all of them need to be "duplicated" with CodeElementType.Module, especially in IsStackableElement, probably answering your question.
Partial code to explain the required modifications:
public void RecurseCodeElements(CodeElements elements, CodeElementAggregate aggregate) {
// ...
foreach (CodeElement element in elements) {
// ...
if (element.Kind == vsCMElement.vsCMElementModule) {
RecurseCodeElements(GetMembers(element), aggregate.Add(element));
}
// ...
}
}
private CodeElements GetMembers(CodeElement element) {
// ...
if (element.Kind == vsCMElement.vsCMElementModule) {
return (element as CodeClass).Members;
}
// ...
}
public bool IsStackableElement {
get
{
if (IsRoot)
// ...
return (ElementType == CodeElementType.Class || ElementType == CodeElementType.Module || ... )
// ...
}
}
public static CodeElementType GetElementType(CodeElement element) {
switch (element.Kind) {
// ...
case vsCMElement.vsCMElementModule:
return CodeElementType.Module;
// ...
}
}
public void Draw() {
// ...
if (ElementType == CodeElementType.Module) {
_elementStackPanel.Background = _appConfig.ClassBackgroundBrush;
}
// ...
}
public void Update() {
// ...
if (ElementType == CodeElementType.Module)
{
ElementLabel.AddExpander(_elementStackPanel);
ElementLabel.AddPictureToSingleGrid(ElementInfo.ElementImage);
ElementLabel.AddPictureToSingleGrid(ElementInfo.Accessibility.AccessibilityImage);
ElementLabel.AddText(ElementInfo.Name, _appConfig.ClassTextBrush, _appConfig.HyperlinkTextImageSpacer, FontWeights.Bold);
}
// ...
}
private bool GetElementInfo() {
// ...
if (ElementInfo.ElementType == CodeElementType.Module) {
ElementInfo.ElementImage = _appConfig.GetImage("staticclass");
}
// ...
}
private void ParseCodeElement() {
// ...
if (ElementInfo.ElementType == CodeElementType.Module)
{
ElementInfo.IsStackable = true;
var codeClass = (CodeClass)Original;
var codeClass2 = (CodeClass2)Original;
ElementInfo.Accessibility = new AccessibilityContainer(_appConfig, codeClass.Access);
ElementInfo.IsStatic = true;
}
// ...
}

Any way to make this more generic?

I have these two functions, used to search for a folder within a hierarchy:
public Folder<T> Find (string Path)
{
Folder<T> Result = null;
if (this.Path != Path &&
ChildrenDict != null)
{
foreach (KeyValuePair<long, Folder<T>> Child in ChildrenDict)
{
Result = Child.Value.Find (Path);
}
}
else
{
Result = this;
}
return Result;
}
public Folder<T> Find (long ID)
{
Folder<T> Result = null;
if (this.ID != ID &&
ChildrenDict != null)
{
foreach (KeyValuePair<long, Folder<T>> Child in ChildrenDict)
{
Result = Child.Value.Find (ID);
}
}
else
{
Result = this;
}
return Result;
}
As you can see, they are very similar to one another. How can I re-structure them so I don't have essentially the same code several times, one per each property I might want to use to find them?
Create a method with a condition parameter which does the logic:
protected Folder<T> Find(Func<Folder<T>, bool> condition) {
Folder<T> Result = null;
if(!condition(this) && ChildrenDict != null) {
foreach(var Child in ChildrenDict) {
Result = Child.Value.Find(condition);
}
} else {
Result = this;
}
return Result;
}
Rewrite your public Find methods as:
public Folder<T> Find(string path) {
return Find(f => f.Path == path);
}

Lambda expression alternative to several if statements

I'm trying to accomplish an expression function alternative
private static Expression<Func<UserProfile, bool>> CompareFilter(FilterViewModel f)
{
...
}
on this one:
private static bool CompareFilter(UserProfile profile, FilterViewModel filter)
{
if (filter.FirstName != null)
{
if (profile.FirstName != null)
{
if (profile.FirstName.CompareTo(filter.FirstName) != 0)
{
return false;
}
}
else
{
return false;
}
}
if (filter.TownId != null)
{
if (profile.TownId != filter.TownId)
{
return false;
}
}
// true if at least one of the filter interests match
if (filter.InterestsIds != null)
{
var firstInterestFound = profile.Interests
.Where(i => filter.InterestsIds.Contains(i.Id))
.FirstOrDefault();
if (firstInterestFound == null)
{
return false;
}
}
...
return true;
}
Is there a way to fit this many nested if statements to a lambda expression or something else that will work for the expression function? The idea is the verification to go trough all the if statements and to return true only if all of them are not returning false.
Thanks in advance!
First of all, your current method is so long because you are not making good use of boolean logic. It can be simplified to this:
private static bool CompareFilter(UserProfile profile, FilterViewModel filter)
{
if (filter.FirstName != null && filter.FirstName != profile.FirstName)
{
return false;
}
if (filter.TownId != null && filter.TownId != profile.TownId)
{
return false;
}
// true if at least one of the filter interests match
if (filter.InterestsIds != null &&
!profile.Interests.Any(i => filter.InterestsIds.Contains(i.Id)))
{
return false;
}
...
return true;
}
You can turn this into a big hulking expression by inverting all the logic1:
private static bool CompareFilter(UserProfile profile, FilterViewModel filter)
{
return (filter.FirstName == null || filter.FirstName == profile.FirstName) &&
(filter.TownId == null || filter.TownId == profile.TownId) &&
(filter.InterestsIds == null ||
profile.Interests.Any(i => filter.Interests.Contains(i.Id)));
// etc. etc.
}
And once you have this, it's a piece of cake to turn it into a lambda and get your Expression<T>:
private static Expression<Func<UserProfile, bool>> CompareFilter(FilterViewModel f)
{
return profile =>
(filter.FirstName == null || filter.FirstName == profile.FirstName) &&
(filter.TownId == null || filter.TownId == profile.TownId) &&
(filter.InterestsIds == null ||
profile.Interests.Any(i => filter.Interests.Contains(i.Id)));
}
Technically, you can just || together all the conditions from the first method and put a big !( ) around it: return !( (...) || (...) || (...));, but inverting everything and joining the conditions with && is a lot nicer.

Categories

Resources