I get SonarCloud error in this code:
foreach (var item in itemList)
{
if (string.IsNullOrEmpty(item.Name))
{
throw new BadRequestException("Item name is null or missing...");
}
if (someOtherList.Any(x => x.Name == item.Name))
{
throw new NotAcceptedException("Item name already exist in Db.");
}
}
My question is how to translate this code into LINQ using Select (as sonar suggest) when I have exceptions?
I found the solution in order for sonar to be happy. :) But honestly it makes sense.
So, sometimes you can get the this error when you want to iterate through whole object but you just need one property, like in this situation. Here I want to iterate only through the names, so the solution will be:
foreach (var itemName in itemList.Select(x => x.Name))
{
if (string.IsNullOrEmpty(itemName))
{
throw new BadRequestException("Item name is null or missing...");
}
if (someOtherList.Any(x => x.Name == itemName))
{
throw new NotAcceptedException("Item name already exist in Db.");
}
}
Related
Some people decided to close my previous question, but the question they linked (What is a NullReferenceException, and how do I fix it?) did not have an answer. This question is fundamentally different since the enumerable is populated. It is not null. Just as the first answer stated, I placed "strategic breakpoints" and checked the variables.
I'm debugging a XUnit test and it turns out that in my business logic the iteration variable in the foreach loop is throws an exception "Object Reference not set to instance of object". However, the list over which the iteration is happening is NOT null. I can see that when I'm debugging. Here is the code:
Business logic:
List<string> regionArray = new List<string>();
if (someCondition)
{
regionArray = _utils.GetRegions(someParam); // this is not returning null
}
foreach (var region in regionArray)
{
var query = from dataSet in myDataSets
where dataSet.Location == region
select dataSet;
var queryResult = query.FirstOrDefault();
if (queryResult == null)
{
// do stuff
} else if (queryResult.State != States.Provisioned)
{
// do stuff
}
}
Here is how I am mocking the _utils.GetRegions call, but I dont think thats the problem.
private Mock<IUtils> _mockRegionUtils;
[Fact]
public void ItWorks()
{
// do stuff
_mockRegionUtils = new Mock<IUtils>();
_mockRegionUtils.Setup(utils => utils.GetRegions(It.IsAny<ISomeParam>())).Returns(new List<string>() {"america", "china"});
// call business logic
}
I have checked all the types in the debugger. regionArray.GetType() returns {System.Collections.Generic.List`1[System.String]}. when I type region into the console however, i get:
region
'region' threw an exception of type 'System.NullReferenceException'
how is this possible?
EDIT: fixed a typo above, sorry about that. Something weird though, so if I reassign the value of regionArray to be an inline list, it still fails. But if I define a new inline list and iterate over that, the looping works fine.
List<string> regionArray = new List<string>();
if (someCondition)
{
regionArray = _utils.GetRegions(someParam); // this is not returning null
}
regionArray = new List<string>() {"china", "america"};
List<string> temp = new List<string>() {"foo", "bar"}
foreach (var region in regionArray)
{
// region still throws null reference exception
foreach (var tempVar in temp)
{
var c = tempVar; // this works. tempvar is never null.
}
var query = from dataSet in myDataSets
where dataSet.Location == region
select dataSet;
var queryResult = query.FirstOrDefault();
if (queryResult == null)
{
// do stuff
} else if (queryResult.State != States.Provisioned)
{
// do stuff
}
}
EDIT 2: So I tried iterating over the regionArray in the same way just before the logic above, and it worked fine.
List<string> regionArray = new List<string>();
if (someCondition)
{
regionArray = _utils.GetRegions(someParam); // this is not returning null
}
foreach (var region in regionArray)
{
var c = region; // this works
}
foreach (var region in regionArray)
{
// region throws null reference exception
var query = from dataSet in myDataSets
where dataSet.Location == region
select dataSet;
var queryResult = query.FirstOrDefault();
if (queryResult == null)
{
// do stuff
} else if (queryResult.State != States.Provisioned)
{
// do stuff
}
}
so most likely, it is not a problem with the moq object. based on #Iliar's suggestion, I will see if regionArray gets modified, but at first glance since regionArray is not used within the loop, my answer would be "no".
Update: I got around this issue by renaming the region looping variable to a different name. As it turns out, I was doing another foreach (var region ...) loop earlier in my code. I spoke to some senior colleagues as to why these 2 names would conflict with each other, and they said maybe it was some issue with symbols in VSCode and not really with my actual code. Thank you all for your help!
There was a lot of info in this thread, so just to summarize here are a few bulletpoints in case it is helpful to someone else in the future:
When debugging an XUnit test, I was seeing my looping variable in my foreach displaying the following info in the tooltip 'region' threw an exception of type 'System.NullReferenceException' Data [IDictionary]:{System.Collections.ListDictionaryInternal} HResult [int]:-2147467261 HelpLink [string]:null InnerException [Exception]:null Message [string]:"Object reference not set to an instance of an object." Source [string]:"9dd66c33104045bba27ad3fc9fb95185" StackTrace [string]:" at <>x.<>m0(<IngestEvents>d__13 <>4__this)" TargetSite [MethodBase]:{System.String <>m0(<IngestEvents>d__13)} Static members ....
even as I stepped INTO the loop, the tooltip for region was still showing the above, and when I typed region into the console, I got 'region' threw an exception of type 'System.NullReferenceException'.
The above 2 points led me to believe region was null. However, through #IVSoftware 's help, I verified that region was not actually null, because the assertion was passing.
I then looked at the rest of my code, and as a random guess, I tried renaming the looping variable region to something else. When I did, region was correctly set to the elements of the list.
Hi I really hope to be helpful. First I will answer your question "how is this possible?" and I think I can explain why your edited question with the inline list works. True, the GetRegions method returns a list that is not null. Sure, if you call GetType() on this it correctly identifies it as a "list of strings". I believe however, that the GetRegions method is returning a list that contains at least one null value. And you prove it out yourself when you added the edit, because you say this works:
regionArray = new List<string>() {"china", "america"};
But try making a list with three values like this where one of them is null and probably the loop will fail again.
regionArray = new List<string>() {"china", null, "america"};
This suggests a bug inside the GetRegions method is putting a null value into the list that it is returning. Adding these two lines at the beginning of your loop might go a long way to identifying the issue:
foreach (var region in regionArray)
{
// Look for this in the Visual Studio 'Output' window
System.Diagnostics.Debug.WriteLine(region == null ? "Null" : region);
// You believe that region is not null. So 'assert' that
// this evaluates to True and if it doesn't the debugger will break here.
System.Diagnostics.Debug.Assert(region != null, "Break on this line if region is null");
...
From what I can tell, I would look inside your GetRegions(someParam) method and see if it's inserting a null into the list somewhere. Good luck!
List<string> regionArray = new List<string>();
if (someCondition)
{
regionArray = _utils.GetRegions(someParam); // this is not returning null
}
this will override the regionArray instance, to the GetRegions instance, so creating new List<string> instance is useless.
What you want is :
List<string> regionArray = new List<string>();
if (someCondition)
{
regionArray.AddRange(_utils.GetRegions(someParam));
}
which is using this new instance, and add the resulted elements inside it.
If _utils.GetRegions(someParam) returns empty set, then regionArray will not be null, but it'll be empty regionArray.Count == 0.
this is also can be done using ToList as well:
var regionArray = _utils.GetRegions(someParam).ToList();
now , you need to check regionArray after that :
if(regionArray.Count == 0)
{
// do something
}
Or using Linq
if(!regionArray.Any())
{
// do something
}
if the collection is not empty then you can iterate through the list and validate each string inside this list before you process it:
foreach (var region in regionArray)
{
// check if the element is null or empty
// if true, will skip this element and go to the next one
if(string.IsNullOrEmpty(region)) { continue; } // go to the next iteration
// get the results
var queryResult = myDataSets.FirstOrDefault(x=> x.Location == region);
if (queryResult == null)
{
// do stuff
}
else if (queryResult.State != States.Provisioned)
{
// do stuff
}
}
I've got a method that tries to Add n files to a repository, using SharpSVN. Any number of these files can throw an error, which I catch, and then move on to the next file and try to add that, and so on. I want to alert the user X times, where X is the number of different reasons. So if I added 5 files and 3 failed for one reason and 2 failed for a different reason, I want to present 2 errors. If they all fail for the same reason, 1 error. Five different reasons? Present 5 errors.
I made a class, FileException, that has two properties (Exception Ex, string FileName) and tried to implement a collection so I could group it on the Exception.
public void AddFiles(List<string> files)
{
var sb = new StringBuilder();
var args = new SvnAddArgs {Depth = SvnDepth.Children};
var exes = new Collection<FileException>();
foreach (var file in files)
{
try
{
//only here for testing purposes described below
if (file.Contains("png"))
throw new AccessViolationException();
SVNClient.Add(file, args);
}
catch (Exception ex)
{
exes.Add(new FileException(ex, file));
}
}
if (exes.Count > 1)
{
exes.GroupBy(s => s.Ex.GetType());
Unique<Log>.Instance.AddExceptions(exes);
}
else if (exes.Count == 1)
Unique<Log>.Instance.AddException(exes[0].Ex);
}
public void AddExceptions(Collection<FileException> e)
{
var sb = new StringBuilder();
var ex = e[0].Ex;
for(var i=0; i < e.Count;i++)
{
Logs.Add(new LogMessage(e[i].Ex));
sb.AppendLine(e[i].FileName);
WriteLogFile(new LogMessage(e[i].FileName, e[i].Ex));
if (ex.GetType() == e[i].Ex.GetType())
continue;
ShowLogError(new LogMessage(sb.ToString(), ex));
sb.Length = 0;
ex = e[i].Ex;
}
//Call ShowLogError if only 1 type of Exception in all of e
if (!string.IsNullOrEmpty(sb.ToString()))
ShowLogError(new LogMessage(sb.ToString(), ex));
}
public void ShowLogError(ILogMessage log)
{
//Extra formatting left out as its irrelevant to code sample
XtraMessageBox.Show(log.message, log.title);
}
So, what AddException is trying to do is store the first exception in ex in a sort of flag, and then iterate over its parameter and message the user when the current item is different than the flag item.
Test Case:
Add 3 files (fileA.cs, fileB.png, fileC.cs)
Expected Results:
ShowLogError() shows me fileA and fileC, since these threw the same error
ShowLogError() shows me fileB, which threw the hard-coded exception since it contains "png"
Actual Results:
ShowLogError() shows fileA and B, which threw 2 different exceptions
ShowLogError() shows fileC, which threw the same as fileA.
So I think there's a problem with how I'm using GroupBy() in the AddFiles method, but I'm totally clueless here. Oh, also, I get this note from Resharper on my GroupBy() statement: Return value of pure method is not used but I'm not really sure what is meant by "pure method".
Edit: I just tried using OrderBy instead of GroupBy, but I get the same results for the aforementioned test case.
Edit 2: Using my OrderBy instead of GroupBy, here is a screenshot from my Immediate Window:
![enter image description here][1]
Added highlighting just to make it easier to differentiate the three rows.
Should items 0 and 2 be together since their Ex is of the same type?
Edit Adding a screenshot of Gert Arnold's answer to show that it does not compile. I proposed an edit that does compile and it was removed. I appreciate his help, of course, but it wasn't 100% working.
When you've got your exes collection, basically all you have to do is:
var result = fileExceptions.GroupBy(e => e.Ex.GetType().Name)
.Select(g => new
{
g.Key, Files = string.Join("\r\n", g.Select(x => x.FileName).ToArray())
});
I believe that you are not capturing the results of of your "GroupBy" or "OrderBy" clauses.
What about this:
List<Type> types = exes.Select(x => x.GetType()).Distinct().ToList();
This gives you the unique types of FileExceptions that were added to the collection
I can get a list from the solution of all calls to a particuliar method using the following code:
var createCommandList = new List<MethodSymbol>();
INamedTypeSymbol interfaceSymbol =
(from p
in solution.Projects
select p.GetCompilation().GetTypeByMetadataName(
"BuySeasons.BsiServices.DataResource.IBsiDataConnection")
).FirstOrDefault();
foreach (ISymbol symbol in interfaceSymbol.GetMembers("CreateCommand"))
{
if (symbol.Kind == CommonSymbolKind.Method
&& symbol is MethodSymbol)
{
createCommandList.Add(symbol as MethodSymbol);
}
}
foreach (MethodSymbol methodSymbol in createCommandList)
{
foreach (ReferencedSymbol referenceSymbol
in methodSymbol.FindReferences(solution))
{
foreach (ReferenceLocation referenceLocation
in from l
in referenceSymbol.Locations
orderby l.Document.FilePath
select l)
{
if (referenceLocation.Location.GetLineSpan(false)
.StartLinePosition.Line ==
referenceLocation.Location.GetLineSpan(false)
.EndLinePosition.Line)
{
Debug.WriteLine("{0} {1} at {2} {3}/{4} - {5}",
methodSymbol.Name,
"(" + String.Join(",",
(from p
in methodSymbol.Parameters
select p.Type.Name + " " + p.Name).ToArray()
) + ")",
Path.GetFileName(referenceLocation.Location.GetLineSpan(false)
.Path),
referenceLocation.Location.GetLineSpan(false)
.StartLinePosition.Line,
referenceLocation.Location.GetLineSpan(false)
.StartLinePosition.Character,
referenceLocation.Location.GetLineSpan(false)
.EndLinePosition.Character));
}
else
{
throw new ApplicationException("Call spans multiple lines");
}
}
}
}
But this gives me a list of ReferencedSymbol. Although this gives me the file and line number that the method is called from I would also like to get the specific arguments that the method is called with. How can I either convert what I have or get the same information with Roslyn? (notice the I first load the solution with the Solution.Load method and then loop through to find out where the method is defined/declared (createCommandList)).
You are already using Roslyn here. When you have a referenceSymbol, you can get at the Method Declaration Syntax and then look down into the tree to get the Parameter list.
I've inserted a arguments variable that uses your referenceSymbol:
// Snip start
foreach (MethodSymbol methodSymbol in createCommandList)
{
foreach (ReferencedSymbol referenceSymbol
in methodSymbol.FindReferences(solution))
{
var arguments = referenceSymbol.Definition.DeclaringSyntaxNodes.First()
.DescendantNodes().OfType<ParameterSyntax>().ToList();
foreach (ReferenceLocation referenceLocation in
from l
in referenceSymbol.Locations
orderby l.Document.FilePath
select l)
{
// Snip end
When you perform a Debug output, you can then use that list of arguments to get the names.
My solution requires getting the First() of the DeclaringSyntaxNodes, which I don't like very much but cannot find another way to get at the Descendant Nodes of the Syntax Tree.
I have discovered another way of getting the Parameter list from a method that might work for others as well.
Say I have the following method that has two parameters:
public string DebugPage(string enabled, string show){
//do stuff
}
Then you get your nodes however you wish. For example this gives me a list of public methods:
IEnumerable<MethodDeclarationSyntax> methods = from m in root.DescendantNodes().OfType<MethodDeclarationSyntax>() where m.Modifiers.ToString().Contains("public") select m;
Then I can iterate through that list of methods to get at the method's ParameterList property which is exposed to make this operation really easy. By the end of this loop the list parameters will hold the names of each parameter in the method (in the example of the Debug method above it will hold the values enabled and show as expected):
var parameters = new List<string>();
foreach (var method in methods)
{
foreach (var n in method.ParameterList.Parameters)
{
var parameterName = n.Identifier.Text;
parameters.Add(parameterName);
}
}
You can search the syntax tree of the reference at the specific source location it occurs to find the node you are looking for. You'll need to use a call like DescendentNodes from the tree's root node and you'll probably need to request the language specific node type you are looking for. Once you have the node in the referecing tree you can then use that tree's semantic model to tell you other information about the arguments.
This may be the wrong way to do things(!) but i am just wondering ....
I am using
//check that all transform fields have corresponding database columns
foreach (string sDatabaseFieldName in l_sDatabaseFieldNames)
{
bool bFound = false;
foreach (SqlParameter sqlp in sqlcmdAll.Parameters)
{
if (sqlp.SourceColumn == sDatabaseFieldName) bFound = true;
}
if (!bFound)
{
throw new Exception(string.Format("Transform field {0} does not have a corresponding column in the target table.", sDatabaseFieldName));
}
}
where l_sDatabaseFieldNames is a List< string> and sqlcmdAll is an insert SqlCommand with validated parameter names.
I want to throw an exception if an item in l_sDatabaseFieldNames is not in any sqlcmdAll.Parameters.SourceColumn. In other words all column names contained in l_sDatabaseFieldNames should also have a matching parameter (compared using SourceColumn property).
I can also use
bool bFound = l_sDatabaseFieldNames.All(sDBFN => sqlcmdAll.Parameters.Cast<SqlParameter>().Any(param => sDBFN == param.SourceColumn));
but i only get a true/false result.
Can i use a combination of the two techniques and throw an exception from within the linq query if an item is in l_sDatabaseFieldNames, but not in any sqlcmdAll.Parameters.SourceColumn?
Thank you in advance,
James.
What exception would you expect it to throw? I feel this is better for you to take the result and throw the exception you want to throw:
if (!l_sDatabaseFieldNames.All(sDBFN => sqlcmdAll.Parameters.Cast<SqlParameter>().Any(param => sDBFN == param.SourceColumn)))
{
throw new YourCustomException("Your custom message");
}
Of course, if this is for debugging purposes only to verify a condition in testing and you don't need it to go into the actual release code you can use an assertion:
Debug.Assert(l_sDatabaseFieldNames.All(sDBFN => sqlcmdAll.Parameters.Cast<SqlParameter>().Any(param => sDBFN == param.SourceColumn)));
* UPDATE *
Based on your comment, you have a few choices, basically. We could bastardize a select clause but I don't like this as it feels kinda weird throwing in a projection. Sadly Linq doesn't already have a ForEach() that lets you perform an Action<T>, so you can either write your own ForEach() for IEnumerable<T>, or you can use ToList() to convert the sequence to a List<T> which does have a ForEach():
sqlcmdAll.Parameters.Cast<SqlParameter>().ToList().ForEach(p =>
{
if (!l_sDatabaseFieldNames.Contains(p.SourceColumn))
{
throw new Exception("Could not find " + p.SourceColumn);
}
});
If you don't mind writing your own extension method, you can add this to your library (comes in very handy) to give ForEach() to IEnumerable:
public static class EnumerableExtensions
{
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source,
Action<T> action)
{
if (source == null) throw new ArgumentNullException("source");
foreach (T element in source)
{
action(element);
}
return source;
}
}
Which would let you do this instead of using ToList():
sqlcmdAll.Parameters.Cast<SqlParameter>().ForEach(p =>
{
if (!l_sDatabaseFieldNames.Contains(p.SourceColumn))
{
throw new Exception("Could not find " + p.SourceColumn);
}
});
You could select the first non matching parameter
SqlParameter sqlp = sqlcmdAll.Parameters.Cast<SqlParameter>().Where(param => !l_sDatabaseFieldNames.Contains(param.SourceColumn)).FirstOrDefault();
if (sqlp != null)
throw new Exception(string.Format("Transform field {0} does not have a corresponding column in the target table.", sqlp.SourceColumn));
I was looking for a general solution for cascading delete in linq to sql from c#, but I could not find any yet.
So I came up with my own solution, which handles one to many relations.
It would not be the general way to delete entities, but some edge case requires it. So before it goes to the production environment, I would like to know that what are you think about it?
It seems to work, but what are the pros and cons, where it can fail? Note that it only supposed to traverse the relation tree downside.
public class CascadingDeleteHelper
{
public void Delete(object entity, Func<object, bool> beforeDeleteCallback)
{
if (!(entity is BusinessEntity))
{
throw new ArgumentException("Argument is not a valid BusinessEntity");
}
if (beforeDeleteCallback == null || beforeDeleteCallback(entity))
{
Type currentType = entity.GetType();
foreach (var property in currentType.GetProperties())
{
var attribute = property
.GetCustomAttributes(true)
.Where(a => a is AssociationAttribute)
.FirstOrDefault();
if (attribute != null)
{
AssociationAttribute assoc = attribute as AssociationAttribute;
if (!assoc.IsForeignKey)
{
var propertyValue = property.GetValue(entity, null);
if (propertyValue != null && propertyValue is IEnumerable)
{
IEnumerable relations = propertyValue as IEnumerable;
List<object> relatedEntities = new List<object>();
foreach (var relation in relations)
{
relatedEntities.Add(relation);
}
relatedEntities.ForEach(e => Delete(e, beforeDeleteCallback));
}
}
}
}
SingletonDataContext.DataContext.GetTable(currentType).DeleteOnSubmit(entity);
SingletonDataContext.DataContext.SubmitChanges();
}
}
}
Thank you very much,
negra
Pros of your solution:
It's generic
It does not require database changes on schema updates
Cons:
Error prone (more custom code)
Performance is (most likely) not optimal
There is a less complex, but more maintenance heavy solution, which is adding ON CASCADE DELETE rules into your database.