I have a bit of a silly issue:
I have a large number of unit tests which all have method attributes like this:
[TestMethod]
[Owner("me")]
[Description("It tests something.")]
[TestProperty(TC.Name, "Some Test")]
[TestProperty(TC.Requirement, "req203")]
[TestProperty(TC.Reviewer, "someguy")]
[TestProperty(TC.Environment, "MSTest")]
[TestProperty(TC.CreationDate, "24.01.2012")]
[TestProperty(TC.InternalTcId, "{9221A494-2B31-479D-ADE6-D4773C2A9B08}")]
public void TestSomething()
{ ... }
(If you're wondering: these attributes are used for automated testing and requirement coverage stuff.. )
Now, unfortunately these attributes are in a different order at most test methods - which makes it a bit messy to review and such. So I'm looking for a way to order them..
Would you know any other way than rearranging them manually?
(I thought about writing some VS plugin or so) - I'm just wondering whether I'm really the first person with that wish.
Open up the Macro Explorer - and paste this code into a module (It's straight from my own little collection of macros):
Sub Sort()
Dim selection As EnvDTE.TextSelection = DTE.ActiveDocument.Selection
If selection Is Nothing Or String.IsNullOrWhiteSpace(selection.Text) Then
Exit Sub
End If
Dim lines As String() = selection.Text.Split(vbCrLf.ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
If lines.Length <= 1 Then Exit Sub
lines = lines.OrderBy(Function(s As String) s, StringComparer.CurrentCulture).ToArray()
DTE.UndoContext.Open("Sort Lines")
selection.Insert(String.Join(vbCrLf, lines))
selection.SmartFormat()
DTE.UndoContext.Close()
DTE.StatusBar.Text = "Sort Lines complete"
selection.SmartFormat()
End Sub
(just edited it as the try/end try wasn't really right - so I've taken it out)
Now you can bind a shortcut to this macro in VS - it uses a Linq OrderBy using the current culture's string comparer to sort the lines of the currently selected block of text. It should therefore group the attributes together accordingly.
If you need something that context-sensitive (i.e. the same attribute being called with different numbers of parameters) - then you'll need to do considerably more work.
You are the first person with that wish :)
I would arrange them manually, but also, if you are looking for a more solid solution, then I would implement a property in the TestPropertyAttribute class int Index { get; set; } and set the order in which I want them processed. In that case, you can control which how attributes are read in the reflection code that reads them.
This is how NHibernate does it.
[TestProperty(TC.Name, "Some Test", 0)]
[TestProperty(TC.Requirement, "req203", 1)]
Related
is there a way to convert C# code to any block representation and back?
Something like this:
int foo(int a){return a+1;}
to
{function:{name:"foo", return:"int", args:[{type:"int", name:"a"}], operations:[{type:"return", operations:[{type:"add", args:[{type:"vairable", value:"a"},{type:"const", value: 1}]}]}]}}
Does not have to be JSON, but I need it to be split to smallest parts.
UPDATE:
Lets say I generate a function that fills a structure based on a data from database:
public Person GetPerson(int id)
{
try { // <-- entire block added by user
using (var query = db.GetPerson(id))
{
return new Person(){
/*0*/name = query['name'], // /*#*/ is my mark of generated line
/*1*/age = query['age']
};
}
}
...
}
Assume that a user changed the line:
/*1*/age = query['age']
to /*1*/age = 10 - query['age'] for some reason.
Now the database column age is changed to years.
The new line should be /*1*/years = 10 - query['years']. The problem is that I need to keep the 10 - entered by the user.
If I had this code is JSON (or any graph) I could find the part that needs to be changed and only affect the nodes I genereated before keeping the excess.
This exmaple is trivial, but it can get complicated very quickly. Especially with quotes and brackets. This is the only approach I can see to work right now. Just hoped that tools for it already exists.
Is there a way to convert C# code to JSON
Sure. You can either make your json contain a string with your c# code and use runtime code generation to execute it, or encode dlls as base64 data and put it into your json. If you want to dig deeper you could probably extract the CIL code and make some custom JSON encoding of it. There are also expression trees, but I only think they allow encoding of expressions, not arbitrary code.
But in any case it is probably not a good idea to let the user customize code at that level. If you want to allow customization you should probably go for an actual plugin architecture. Or if you just want some customization of some simple mathematical expression, just store it as a string and write a simple parser to validate & evaluate it.
I have logfiles in the file-format of csv.
The vendor of the app it comes from wants to stick with it.
MY goal is to build a logreader that does some highlighting and filtering.
The CSV looks ugly as hell, because it is not at all tidy, even if you put some work into it in Excel. A sample of that structure is attached.
Now, i've been pondering about the solution of this a while and came up with a kinda complex solution. But i don't like that solution. It feels too "special"
But Let's look at what we have:
- The columns and their order are defined by a customizable view in the app.
So sometimes e.g. the date-column is first, could also be last, could be missing even. The output is not in the correct order. Sometimes the DAte-coulmn contains the Details-Text. Sometimes a MAC. It's madness.
Edit: Here is how it can look when it's raw data:
http://s000.tinyupload.com/?file_id=79649596476923658435
So what i came up with is this:
- read line1 of your csv, then you know the columns of that file.
- read a config-file that defines "detection-rules" for Columns with set names. So for instance you have a filter for a Colunm for Mac-addresses. One for IP-addresses and vice versa. The Columnns themself are predefined so this is at least possible.
so you read the CSV-line2 and spilt that string to an array, loop through and check for each string if that string matches one of your filters.
Then - if you have a match - you loop through the array you created with the Column-titles (you read line1 of the CSV)
The looping-instance increments a number up.
Then you have another array with the same size as the array containing the titles. You put the found value in there (the incremented number represents the index you use in the new array)
This all probably sounds hard to understand so i wrote a bit of code to illustrate how i meant it.
Am i missing something or is this the way to go with what i have ?
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim Line1csv() As String = {"Date", "MAC"}
Dim csvdata As String() = System.IO.File.ReadAllLines("tst.csv")
For Each csvline In csvdata
'lets split the line into pot. cell-values
Dim Csvlinestr() As String = csvline.Split(",")
'now lets loop through what we created and find out what it is
For Each csvfielstr In Csvlinestr
'ok lets ask what this field is
Dim Csvlinetype As String = AskmeWhatitis(csvfielstr)
'good lets say it told us that the result is title2
'so where do we put it ? lets loop through the line1csv to find out the indx
Dim found As Boolean = False
Dim Indx As Integer = 0
For Each l1csvstr In Line1csv
If l1csvstr = Csvlinetype Then
found = True
End If
Indx += 1
Next
If found = True Then
' here we'd put it into our "sorted array that has the same size as the line1csv-array()
' the index used is sthe indx-variable we used above
End If
Next
Next
End Sub
Public Function AskmeWhatitis(ByVal instr As String) As String
' this sub has a config-file that tells it what to search how and what the match will mean: does it mean its a MAC or date or whaterver
Return "truth"
End Function
End Class
I want to generate a .txt file when user clicks a button. The issue is I am not sure how to do it? I would like to have it so it uses this format:
00000012 <-- at the very start of the text file
2011 11 29 <-- Year 2011, Month 11, Day 29 (the year month and day based on my PC)
0010 or 0054 <-- this is a random number randomly one of these 2 numbers...
123456 <-- Time, Hour 12, Minutes 34, Seconds 56.
so when I open the text file it should be something like this:
00000012201111290054123456
I am new to C#. I was able to accomplish this on visual basic with this:
Public Class Form1
'declare new random object
Dim r As New Random
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'declare string + set initial value
Dim fileString As String = "00000012"
'add formatted date
fileString &= Now.ToString("yyyyMMdd")
'add random 0010 or 0054
fileString &= New String() {"0010", "0054"}(r.Next(0, 2))
'add random 6 digit number
fileString &= r.Next(0, 1000000).ToString("000000")
'write file
IO.File.WriteAllText("C:\Program Files\Code\maincode\maincode.txt", fileString)
End Sub
End Class
I have decided to change the last random 6 generation to the time instead. How to do it in C#?
The easiest way to solve your problem would be to use String.Format, which works pretty much the same way in VB.NET as it does in C#.
Before getting to that, though, it's worth addressing some issues with your VB.NET code; fixing these will make your C# code a lot better as well:
Your Random instance, r, is declared at the form (module) scope, but you only use it inside your Form1_Load procedure: it's a good idea to always keep your variable scope as limited as possible, in other words keep your declarations "as close to" the usage as you can;
While on the subject of System.Random: do keep in mind that this actually creates a quite predictable (time-based) series of numbers, so it shouldn't be used for anything security-related (that's what System.Security.Cryptography.RNGCryptoServiceProvider is for). Your usage of Random isn't very strong anyway, and I'm not sure of your actual use case, but this is always something to keep in mind;
You use comments that almost literally describe what your code does, which is rather pointless: only add comments to add additional insight into why your code does the things it does, not how;
Strings in .NET are immutable, which means that each time you use the & operator, you create a new string, leaving the old one to linger around until garbage collection kicks in. That gets really expensive after a while: instead, learn about Text.StringBuilder and use it whenever needed;
You create a file under C:\Program Files. Apart from the fact that that directory may have a different name on different versions of Windows (not to mention Linux, in case you're running on Mono), users don't have permission to write there on any non-legacy (i.e. post-XP) versions of Windows. So, if you do this, your program will crash when distributed: if you learn to use the proper file locations early on, that will save you lots of trouble.
Anyway, on to your question: whenever you want to create a string with lots of parameters and/or formatting, String.Format and its custom format strings comes in extremely handy. For example, your original VB.NET code can be rewritten as follows:
Sub Form1_Load(s As Object, e As EventArgs)
Using sw As New IO.StreamWriter(IO.Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"maincode.txt"))
sw.WriteLine("00000012{0:yyyyMMdd}{1}{0:HHmmss}", Now,
If((New Random).Next(0, 2) = 0, "0010", "0054"))
End Using
End Sub
When executed, this should create C:\Users\YourUserName\AppData\Roaming\maincode.txt (or somewhere else if you're on XP or certain localized versions of Windows: check the value of Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) in the debugger to find out).
Translating this to C# is trivial: if you use one of the many VB.NET to C# translators available online, it should give you something like this:
public void Form1_Load(object s, EventArgs e)
{
using (System.IO.StreamWriter sw = new System.IO.StreamWriter(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "maincode.txt"))) {
sw.WriteLine("00000012{0:yyyyMMdd}{1}{0:HHmmss}", DateAndTime.Now, (new Random()).Next(0, 2) == 0 ? "0010" : "0054");
}
}
Basically, it added some semicolons and curly braces for you, included fully-qualified namespaces and rewrote the ternary condition: the .NET Framework bits are identical.
Hope this helps, and good luck with your programming efforts!
Start with creating Text files with C# then go through this tabel or this one and rewrite your code in C# to build the information as described in your question.
I'm looking for some suggestions on better approaches to handling a scenario with reading a file in C#; the specific scenario is something that most people wouldn't be familiar with unless you are involved in health care, so I'm going to give a quick explanation first.
I work for a health plan, and we receive claims from doctors in several ways (EDI, paper, etc.). The paper form for standard medical claims is the "HCFA" or "CMS 1500" form. Some of our contracted doctors use software that allows their claims to be generated and saved in a HCFA "layout", but in a text file (so, you could think of it like being the paper form, but without the background/boxes/etc). I've attached an image of a dummy claim file that shows what this would look like.
The claim information is currently extracted from the text files and converted to XML. The whole process works ok, but I'd like to make it better and easier to maintain. There is one major challenge that applies to the scenario: each doctor's office may submit these text files to us in slightly different layouts. Meaning, Doctor A might have the patient's name on line 10, starting at character 3, while Doctor B might send a file where the name starts on line 11 at character 4, and so on. Yes, what we should be doing is enforcing a standard layout that must be adhered to by any doctors that wish to submit in this manner. However, management said that we (the developers) had to handle the different possibilities ourselves and that we may not ask them to do anything special, as they want to maintain good relationships.
Currently, there is a "mapping table" set up with one row for each different doctor's office. The table has columns for each field (e.g. patient name, Member ID number, date of birth etc). Each of these gets a value based on the first file that we received from the doctor (we manually set up the map). So, the column PATIENT_NAME might be defined in the mapping table as "10,3,25" meaning that the name starts on line 10, at character 3, and can be up to 25 characters long. This has been a painful process, both in terms of (a) creating the map for each doctor - it is tedious, and (b) maintainability, as they sometimes suddenly change their layout and then we have to remap the whole thing for that doctor.
The file is read in, line by line, and each line added to a
List<string>
Once this is done, we do the following, where we get the map data and read through the list of file lines and get the field values (recall that each mapped field is a value like "10,3,25" (without the quotes)):
ClaimMap M = ClaimMap.GetMapForDoctor(17);
List<HCFA_Claim> ClaimSet = new List<HCFA_Claim>();
foreach (List<string> cl in Claims) //Claims is List<List<string>>, where we have a List<string> for each claim in the text file (it can have more than one, and the file is split up into separate claims earlier in the process)
{
HCFA_Claim c = new HCFA_Claim();
c.Patient = new Patient();
c.Patient.FullName = cl[Int32.Parse(M.Name.Split(',')[0]) - 1].Substring(Int32.Parse(M.Name.Split(',')[1]) - 1, Int32.Parse(M.Name.Split(',')[2])).Trim();
//...and so on...
ClaimSet.Add(c);
}
Sorry this is so long...but I felt that some background/explanation was necessary. Are there any better/more creative ways of doing something like this?
Given the lack of standardization, I think your current solution although not ideal may be the best you can do. Given this situation, I would at least isolate concerns e.g. file read, file parsing, file conversion to standard xml, mapping table access etc. to simple components employing obvious patterns e.g. DI, strategies, factories, repositories etc. where needed to decouple the system from the underlying dependency on the mapping table and current parsing algorithms.
You need to work on the DRY (Don't Repeat Yourself) principle by separating concerns.
For example, the code you posted appears to have an explicit knowledge of:
how to parse the claim map, and
how to use the claim map to parse a list of claims.
So there are at least two responsibilities directly relegated to this one method. I'd recommend changing your ClaimMap class to be more representative of what it's actually supposed to represent:
public class ClaimMap
{
public ClaimMapField Name{get;set;}
...
}
public class ClaimMapField
{
public int StartingLine{get;set;}
// I would have the parser subtract one when creating this, to make it 0-based.
public int StartingCharacter{get;set;}
public int MaxLength{get;set;}
}
Note that the ClaimMapField represents in code what you spent considerable time explaining in English. This reduces the need for lengthy documentation. Now all the M.Name.Split calls can actually be consolidated into a single method that knows how to create ClaimMapFields out of the original text file. If you ever need to change the way your ClaimMaps are represented in the text file, you only have to change one point in code.
Now your code could look more like this:
c.Patient.FullName = cl[map.Name.StartingLine].Substring(map.Name.StartingCharacter, map.Name.MaxLength).Trim();
c.Patient.Address = cl[map.Address.StartingLine].Substring(map.Address.StartingCharacter, map.Address.MaxLength).Trim();
...
But wait, there's more! Any time you see repetition in your code, that's a code smell. Why not extract out a method here:
public string ParseMapField(ClaimMapField field, List<string> claim)
{
return claim[field.StartingLine].Substring(field.StartingCharacter, field.MaxLength).Trim();
}
Now your code can look more like this:
HCFA_Claim c = new HCFA_Claim
{
Patient = new Patient
{
FullName = ParseMapField(map.Name, cl),
Address = ParseMapField(map.Address, cl),
}
};
By breaking the code up into smaller logical pieces, you can see how each piece becomes very easy to understand and validate visually. You greatly reduce the risk of copy/paste errors, and when there is a bug or a new requirement, you typically only have to change one place in code instead of every line.
If you are only getting unstructured text, you have to parse it. If the text content changes you have to fix your parser. There's no way around this. You could probably find a 3rd party application to do some kind of visual parsing where you highlight the string of text you want and it does all the substring'ing for you but still unstructured text == parsing == fragile. A visual parser would at least make it easier to see mistakes/changed layouts and fix them.
As for parsing it yourself, I'm not sure about the line-by-line approach. What if something you're looking for spans multiple lines? You could bring the whole thing in a single string and use IndexOf to substring that with different indices for each piece of data you're looking for.
You could always use RegEx instead of Substring if you know how to do that.
While the basic approach your taking seems appropriate for your situation, there are definitely ways you could clean up the code to make it easier to read and maintain. By separating out the functionality that you're doing all within your main loop, you could change this:
c.Patient.FullName = cl[Int32.Parse(M.Name.Split(',')[0]) - 1].Substring(Int32.Parse(M.Name.Split(',')[1]) - 1, Int32.Parse(M.Name.Split(',')[2])).Trim();
to something like this:
var parser = new FormParser(cl, M);
c.PatientFullName = FormParser.GetName();
c.PatientAddress = FormParser.GetAddress();
// etc
So, in your new class, FormParser, you pass the List that represents your form and the claim map for the provider into the constructor. You then have a getter for each property on the form. Inside that getter, you perform your parsing/substring logic like you're doing now. Like I said, you're not really changing the method by which your doing it, but it certainly would be easier to read and maintain and might reduce your overall stress level.
I'm writing an application with Watin. Its great, but running a performance analysis on my program, over 50% of execution time is spent looping through lists of elements.
For example:
foreach (TextField bT in browser.TextFields)
{
Is very slow.
I seem to remember seeing somewhere there is a faster way of doing this in WatiN, but unfortunately I can't find the page again.
Accessing the number of elements also seems to be slow, eg;
browser.CheckBoxes.Count
Thanks for any tips,
Chris
I think I could answer you better if I had a better idea of what you were trying to do, but I can share some observations on what I've learned with WatiN so far.
The more specific your selectors are, the faster things will go. Avoid using "browser.Elements" as that is really generic. I'm not sure that it saves much, but doing something like browser.Body.Elements throws the header elements out of the scope of things to check and may save a few calculations.
When I say "scope", consider that WatiN always starts with the entire DOM. Can you think of ways to limit the scope of elements perhaps to the text fields within the main div on your page? WatiN returns Elements and ElementCollections, each of which may have its own ElementCollection. That div probably has a specific ID, so you can do something like
var textFields = ie.Div("divId").TextFields;
Look for opportunities to be more specific, and you can use LINQ to describe what you want more clearly. For example, can you write something like:
ie.Body.TextFields.
Where(tf => !string.IsNullOrWhiteSpace(tf.ClassName) && tf.ClassName.Contains("classname")).ToList().
Foreach(tf => tf.Value = "Your Text");
I would refine that further by reducing the number of times I scan the collection by doing something like:
ie.Body.TextFields.ToList().
Foreach(tf => {
if(!string.IsNullOrWhiteSpace(tf.ClassName) && tf.ClassName.Contains("classname")) {
tf => tf.Value = "Your Text"
}
});
The "Find.By*" specifiers also help WatiN operate on the collections you want faster and are a more elegant short-hand for what I wrote above:
ie.Body.TextFields.Filter(Find.ByClass("class")).ToList().ForEach(tf => tf.Value = "Your Text");
And as a last piece of advice, this project lets you find elements using jQuery/CSS style selectors.
So, tl;dr: Narrow down the scope of what you're looking for, and be specific.
Hope that helps. I'm looking for ways to speed up my own tests.
If you really need to iterate through all text fields, there is no other way. As #Xaqron pointed out, it depends on IE. But maybe you just need to iterate through text fields of eg. specified <div/>? Finding it first, and then iterating through it's text fields would be faster.
Thanks Dahv for a really detailed answer. In my case I've sped up my tests by about 10x using a number of tricks, some similar to yours:
Refining scope as you and prostynick (in my case using Form1.TextField etc.)
First checking if browser.html matches my regex before seeing if
fields do
Using the GehSoft.PRCE RegEx wrapper - its native code regex
matching is far faster than .NET's for small haystacks. So to find a TextField I'd do:
Gehtsoft.PCRE.Regex regexString = new Gehtsoft.PCRE.Regex("[Nn]ame");
foreach (TextField bT in browser.TextFields)
{
//Skip if no match
if (!regexString.Execute(bT.Name).Success) continue;
Before I was looping on a list of regexes, then inside that i was looping on TextFields. Making the TextFields loop the top loop improved speed about 3x.