I found a scenario with SonarQube 5.3 where different values are being reported in code coverage from Visual Studio code coverage analysis.
Here is a small reproduction, using the MSTest framework.
I'm not able to determine if there is something wrong in what we are doing or if one of the applications is wrong.
The object being tested
[Serializable]
public class Document : IEquatable<Document>
{
public long Id { get; set; }
public string Name { get; set; }
public long DocumentHandle { get; set; }
public long BatchId { get; set; }
public string BatchName { get; set; }
public string RepositoryName { get; set; }
public long DocumentTypeId { get; set; }
public string DocumentTypeName { get; set; }
public int SequenceNumber { get; set; }
public string LoanNumber { get; set; }
public bool IsJunked { get; set; }
public DateTime ArrivalDate { get; set; }
public bool Equals(Document other)
{
if (ReferenceEquals(other, null))
{
return false;
}
if (Id != other.Id)
{
return false;
}
if (!string.Equals(Name, other.Name))
{
return false;
}
if (DocumentHandle != other.DocumentHandle)
{
return false;
}
if (BatchId != other.BatchId)
{
return false;
}
if (!string.Equals(BatchName, other.BatchName))
{
return false;
}
if (!string.Equals(RepositoryName, other.RepositoryName))
{
return false;
}
if (DocumentTypeId != other.DocumentTypeId)
{
return false;
}
if (!string.Equals(DocumentTypeName, other.DocumentTypeName))
{
return false;
}
if (SequenceNumber != other.SequenceNumber)
{
return false;
}
if (!string.Equals(LoanNumber, other.LoanNumber))
{
return false;
}
if (IsJunked != other.IsJunked)
{
return false;
}
if (ArrivalDate != other.ArrivalDate)
{
return false;
}
return true;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return Equals((Document) obj);
}
public static bool operator == (Document document1, Document document2)
{
return ReferenceEquals(document1, null) ? ReferenceEquals(document2, null) : document1.Equals(document2);
}
public static bool operator != (Document document1, Document document2)
{
return !(document1 == document2);
}
public override int GetHashCode()
{
// ReSharper disable once BaseObjectGetHashCodeCallInGetHashCode
// This was done to suppress the messages about needing to override GetHashCode
// Because this class has no ReadOnly properties there is no way to provide a better hashcode
return base.GetHashCode();
}
}
The code has the following tests:
[TestClass]
[ExcludeFromCodeCoverage]
public class DocumentTests
{
private Document defaultDocument;
private Document alteredDocument;
[TestInitialize]
public void Setup()
{
defaultDocument = new Document
{
Id = 1,
Name = "Growlithe",
DocumentHandle = 2,
BatchId = 3,
BatchName = "Vulpix",
RepositoryName = "Pancham",
DocumentTypeId = 4,
DocumentTypeName = "Skrelp",
SequenceNumber = 5,
LoanNumber = "Zorua",
IsJunked = true,
ArrivalDate = new DateTime(1, 1, 1)
};
alteredDocument = new Document
{
Id = 1,
Name = "Growlithe",
DocumentHandle = 2,
BatchId = 3,
BatchName = "Vulpix",
RepositoryName = "Pancham",
DocumentTypeId = 4,
DocumentTypeName = "Skrelp",
SequenceNumber = 5,
LoanNumber = "Zorua",
IsJunked = true,
ArrivalDate = new DateTime(1, 1, 1)
};
}
[TestMethod]
public void ToStringMethod_DocumentPOCO_ConvertObjectToString()
{
// Arrange
var expectedStringDocument = "Document" + Environment.NewLine +
"\tId: 101" + Environment.NewLine +
"\tName: TestName" + Environment.NewLine +
"\tDocumentHandle: 5000000" + Environment.NewLine +
"\tBatchId: 500000000" + Environment.NewLine +
"\tBatchName: TestBatchName" + Environment.NewLine +
"\tRepositoryName: TestRepositoryName" + Environment.NewLine +
"\tDocumentTypeId: 5000000" + Environment.NewLine +
"\tDocumentTypeName: TestDocumentTypeName" + Environment.NewLine +
"\tSequenceNumber: 101" + Environment.NewLine +
"\tLoanNumber: TestLoanNumber" + Environment.NewLine +
"\tIsJunked: False" + Environment.NewLine +
"\tArrivalDate: " + DateTime.Now + Environment.NewLine;
alteredDocument = new Document
{
Id = 101,
Name = "TestName",
DocumentHandle = 5000000,
BatchId = 500000000,
BatchName = "TestBatchName",
RepositoryName = "TestRepositoryName",
DocumentTypeId = 5000000,
DocumentTypeName = "TestDocumentTypeName",
SequenceNumber = 101,
LoanNumber = "TestLoanNumber",
IsJunked = false,
ArrivalDate = DateTime.Now
};
// Act
var processedDocumentObj = StringUtility.StringUtility.ConvertToString(alteredDocument);
// Assert
Assert.IsTrue(processedDocumentObj.Equals(expectedStringDocument));
}
[TestMethod]
public void EqualsReturnsTrueForEquivalentDocument()
{
Assert.IsTrue(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsReturnsFalseForNullDocument()
{
alteredDocument = null;
Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsReturnsFalseForDifferentId()
{
alteredDocument.Id = 9;
Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsReturnsFalseForDifferentName()
{
alteredDocument.Name = "Arcanine";
Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsReturnsFalseForDifferentDocumentHandle()
{
alteredDocument.DocumentHandle = 9;
Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsReturnsFalseForDifferentBatchId()
{
alteredDocument.BatchId = 9;
Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsReturnsFalseForDifferentBatchName()
{
alteredDocument.BatchName = "Ninetails";
Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsReturnsFalseForDifferentRepositoryName()
{
alteredDocument.RepositoryName = "Pangoro";
Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsReturnsFalseForDifferentDocumentTypeId()
{
alteredDocument.DocumentTypeId = 9;
Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsReturnsFalseForDifferentDocumentTypeName()
{
alteredDocument.DocumentTypeName = "Dragalge";
Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsReturnsFalseForDifferentSequenceNumber()
{
alteredDocument.SequenceNumber = 9;
Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsReturnsFalseForDifferentLoanNumber()
{
alteredDocument.LoanNumber = "Zoroark";
Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsReturnsFalseForDifferentIsJunked()
{
alteredDocument.IsJunked = false;
Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsReturnsFalseForDifferentArrivalDate()
{
alteredDocument.ArrivalDate = new DateTime(2, 2, 2);
Assert.IsFalse(defaultDocument.Equals(alteredDocument));
}
[TestMethod]
public void EqualsOperatorWorksForNonNullValues()
{
Assert.IsTrue(defaultDocument == alteredDocument);
}
[TestMethod]
public void NotEqualsOperatorWorksForNonNullValues()
{
alteredDocument = null;
Assert.IsTrue(defaultDocument != alteredDocument);
}
[TestMethod]
public void EqualsOperatorReturnsFalseForNullDotNonNull()
{
alteredDocument = null;
Assert.IsFalse(alteredDocument == defaultDocument);
}
[TestMethod]
public void EqualsOperatorReturnsFalseForNonNullDotNull()
{
alteredDocument = null;
Assert.IsFalse(defaultDocument == alteredDocument);
}
[TestMethod]
public void EqualsOperatorReturnsTrueForNullDotNull()
{
alteredDocument = null;
defaultDocument = null;
Assert.IsTrue(defaultDocument == alteredDocument);
}
}
Visual Studio shows the percentage as: 90.10%
SonarQube shows the percentage as: 40.00%
Sonar doesn't appear to consider the early return statements after the
if (ReferenceEquals(other, null))
{
return false;
}
of the method: public bool Equals(Document other)
I have debugged the tests to validate the lines are hit.
It may be a difference between line/branch coverage:
What is the difference between code coverage and line coverage in sonar
... or white spacing/line wrapping.
You can find the formula for SonarQube's metric description page here: http://docs.sonarqube.org/display/SONAR/Metric+definitions#Metricdefinitions-Tests
coverage = (CT + CF + LC)/(2*B + EL)
where
CT - branches that evaluated to "true" at least once
CF - branches that evaluated to "false" at least once
LC - lines covered (lines_to_cover - uncovered_lines)
B - total number of branches (2*B = conditions_to_cover)
EL - total number of executable lines (lines_to_cover)
Actually could be like others say that you have to build in debug mode. But my coworker has found a strange thing:
If you add () to [TestClass] and [TestMethod] decorators as
[TestClass()] and [TestMethod()] it works fine.
Problem is telling all the developers that Sonar requires it, while Microsoft docs does not.
This applies to Sonarqube DE. In community Edition works fine, without ()
Related
I'm currently trying to scrape a cannabis strain database as it is no longer being maintained. I seem to be running into an issue where table rows are skipped in my logic but it really doesn't make sense, it's like a break is being called when I'm iterating through the //tr elements of the table that is storing the chemical reports.
Is it something like the δ α symbols in the next row. I've tried regex replacing them out to now luck. Any help would be appreciated all code is in a single class console app.
Issue is in the table.ChildNodes not iterating all the way through. Located in the ParseChemical() method.
Sample Page: http://ocpdb.pythonanywhere.com/ocpdb/420/
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel.Design;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Net.Http;
using System.Reflection.Emit;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using HtmlAgilityPack;
using Microsoft.VisualBasic.CompilerServices;
namespace CScrape
{
class Program
{
private static readonly HttpClient Client = new HttpClient();
private static readonly string BaseUri = "http://ocpdb.pythonanywhere.com/ocpdb/";
private static int _startId = 420;
private static int _endId = 1519;
private static List<Lab> _labs = new List<Lab>();
private static List<ChemicalItem> _chemicalItems = new List<ChemicalItem>();
private static List<UnitOfMeasure> _uoms = new List<UnitOfMeasure>();
private static List<Strain> _strains = new List<Strain>();
static void Main(string[] args)
{
Client.DefaultRequestHeaders.Accept.Clear();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13;
_uoms.AddRange(GetUoms());
for (var i = _startId; i <= _endId; i++)
{
var result = GetUri($"{BaseUri}{i}").Result;
_strains.Add(ParseChemical(result));
}
}
private static long AddChemicalItem(ChemicalItem item)
{
if (ChemicalExists(item.Symbol))
return _chemicalItems.FirstOrDefault(ci => ci.Symbol == item.Symbol)?.Id ?? -1;
item.Id = _chemicalItems.Count + 1;
_chemicalItems.Add(item);
return item.Id;
}
private static void UpdateChemicalItem(ChemicalItem item)
{
if (!ChemicalExists(item.Symbol)) return;
var index = _chemicalItems.IndexOf(item);
if (!(index >= 0)) return;
_chemicalItems.RemoveAt(index);
AddChemicalItem(item);
}
private static long AddLab(Lab lab)
{
if (LabExists(lab.Name))
return _labs.FirstOrDefault(l => l.Name == lab.Name)?.Id ?? -1;
lab.Id = _labs.Count + 1;
_labs.Add(lab);
return lab.Id;
}
private static async Task<string> GetUri(string uri)
{
var response = await Client.GetByteArrayAsync(uri);
return Encoding.UTF8.GetString(response, 0, response.Length - 1);
}
private static Strain ParseChemical(string html)
{
html = Regex.Replace(html, #"Δ", "Delta");
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(html);
var strain = new Strain();
strain.Reports ??= new List<ChemicalReport>();
try
{
strain.Name = htmlDoc.DocumentNode.SelectSingleNode("/html/body/div/div[1]/div[1]/h3/b").InnerText;
}
catch (Exception e)
{
// TODO: DOcument Exception
Console.WriteLine(e.Message);
}
if (string.IsNullOrWhiteSpace(strain.Name)) return null;
try
{
var ocpId = htmlDoc.DocumentNode.SelectSingleNode("/html/body/div/div[1]/div[2]/p/b/text()[1]");
strain.OcpId = SanitizeHtml(ocpId.InnerText).Split(':')[1];
}
catch (Exception e)
{
// TODO: Document Exception
Console.WriteLine(e.Message);
}
if (string.IsNullOrWhiteSpace(strain.OcpId)) return null;
try
{
var date = htmlDoc.DocumentNode.SelectSingleNode("/html/body/div/div[1]/div[2]/p/text()");
}
catch (Exception e)
{
// TODO: Document Exception
Console.WriteLine(e.Message);
}
var chemReport = new ChemicalReport();
chemReport.Items ??= new List<ReportItem>();
try
{
var table = htmlDoc.DocumentNode.SelectSingleNode("/html/body/div/div[2]/div[1]/table/tbody");
var children = table.ChildNodes.ToList();
// On the sample page there are 200 children here
// However it only interates through the first few and then just breaks out of the loop
foreach (var child in children)
{
var name = child.Name;
if (child.Name == "tr")
{
var infos = child.SelectNodes("th|td");
foreach (var info in infos)
{
if(string.IsNullOrWhiteSpace(info.InnerText)) continue;
if (info.InnerText.Contains("Report")) continue;
if (double.TryParse(info.InnerText, out var isNumber))
{
var last = chemReport.Items.LastOrDefault();
if (last == null) continue;
if (last.Value <= 0.0000) last.Value = isNumber;
else
{
var further = chemReport.Items.ToArray()[chemReport.Items.Count - 2];
if (further.Value <= 0.0000)
further.Value = isNumber;
}
continue;
}
var _ = new ChemicalItem
{
Name = info.InnerText,
Symbol = info.InnerText
};
_.Id = AddChemicalItem(_);
var report = new ReportItem
{
Chemical = _,
ChemicalItemId = _.Id,
UnitOfMeasureId = 1,
UoM = GetUoms()[0]
};
chemReport.Items.Add(report);
}
}
}
strain.Reports.Add(chemReport);
}
catch (Exception e)
{
// TODO: Document exception
Console.Write(e.Message);
}
return strain;
}
private static List<UnitOfMeasure> GetUoms()
{
return new List<UnitOfMeasure>
{
new UnitOfMeasure {Name = "Milligrams per Gram", Symbol = "mg/g"},
new UnitOfMeasure {Name = "Percent", Symbol = "%"}
};
}
private static string SanitizeHtml(string text, string replacement = "")
{
return Regex.Replace(text, #"<[^>]+>| |α|\n|\t", replacement);
}
private static string GetLabName(string[] split)
{
var strip = split[0].Split(':')[1];
for(var i = 1; i < split.Length - 2; i ++)
{
if (string.IsNullOrWhiteSpace(split[i])) break;
strip += $" {split[i]}";
}
return strip;
}
private static string GetSampleId(string[] split)
{
var found = false;
foreach (var item in split)
{
if (found)
return item.Split(':')[1];
if (item == "Sample") found = true;
}
return "NA";
}
private static bool LabExists(string name)
{
return _labs.Any(lab => lab.Name == name);
}
private static bool ChemicalExists(string name)
{
return _chemicalItems.Any(ci => ci.Symbol == name);
}
private static ReportItem GetReportItem(string text)
{
if (string.IsNullOrWhiteSpace(text)) return null;
ReportItem ri = null;
try
{
var clean = SanitizeHtml(text);
var check = 0;
var split = clean.Split(':');
var label = split[0];
if (string.IsNullOrWhiteSpace(label)) return null;
if (double.TryParse(label, out var invalidType)) return null;
var val = string.Empty;
if (split.Length == 1)
{
if (split[0].Contains("Total"))
{
Regex re = new Regex(#"([a-zA-Z]+)(\d+)");
Match result = re.Match(split[0]);
label = result.Groups[1].Value;
val = result.Groups[2].Value;
}
}
if(split.Length > 1)
val = split[1];
if (!ChemicalExists(label)) AddChemicalItem(new ChemicalItem {Id = _chemicalItems.Count + 1,Symbol = label});
ri = new ReportItem();
ri.Chemical = _chemicalItems.FirstOrDefault(ci => ci.Symbol == label);
ri.UoM = val.Contains("%")
? _uoms.FirstOrDefault(uom => uom.Symbol == "%")
: _uoms.FirstOrDefault(uom => uom.Symbol == "mg/g");
if (string.IsNullOrWhiteSpace(val)) return ri;
var value = val.Contains("%") ? split[1].Substring(0, val.Length - 1) : val;
ri.Value = Convert.ToDouble(value);
}
catch (Exception e)
{
// TODO: Document Exception
Console.WriteLine(e.Message);
}
return ri;
}
//private static ChemicalItem GetChemicalItem(string text)
//{
//}
public class Strain
{
public long Id { get; set; }
public string Name { get; set; }
public DateTime Created { get; set; }
public string OcpId { get; set; }
public bool IsHidden { get; set; } = false;
public virtual ICollection<ChemicalReport> Reports { get; set; }
}
public class Lab
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ChemicalReport> Reports { get; set; }
}
public class ChemicalReport
{
public long Id { get; set; }
[ForeignKey("Lab")]
public long LabId { get; set; }
public virtual Lab Lab { get; set; }
public string SampleId { get; set; }
public DateTime Created { get; set; }
public virtual ICollection<ReportItem> Items { get; set; }
[ForeignKey("Strain")]
public long StrainId { get; set; }
public virtual Strain Strain { get; set; }
}
public class ChemicalItem
{
public long Id { get; set; }
public string Name { get; set; }
public string Symbol { get; set; }
}
public class ReportItem
{
public long Id { get; set; }
[ForeignKey("Chemical")]
public long ChemicalItemId { get; set; }
public virtual ChemicalItem Chemical { get; set; }
public double Value { get; set; }
[ForeignKey("UoM")]
public long UnitOfMeasureId { get; set; }
public virtual UnitOfMeasure UoM { get; set; }
}
public class UnitOfMeasure
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Symbol { get; set; }
}
}
}
My professor wants us to create a reusable class and console app that lists book objects. I got the first part of the assignment where I am supposed to print the books I created, but now I am stuck on the part where I have to modify the data and print again using the same method and then check out two books and print again. I have tried to look at example online and although some of them have helped, none have been able to get me to pass this roadblock.
class LibraryBook
{
private string _bookTitle;
private string _authorName;
private string _publisher;
private int _copyrightYear;
private string _callNumber;
public LibraryBook(string booktitle, string authorname, string publisher, int ccyear, string callnumber)
{
BookTitle = booktitle;
AuthorName = authorname;
Publisher = publisher;
CopyrightYear = ccyear;
CallNumber = callnumber;
}
public string BookTitle
{
get
{
return _bookTitle;
}
set
{
_bookTitle = value;
}
}
public string AuthorName
{
get
{
return _authorName;
}
set
{
_authorName = value;
}
}
public string Publisher
{
get
{
return _publisher;
}
set
{
_publisher = value;
}
}
public int CopyrightYear
{
get
{
return _copyrightYear;
}
set
{
const int CYEAR = 2019;
if (value > 0)
_copyrightYear = value;
else
_copyrightYear = CYEAR;
}
}
public string CallNumber
{
get
{
return _callNumber;
}
set
{
_callNumber = value;
}
}
public bool Avail;
public void CheckOut()
{
Avail = true;
}
public void ReturnToShelf()
{
Avail = false;
}
public bool IsCheckedOut()
{
return Avail;
}
public override string ToString()
{
return $"Book Title: {BookTitle}{Environment.NewLine}" +
$"Author Name: {AuthorName}{Environment.NewLine}" +
$"Publisher: {Publisher}{Environment.NewLine}" +
$"Copyright Year: {CopyrightYear}{Environment.NewLine}" +
$"Call Number: {CallNumber}{Environment.NewLine}" +
$"Checked Out: {IsCheckedOut()}{Environment.NewLine}";
}
}
}
class Program
{
static void Main(string[] args)
{
LibraryBook[] favBooksArray = new LibraryBook[5];
favBooksArray[0] = new LibraryBook("Harry Potter and the Philospher's Stone", "J.K. Rowling", "Scholastic Corporation", 1997, "HA-12.36");
favBooksArray[1] = new LibraryBook("Harry Potter and the Chamber of Secret", "J.K. Rowling", "Scholastic Corporation", 2001, "HA-13.48");
favBooksArray[2] = new LibraryBook("Tangerine", "Edward Bloor", "Harcourt", 1997, "TB-58.13");
favBooksArray[3] = new LibraryBook("Roll of Thunder, Hear My Cry", "Mildred D. Taylor", "Dial Press", 1976, "RT-15.22");
favBooksArray[4] = new LibraryBook("The Giver", "Lois Lowry", "Fake Publisher", -1, "Fk200-1");
WriteLine($"------LIBRARY BOOKS------{Environment.NewLine}");
BooksToConsole(favBooksArray);
WriteLine($"------CHANGES MADE----- {Environment.NewLine}");
ChangesToBooks(favBooksArray);
BooksToConsole(favBooksArray);
WriteLine($"------RETURNING BOOKS TO SHELF------{Environment.NewLine}");
ReturnBooksToConsole(favBooksArray);
BooksToConsole(favBooksArray);
}
public static void BooksToConsole(LibraryBook[] favBooksArray)
{
foreach (LibraryBook books in favBooksArray)
{
WriteLine($"{books}{Environment.NewLine}");
}
}
public static void ChangesToBooks(LibraryBook[] favBooksArray)
{
favBooksArray[1].AuthorName = "*****The Rock*****";
favBooksArray[3].BookTitle = "****Totally Not A Fake Name*****";
favBooksArray[1].CheckOut();
favBooksArray[4].CheckOut();
}
public static void ReturnBooksToConsole(LibraryBook[] favBooksArray)
{
favBooksArray[1].ReturnToShelf();
favBooksArray[4].ReturnToShelf();
}
}
}
I'm trying to agregate a list of multiple propertys with Linq.
My Second field is a List of Strings + an other List of strings inside.
Here's a sample of my code :
using System;
using System.Collections.Generic;
using System.Linq;
public class RefValueData
{
public int ReferenceId { get; set; }
public int SiteId { get; set; }
public string SiteName { get; set; }
public string Code { get; set; }
public decimal UnitPoints { get; set; }
public List<TranslationData> Texts { get; set; }
}
public class TranslationData
{
public string Text { get; set; }
public List<TranslationValue> Translations { get; set; }
}
public class TranslationValue
{
public string Culture { get; set; }
public string TranslationText { get; set; }
}
public class Program
{
public static void Main()
{
var values = new List<RefValueData>
{
new RefValueData(){
ReferenceId = 4,
Code = "Code",
SiteId = 2,
SiteName = "Paris",
UnitPoints = 50,
Texts = new List<TranslationData>
{
new TranslationData(){
Text = "A",
Translations = new List<TranslationValue>
{
new TranslationValue() { Culture = "FR-fr", TranslationText = "Bonjour" },
new TranslationValue() { Culture = "ES-es", TranslationText = "Hola" },
}
}
}
},
new RefValueData()
{
ReferenceId = 5,
Code = "Code",
SiteId = 4,
SiteName = "Lyon",
UnitPoints = 50,
Texts = new List<TranslationData>
{
new TranslationData(){
Text = "A",
Translations = new List<TranslationValue>
{
new TranslationValue() { Culture = "FR-fr", TranslationText = "Bonjour" },
new TranslationValue() { Culture = "ES-es", TranslationText = "Hola" },
}
}
}
},
new RefValueData()
{
ReferenceId = 6,
Code = "Code",
SiteId = 3,
SiteName = "Paris",
UnitPoints = 52,
Texts = new List<TranslationData>
{
new TranslationData(){
Text = "B",
Translations = new List<TranslationValue>
{
new TranslationValue() { Culture = "FR-fr", TranslationText = "Salut" },
new TranslationValue() { Culture = "ES-es", TranslationText = "Ciao" },
}
}
}
}
};
var values2 = values
.Distinct()
.GroupBy(x => new
{
x.UnitPoints,
x.Texts
})
.Select(x => new
{
x.Key.UnitPoints,
Texts = x.Key.Texts,
Site = x.Select(y=>y.SiteName)
})
.ToList();
Console.WriteLine(values2.Count);
}
}
I want to have only two lines in my values2 list, but everytime it returns me the whole list.
When I only group by Unit Point, it's work great !
I tried to group the first two lines of my list with some custom Linq query but it doesn't work at all...
Any help / advice is much appreciated :) !
EDIT :
I also tried with an override of the Equals methods like this, but I can't make it work :
public class TranslationValue
{
public string Culture { get; set; }
public string TranslationText { get; set; }
public override bool Equals(object obj)
{
var other = obj as TranslationValue;
if (other == null)
{
return false;
}
return Culture == other.Culture && TranslationText == other.TranslationText;
}
public override int GetHashCode()
{
var hashCode = -2095322044;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Culture);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(TranslationText);
return hashCode;
}
}
public class TranslationData
{
public string Text { get; set; }
public List<TranslationValue> Translations { get; set; }
public override bool Equals(object obj)
{
var other = obj as TranslationData;
if (other == null)
{
return false;
}
return Text == other.Text && Translations.SequenceEqual(other.Translations);
}
public override int GetHashCode()
{
var hashCode = -1551681861;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Text);
hashCode = hashCode * -1521134295 + EqualityComparer<List<TranslationValue>>.Default.GetHashCode(Translations);
return hashCode;
}
}
EDIT2 : Here's my 'real' code :
var values = referenceValues.Select(value => new
{
ReferenceId = value.ReferenceId,
SiteId = value.Reference.SiteId ?? -1,
SiteName = value.Reference.Site.Name ?? allSitesName,
Code = value.Code,
UnitPoints = value.UnitPoints,
Texts = // Type: List<TranslationData> , but it will not use the TranslationDataList class that normally work thanks to your help
value.ReferenceValueTexts.Select(text =>
new TranslationData
{
Text = text.Text, // string
Translations = text.TranslationDataValues.Select(translation => // List<TranslationValue>
new TranslationValue {
Culture = translation.Language.StrCulture,
TranslationText = translation.Value
}).ToList()
}).ToList()
}
Julien.
Here's one solution. It works for the sample code that you wrote. But it needs a little work to be robust:
// and also change the declarations in the main method to: new TranslationDataList
public class TranslationDataList : List<TranslationData>
{
public override int GetHashCode()
{
int hash = 13;
// string.GetHashCode() is not reliable. This should be an algorithm that returns the same value for two different lists that contain the same data
foreach (var data in this)
hash = (hash * 7) + data.Text.GetHashCode();
return hash;
}
public override bool Equals(object obj)
{
var other = obj as TranslationDataList;
if (other == null) return false;
if (other.Count != Count) return false;
// write the equality logic here. I don't know if it's ok!
for (int i = 0; i < other.Count; i++)
if (other[i].Text != this[i].Text)
return false;
return true;
}
}
First of all you should add a constructor to the TranslationDataList:
public class TranslationDataList : List<TranslationData>
{
public TranslationDataList(IEnumerable<TranslationData> translationData)
: base(translationData)
{ }
// other members ...
}
Now you can use the TranslationDataList in your query:
var values = referenceValues.Select(value => new
{
ReferenceId = value.ReferenceId,
SiteId = value.Reference.SiteId ?? -1,
SiteName = value.Reference.Site.Name ?? allSitesName,
Code = value.Code,
UnitPoints = value.UnitPoints,
Texts = new TranslationDataList( value.ReferenceValueTexts.Select(text =>
new TranslationData
{
Text = text.Text, // string
Translations = text.TranslationDataValues.Select(translation => // List<TranslationValue>
new TranslationValue {
Culture = translation.Language.StrCulture,
TranslationText = translation.Value
}).ToList()
})); // don't ToList() here anymore
}
And here is another solution:
The GroupBy method takes an IEqualityComparer that can do the responsibility of comparing items for the grouping. But the problem is you used an anonymous type for the key in your grouping "GroupBy(x=>new{x.UnitPoints, x.Texts})". First we have to create a class to play the key role:
public class Key
{
public Key(decimal unitPoints, List<TranslationData> texts)
{
UnitPoints = unitPoints;
Texts = texts;
}
public decimal UnitPoints { get; set; }
public List<TranslationData> Texts { get; set; }
}
then we can implement the comparer:
public class Comparer : IEqualityComparer<Key>
{
public bool Equals(Key x, Key y)
{
if (x.UnitPoints != y.UnitPoints) return false;
if (!ListsAreEqual(x.Texts, y.Texts)) return false;
return true;
}
private bool ListsAreEqual(List<TranslationData> x, List<TranslationData> y)
{
if (x.Count != y.Count) return false;
for (int i = 0; i < x.Count; i++)
if (x[i].Text != y[i].Text)
return false;
return true;
}
public int GetHashCode(Key key)
{
int hash = 13;
hash = (hash * 7) + key.UnitPoints.GetHashCode();
foreach (var data in key.Texts)
hash = (hash * 7) + data.Text.GetHashCode();
return hash;
}
}
and finally this is what your query will look like:
var values2 = values
.Distinct()
.GroupBy(x => new Key(x.UnitPoints, x.Texts), new Comparer())
.Select(x => new
{
x.Key.UnitPoints,
Texts = x.Key.Texts,
Site = x.Select(y => y.SiteName)
}).ToList();
I think the first solution (creating the customized list class) is better, because you can also refactor your code and extract some logic to it.
I wrote a piece of code to run from the first IP address to the last and retrieve the MAC and Hostname of devices in my network.
But, i do not know how to get the hierachy of then. Information like what router is the device conected (via Cable of WiFi also). And some routers are not managed (they don't have IP - they are just "switchs").
First, i didn't think it was possible, since a "tracert" on the CMD does not show then, but when i call the "Network Complete Map" on Windows Control Panel, they get all the information i need - so there is a way. See the image below:
The "switchs" circled in red have no IP, the "TL-xxxx" are routers with DHCP turned of. The "gateway" is a server that has the DHCP and provides connection to the internet.
Thus far, i wrote the following code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Net;
using System.Runtime.InteropServices;
namespace Network
{
[Serializable] public sealed class Map
{
private bool mMARKED = true;
private long mDB = 0L;
private string mIP = string.Empty;
private string mMAC = string.Empty;
private string mBRAND = string.Empty;
private string mNAME = string.Empty;
public bool Save { get { return this.mMARKED; } set { this.mMARKED = value; } }
public long DBId { get { return this.mDB; } set { this.mDB = value; } }
public string IP { get { return this.mIP; } set { this.mIP = value; } }
public string MAC { get { return this.mMAC; } set { this.mMAC = value; } }
public string BRAND { get { return this.mBRAND; } set { this.mBRAND = value; } }
public string NAME { get { return this.mNAME; } set { this.mNAME = value; } }
}
[Serializable] public sealed class Scanner
{
public const string WebOUIFile = "http://standards-oui.ieee.org/oui.txt";
public const string LocalOUIFileName = "oui.txt";
private const long MaxOUIAge = (TimeSpan.TicksPerDay * 90L);
internal Dictionary<string, string> OUIList;
private List<Map> mDevices = new List<Map>(50);
public List<Map> Devices { get { return this.mDevices; } }
public static Scanner Scan;
public Thread Worker;
public bool AutoSave { get; set; }
private string Node;
private byte mInitial;
public static string UploadPath { get; set; }
public byte Initial { get { return this.mInitial; } set { this.mInitial = value; } }
public byte Current { get; private set; }
public byte Limit { get; set; }
public bool Working { get; private set; }
public string Errors;
public string Message { get; private set; }
public bool UpdatingOUI { get; private set; }
public void Interrupt()
{
this.Working = false;
if (this.Worker != null)
{
this.Worker.Abort();
this.Worker = null;
}
this.Node = string.Empty;
this.Initial = 0;
this.Current = 0;
this.Limit = 0;
this.Working = false;
}
public void ToDestroy()
{
this.Interrupt();
this.Errors = string.Empty;
this.Message = string.Empty;
}
public void Stop(bool immediate) { if (immediate) { this.Interrupt(); } }
[DllImport("iphlpapi.dll", SetLastError = true, CharSet = CharSet.Auto)] private static extern int SendARP(int DestIP, int SrcIP, out long pMacAddr, ref int PhyAddrLen);
public void ToBegin() { }
public void Stop() { this.Stop(true); }
private static int IPToInt(string Expression)
{
try
{
byte[] IPAddress = System.Net.IPAddress.Parse(Expression).GetAddressBytes();
return (Convert.ToInt32(IPAddress[3]) << 24) | (Convert.ToInt32(IPAddress[2]) << 16) | (Convert.ToInt32(IPAddress[1]) << 8) | Convert.ToInt32(IPAddress[0]);
} catch { return 0; }
}
private Map GetBasics(string IPString)
{
int res = Scanner.IPToInt(IPString);
if (res > 0)
{
long mem = 0L;
int PhyAddrLen = 6;
if (Scanner.SendARP(res, 0, out mem, ref PhyAddrLen) == 0)
{
Map dev = new Map();
byte[] macbytes = BitConverter.GetBytes(mem);
dev.IP = IPString;
string Tmp = BitConverter.ToString(macbytes, 0, 3);
if (this.OUIList != null && this.OUIList.ContainsKey(Tmp)) { dev.BRAND = this.OUIList[Tmp]; }
dev.MAC = Tmp + "-" + BitConverter.ToString(macbytes, 3, 3);
try { dev.NAME = Dns.GetHostEntry(IPString).HostName.ToLower(); } catch { dev.NAME = "unknow"; }
return dev;
}
}
return null;
}
private static void GetNode(ref string IP, ref string Node, ref byte Device)
{
string[] NodeComp = IP.Split('.');
Node = NodeComp[0] + "." + NodeComp[1] + "." + NodeComp[2] + ".";
Device = Convert.ToByte(NodeComp[3]);
}
public static Dictionary<string, string> DonwloadOUTFile(bool ForceUpdate = true)
{
Dictionary<string, string> List = null;
try
{
string Aux = Scanner.UploadPath;
if (Aux == null) { Aux = string.Empty; }
else if (Aux != string.Empty)
{
string Tmp = Aux + "~" + Scanner.LocalOUIFileName;
Aux += Scanner.LocalOUIFileName;
bool FileExists = File.Exists(Aux);
if (FileExists && ((DateTime.UtcNow.Ticks - (new FileInfo(Aux)).CreationTimeUtc.Ticks) > Scanner.MaxOUIAge))
{
File.Delete(Aux);
ForceUpdate = true;
}
string Aux2 = string.Empty;
if (ForceUpdate)
{
List = new Dictionary<string, string>(25000);
using (WebClient Downloader = new WebClient()) { Downloader.DownloadFile(Scanner.WebOUIFile, Tmp); }
using (StreamReader Reader = new StreamReader(Tmp))
using (StreamWriter Writer = new StreamWriter(Aux))
{
do
{
Aux = Reader.ReadLine();
if (Aux.ToLower().Contains("(hex)"))
{
Aux2 = Aux.Substring(0, 8).ToUpper();
Aux = Aux.Substring(Aux.LastIndexOf('\t') + 1);
if (!List.ContainsKey(Aux2))
{
List.Add(Aux2, Aux);
Writer.WriteLine(Aux2 + "\t" + Aux);
}
}
} while (Reader.Peek() >= 0);
Reader.Close();
Writer.Close();
}
try { File.Delete(Tmp); } catch { /* NOTHING */ }
}
else if (FileExists)
{
List = new Dictionary<string, string>(25000);
using (StreamReader Reader = new StreamReader(Aux))
{
do
{
Aux = Reader.ReadLine();
if (Aux != null && Aux.Length > 9)
{
Aux2 = Aux.Substring(0, 8);
if (!List.ContainsKey(Aux2)) { List.Add(Aux2, Aux.Substring(9)); }
}
} while (Reader.Peek() >= 0);
Reader.Close();
}
}
}
}
catch
{
if (List != null) { List.Clear(); }
List = null;
}
return List;
}
private void ReadScaner()
{
this.UpdatingOUI = true;
try { this.OUIList = Scanner.DonwloadOUTFile(ForceUpdate: false); } catch { /* NOTHING */ }
this.UpdatingOUI = false;
if (this.OUIList == null || this.OUIList.Count == 0) { this.Errors += "\nErrorOUIFileDownload"; }
Map Dev = null;
this.Current = this.Initial;
if (this.Limit < this.Initial)
{
Dev = this.GetBasics(this.Node + this.Initial.ToString());
if (Dev != null) { this.Devices.Add(Dev); }
}
else
{
bool ToAdd = true;
while (this.Current <= this.Limit)
{
Dev = this.GetBasics(this.Node + this.Current.ToString());
this.Current += 1;
if (Dev != null)
{
ToAdd = true;
foreach (Map iDev in this.Devices)
{
if (iDev.MAC == Dev.MAC)
{
ToAdd = false;
break;
}
}
if (ToAdd) { this.Devices.Add(Dev); }
}
}
}
this.Message = "Finished!";
this.Interrupt();
}
public void GetRange(string IPInitial, byte Limit, bool AutoSave = true)
{
if (!this.Working)
{
this.AutoSave = AutoSave;
this.Working = true;
Scanner.GetNode(ref IPInitial, ref this.Node, ref this.mInitial);
this.Limit = Limit;
this.Worker = new Thread(this.ReadScaner);
this.Worker.IsBackground = true;
this.ToBegin();
this.Worker.Start();
}
}
public static void GetRange(bool AutoSave, string IPInitial, byte Limit)
{
if (Scanner.Scan == null)
{
Scanner.Scan = new Scanner();
Scanner.Scan.GetRange(IPInitial, Limit, AutoSave: AutoSave);
}
}
~Scanner()
{
if (this.OUIList != null)
{
this.OUIList.Clear();
this.OUIList = null;
}
}
}
}
How do i make that code able to get the hierarchy like windows does?
UPDATE
I don´t want you to do my Work and write code for me I just wanted a nurge in the right Direction!
So I have to be more specific with my Problem, give me a chance to do some work on this and I will update my question with the results ;-)
UPDATE 2
I´ve solved my Problem with Roslyn maybe not very elegant but it work for my needs, here is the code ;-)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
namespace ParserTest
{
public class MyParser
{
private int _currentLevel = 1;
public void TestMethod()
{
string testString =
#" if(#ISEMPTY(temp.tis_filterstatus2))
{
tis_datasheet_selection.is_selected = 'Y'
}
else
{
if(#ISEMPTY(temp.tis_programmtyp_filter)) { }
else
{
AND tis_programme_v.type = '#SUB(temp.tis_programmtyp_filter)'
}
if(#ISEMPTY(temp.tis_programmfilter)) { }
else
{
AND tis_programme_v.programm LIKE '#SUB(temp.tis_programmfilter)%'
}";
var result = this.Parse(testString);
var finalResult = this.GenerateDsl(result);
}
public List<IfStatement> Parse(string strToParse)
{
var result = new List<IfStatement>();
var syntaxTree = SyntaxTree.ParseText(#"using System;class C{static void M(){" + strToParse + "}}");
var rootNodes = syntaxTree.GetRoot().DescendantNodes().Where(getRootNodes);
result = rootNodes.Select(n => ToIfStatement(n, null)).ToList();
ApplyNestingLevel(result);
return result;
}
private string GenerateDsl(List<IfStatement> list)
{
var sb = new StringBuilder();
foreach(var ifStmt in list)
{
IfStatementToDsl(ifStmt, sb);
}
return sb.ToString();
}
private string IfStatementToDsl(IfStatement ifStmt, StringBuilder sb)
{
string sqr = "";
for (int i = 0; i < ifStmt.Level; i++)
{
sqr += "'";
}
sb.Append("#IF(");
sb.Append(ifStmt.Condition.ApplyLevel(ifStmt.Level) + "," + sqr);
sb.Append(ifStmt.Statement.ApplyLevel(ifStmt.Level));
if(ifStmt.Childs.Count > 0)
{
foreach(var c in ifStmt.Childs)
{
IfStatementToDsl(c, sb);
}
}
sb.Append(sqr + "," + sqr);
if(ifStmt.Else != null)
{
sb.Append(ifStmt.Else.Statement.ApplyLevel(ifStmt.Level));
foreach(var c in ifStmt.Else.Childs)
{
IfStatementToDsl(c, sb);
}
}
sb.Append(sqr + ")");
return sb.ToString();
}
#region Parsing-Methods
private IfStatement ToIfStatement(SyntaxNode node, SyntaxNode parent)
{
var ifNode = (IfStatementSyntax)node;
var ifStmt = new IfStatement
{
Condition = ifNode.Condition.ToString(),
Statement = GetIfStatement(ifNode),
Childs = GetIfChilds(ifNode)
};
if (ifNode.Else != null)
{
ifStmt.Else = new ElseStatement
{
Statement = GetElseStatement(ifNode.Else),
Childs = GetElseChilds(ifNode.Else)
};
}
return ifStmt;
}
private List<IfStatement> GetIfChilds(IfStatementSyntax node)
{
var childs = node.Statement.DescendantNodes().Where(n => WhereIfNodes(n, node));
return childs.Select(n => ToIfStatement(n, node)).ToList();
}
private List<IfStatement> GetElseChilds(ElseClauseSyntax node)
{
var childs = node.Statement.DescendantNodes().Where(n => WhereElseNodes(n, node));
return childs.Select(n => ToIfStatement(n, node)).ToList();
}
private string GetIfStatement(IfStatementSyntax node)
{
var result = node.Statement.DescendantNodes().Where(n => WhereIfStatement(n, node));
string returnValue = "";
foreach (var n in result)
{
returnValue += n.ToString();
}
return returnValue.CleanString();
}
private string GetElseStatement(ElseClauseSyntax node)
{
var result = node.Statement.DescendantNodes().Where(n => WhereElseStatement(n, node));
string returnValue = "";
foreach (var n in result)
{
returnValue += n.ToString() + " ";
}
return returnValue.CleanString();
}
private void ApplyNestingLevel(List<IfStatement> list)
{
foreach (var item in list)
{
item.Level = _currentLevel;
if (item.Childs.Count > 0 || (item.Else != null && item.Else.Childs.Count > 0))
{
_currentLevel++;
}
ApplyNestingLevel(item.Childs);
if (item.Else != null)
{
ApplyNestingLevel(item.Else.Childs);
}
}
}
#endregion
#region Linq Where-Conditions
private bool WhereIfNodes(SyntaxNode node, IfStatementSyntax parent)
{
if(node.Kind == SyntaxKind.IfStatement && (node.Parent.Parent == parent))
{
return true;
}
return false;
}
private bool WhereElseNodes(SyntaxNode node, ElseClauseSyntax parent)
{
if (node.Kind == SyntaxKind.IfStatement && (node.Parent.Parent == parent))
{
return true;
}
return false;
}
private bool WhereIfStatement(SyntaxNode node, IfStatementSyntax parent)
{
if ((node.Kind == SyntaxKind.ExpressionStatement || node.Kind == SyntaxKind.LocalDeclarationStatement)
&& (node.Parent.Parent == parent))
{
return true;
}
return false;
}
private bool WhereElseStatement(SyntaxNode node, ElseClauseSyntax parent)
{
if ((node.Kind == SyntaxKind.ExpressionStatement || node.Kind == SyntaxKind.LocalDeclarationStatement)
&& (node.Parent.Parent == parent))
{
return true;
}
return false;
}
private Func<SyntaxNode, bool> getRootNodes =
n => n.Kind == SyntaxKind.IfStatement &&
(n.Parent.Parent.Kind != SyntaxKind.ElseClause && n.Parent.Parent.Kind != SyntaxKind.IfStatement);
#endregion
}
public class IfStatement
{
public int Level { get; set; }
public string Condition { get; set; }
public string Statement { get; set; }
public ElseStatement Else { get; set; }
public List<IfStatement> Childs { get; set; }
}
public class ElseStatement
{
public string Statement { get; set; }
public List<IfStatement> Childs { get; set; }
}
public static class Ext
{
public static string CleanString(this string value)
{
return value.Replace("\t", "").Replace("\n", "").Replace("\r", "");
}
public static string ApplyLevel(this string value, int level)
{
int multiplier = level * 2;
if (level == 0)
multiplier = 1;
var sb = new StringBuilder(multiplier);
for (int i = 0; i < multiplier; i++)
{
sb.Append("'");
}
return value.Replace("'", sb.ToString());
}
}
}
I have to write if-else Statements in a Domain-Specific-Language which is really a pain in the ass!
(The DSL is from a Third-Party-Tool which I have to use to generate WHERE-Statements for SQL-Queries)
Syntax:
#IF(#ISEMPTY(#SUB(temp.last_name))),'if true','else')
#SUB() reads a textboxvalue
Sample:
#IF(#ISEMPTY(#SUB(temp.last_name))),'','account_contact.last_name = ''DOE'' ')
you have to double your single-quotes in the else statement and if you want to nest different "if-else" every time you go a level deeper you have to double the doubled single-quotes!
You see it´s not very simple to write if you have complicated contitions...
So I thought I write a parser that transforms a normal if-else statement to this DSL-Syntax!
The Parser should create objects of this class:
public class IfCondition
{
public int ID { get; set; }
public int ParentID { get; set; }
public int Level { get; set; }
public string Condition { get; set; }
public string Content { get; set; }
public string ElseContent { get; set; }
}
based on a Collection of this Objects I could generate the DSL-Statements!
So my Problem is I really don´t have a clue, how to parse a String like this:
IF(#ISEMPTY(#SUB(temp.last_name))) { }
ELSE
{
IF(#SUB(temp.test) = 'Y')
{
account_contact.last_name = 'DOE'
}
ELSE
{
account_contat.first_name = "JOHN"
}
}
can somebody give me a nudge in the right direction?
If you use Roslyn for syntax rewritting. Not to just write about what you can do, here is an easy example.
Basically how it works. You create a syntax tree for your code and then use predefined visitors to visit those nodes you want to rewrite, you replace them by valid C# code and then you can compile this tree and make it work.
EDIT:
Another, more reliable source with an example