I have a test script that does something to one object then the same thing to a second object. This continues for quite a while. With so much predictable repetition that it seems ripe for automation but I can't figure out how. I wouldn't care so much except with so much repetition, it's easy to overlook using the wrong variable (ie: stagingXyz when prodXyz was intended.)
The details below are irrelevant. What's important is the pattern.
var stagingDbs = cleanupDbs(stagingServer.Databases);
var prodDbs = cleanupDbs(prodServer.Databases);
printDiff(stagingDbs, prodDbs, "Databases mis-matched");
foreach (var db in stagingDbs.Intersect(prodDbs)) {
var stagingDb = stagingServer.Databases[db];
var prodDb = prodServer.Databases[db];
var stagingTables = cleanupTables(stagingDb.Tables);
var prodTables = cleanupTables(prodDb.Tables);
printDiff(stagingTables, prodTables, "Tables mis-matched on " + db);
foreach (var table in stagingTables.Intersect(prodTables)) {
var stagingTable = stagingDb.Tables[table];
var prodTable = prodDb.Tables[table];
var matchedColumns = stagingColumns.Intersect(prodColumns);
var stagingTableColumns = stagingTable.Columns
.Cast<Column>()
.Where(c => matchedColumns.Contains(c.Name))
.Select(c => formatColumn(c));
var prodTableColumns = prodTable.Columns
.Cast<Column>()
.Where(c => matchedColumns.Contains(c.Name))
.Select(c => formatColumn(c));
printDiff(stagingTableColumns, prodTableColumns,
"Columns mis-matched");
}
}
I don't want to go through, for instance, replacing this
var stagingTableColumns = stagingTable.Columns
.Cast<Column>()
.Where(c => matchedColumns.Contains(c.Name))
.Select(c => formatColumn(c));
var prodTableColumns = prodTable.Columns
.Cast<Column>()
.Where(c => matchedColumns.Contains(c.Name))
.Select(c => formatColumn(c));
with this
var stagingTableColumns = doStuff(stagingTable, matchedColumns);
var prodTableColumns = doStuff(prodTable, matchedColumns);
because I have to make sure everything in the 1st line is stagingXyz and the 2nd line is prodXyz. Not so bad for 1 line but the test script is huge and only ever does one of these 2 things:
foo(stagingXyz); foo(prodXyz);
bar(stagingXyz, prodXyz);
Similarly, wrapping with these items in an array and having doStuff[0]; doStuff[1]; is subject to the same easy typo error only a typo with 0 vs. 1 will be even harder to spot at a glance.
I thought about making 2 container objects (one for staging, one for prod) and putting these 2 objects in a collection but I fear this will lead to a bazillion tiny loops that will be very hard to maintain.
Is there anyway to simplify this and still have it be readable and maintainable?
Could you generate your test script? The input might read something like
var %%AB%%Dbs = cleanupDbs(%%AB%%Server.Databases);
printDiff(%%A%%Dbs, %%B%%Dbs, "Databases mis-matched");
foreach (var db in %%A%%Dbs.Intersect(%%B%%Dbs)) {
var %%AB%%Db = %%AB%%Server.Databases[db];
var %%AB%%Tables = cleanupTables(%%AB%%Db.Tables);
printDiff(%%A%%Tables, %%B%%Tables, "Tables mis-matched on " + db);
...
}
A line containing %%AB%% might expand to two copies of the same line, one with the "A" replacement and one with the "B" replacement, where %%A%% or %%B%% by itself might just get replaced.
Edit - After reading your comments I see the problem a bit clearer now. I think the problem is more the clarity of the one big function vs. coming up with a funky way to solve the readability problem. I think the more you broke it up into smaller functions, the clearer it would get.
If the main function was broken up into something like this:
public void mainMethod(DB prodDB, DB stagingDB)
{
doPart1(prodDB, stagingDB);
doPart2(prodDB, stagingDB);
}
...and each part had well named inputs like so:
public void doPart1(DB prodDB, DB stagingDB)
{
// Code...
}
Things would clear themselves up as you made things work at a more and more granular level. Anyone working in the doPart1 method only has to be concerned with it's small amount of code, and anyone working in the main section shouldn't have a million things to look over. I understand if this may sound like an oversimplified response, but it sounds like you're trying to solve a problem that shouldn't exist if the code is properly broken up.
If there is a method that is so huge and unreadable that another developer couldn't figure out what's going on with only TWO variables, then there is a different problem.
Related
im trying to deploy a simple search function that uses a simple tag system, probably there are better ways to bt this is the solution i landed on:
public async Task<ActionResult<IEnumerable<t_usuarios_pub>>> Gett_usuarios_pubByTag(string tag)
{
string[] TagList;
TagList = tag.Split(',');
List<t_usuarios_pub> results = new List<t_usuarios_pub>();
var pubs = from m in _context.t_usuarios_pub select m;
if (!String.IsNullOrEmpty(tag))
{
foreach (var Itag in TagList)
{
pubs = pubs.Where(s => (s.tag.Contains(Itag) && s.estatus<2)).OrderBy(x => x.estatus);
foreach(var x in pubs)
{
if (!results.Contains(x))
{
results.Add(x);
}
}
}
}
return await results.AsQueryable().ToListAsync();
}
problem is that the List results cant be converted to IQueryable, this is the error stack.
Any idea of how i can properly implement it ?
System.InvalidOperationException:
The source 'IQueryable' doesn't implement 'IAsyncEnumerable<UserPostAPI.Models.t_usuarios_pub>'.
Only sources that implement 'IAsyncEnumerable' can be used for Entity Framework asynchronous operations.
ยดยดยด
Since results is a local variable of List<> type, I don't believe you need to await the last line. It might just work with:
return results.AsQueryable();
In which case, your method may not even need to be an async method. Or, depending on what _context is, perhaps the await needs to be on the pubs filter call:
pubs = await pubs.Where(s => (s.tag.Contains(Itag) && s.estatus<2))
.OrderBy(x => x.estatus)
.ToListAsync();
Furthermore, since your method says it returns an IEnumerable<>, you probably don't need the .AsQueryable(), either:
return result;
There's also a lot of refactoring you could do -- here are just a few things you may want to consider:
Move the String.IsNullOrEmpty(tag) check to the beginning of the method as a guard.
If this is user input, trim the split tags in TagList to avoid any issues with extra whitespace, and then be sure to remove any resulting empty members.
You could put the estatus < 2 check in your query to reduce having to re-check it again for every item in pubs for every ITag.
I can do this in GitBash:
$ git diff --name-only v01...HEAD -- *.sql
which gives:
Components/1/Database/Stored Procedures/spDC1.sql
Components/1/Database/Stored Procedures/spDC2.sql
I can't see how I would do this in LibGit2Sharp.
Any ideas?
Thanks
Here is an example from one of my projects that get a ICommitLog collection between two commits (current HEAD vs. the master branch):
// git log HEAD..master --reverse
public ICommitLog StalkerList {
get {
var filter = new CommitFilter {
SortBy = CommitSortStrategies.Reverse | CommitSortStrategies.Time,
Since = master,
Until = head.Tip,
};
return repo.Commits.QueryBy (filter);
}
}
Once you have your ICommitLog collection of all the commits within the range that you need, you can cycle through each commit to get a list of the files that were effected within that commit (of course you would need to add filtering of the filename via your "*.sql" requirements):
public String[] FilesToMerge (Commit commit)
{
var fileList = new List<String> ();
foreach (var parent in commit.Parents) {
foreach (TreeEntryChanges change in repo.Diff.Compare<TreeChanges>(parent.Tree, commit.Tree)) {
fileList.Add (change.Path);
}
}
return fileList.ToArray ();
}
I think SushiHangover's answer to this is pretty correct. Just a couple of amendments / updates. (P.S. Yeah I know this question's relatively old, but a complete answer would have helped me if I'd found it today, so putting one here for future peeps).
This bit should be an amendment comment, but I can't comment yet (low rep):
First, I think that in Sushi's example, master and head.Tip are the wrong way around. Until excludes a commit from the results and excludes its ancestors. So if you put head.Tip in there it'll exclude basically the entire history tree.
So AFAIK it should read more like this:
// git log HEAD..master --reverse
public ICommitLog StalkerList {
get {
var filter = new CommitFilter {
SortBy = CommitSortStrategies.Reverse | CommitSortStrategies.Time,
Since = head.Tip,
Until = master
};
return repo.Commits.QueryBy (filter);
}
}
It's also SUPER important to realise that the order you give them in matters. If you just switch them around you'll get nothing back.
(Also note that Sushi's original example had a bad ',' after "head.Tip".
This bit's the update:
Also worth noting that the libgit2sharp library has been updated recently. Replacing "Since" and "Until" with "IncludeReachableFrom" and "ExcludeReachableFrom" respectively.
The names aren't particularly helpful until you realise that they're just way more verbose about what they're doing.
The comment for Exclude, for example, reads:
/// A pointer to a commit object or a list of pointers which will be excluded (along with ancestors) from the enumeration.
So the latest implementation would look more like:
using (Repository r = new Repository(#"[repo_location]"))
{
CommitFilter cf = new CommitFilter
{
SortBy = CommitSortStrategies.Reverse | CommitSortStrategies.Time,
ExcludeReachableFrom = r.Branches["master"].Tip,
IncludeReachableFrom = r.Head.Tip
};
var results = r.Commits.QueryBy(cf);
foreach (var result in results)
{
//Process commits here.
}
}
Tested this out in LINQPad and it seems to work. Might have missed something though as it was a quick draft. If so let me know.
Things to note: master and Head are actually properties of a Repository, I couldn't see that coming from anywhere in the old example but it may just be a version difference with the old version.
(ASP.NET MVC4 on Visual Studio 12 using Identity Framework)
Please, I wish someone could explain this to me.
This is a second case. The first one is described under Edit 1
I am getting a weird situation.
Edit 2 (new)
In my initialization file, after all the initializing is done and right before committing the changes to DataContext, each of the Student objects has Courses.Count() greater than 0, i.e. 5 or 4
bob.Courses.Count();
mary.Courses.Count();
wei.Courses.Count();
john.Courses.Count();
jack.Courses.Count();
jill.Courses.Count();
dc.SaveChanges();
Moments later, however, when I am trying to query them like this ...
var studentsCount = dc.Students.Count();
foreach (var st in dc.Students.ToList()) {
var coursesCount = st.Courses.Count(); // THIS LINE!!!
foreach (var crs in st.Courses.ToList()) {
if (crs.CourseId == cancellation.CourseBase.CourseId) {
var user = manager.FindByName(st.UserName);
cancellation.Users.Add(user);
}
}
}
...the line var coursesCount = st.Courses.Count(); is returning 0!
I have no idea why. Do the values get lost somewhere in between?
Edit 1
I initialize my Course object by hard-coding the values.
When I am debugging, I can see all the fields in the Course object have values, and no value is null.
However,
When I query like this:
var courses = dc.Courses.ToList().Where(course => course.User.Id ==currentUserId);
*
and debug, I see that one of the fields is suddenly null.
It looks like the value gets lost somewhere in between the initialization and retrieval!
Has anyone come across this before?
Tell me if you need to see more code.
Hy,
I have a csv file like that
user_name1,c:\photo\user_photo.jpg,0,0,0,0
in which every line refers a distinct object with own fileds separated from comma.
How I find a particular object knowing the user name? I use distinct user_names. And after that how I make that object, curent object that I use?
What i have done until now:
StreamReader sc = new StreamReader(#"C:\Player.csv");
String linie = sc.ReadLine();
while (!String.IsNullOrEmpty(linie))
{
string[] stringu = linie.Split(',');
Player player = new Player(stringu[0], stringu[1], int.Parse(stringu[2]), int.Parse(stringu[3]), int.Parse(stringu[4]));
players.Add(player);
linie = sc.ReadLine();
}
sc.Close();
var query = players.Where(a => a.Name == label6.Text);
}
Sincerly,
You could try to use a library, which makes easy the use of CSV files with LINQ queries. Please look here.
Firstly I'd suggest that for parsing a CSV file you don't roll your own field splitter. I'd suggest using something like the TextFieldParser (which can be used in C# by referencing Microsoft.VisualBasic).
Once you've created the parser you can use it to get the array of strings for each record represented by line in your application:
List<Players> players = new List<Players>();
var parser = new TextFieldParser();
while ( !parser.EOF )
{
string [] playerFields = parser.ReadFields();
// Create player from fields
var player = Player.FromFields(playerFields);
players.Add(player);
}
Now it really depends on whether you want to continuously query the players in the file, as if you do then getting an in-memory copy and using LINQ makes sense, else if you only want to query once then I'd simply do the check line by line.
Assuming that you do want to query multiple times then parsing the file and holding the values in a List or similar makes sense (assuming the file isn't ridiculously big).
Finally, if you has distinct user names then you could use the FirstOrDefault method in LINQ to give you the single player back that matches.
var player = players.FirstOrDefault(p => p.Name.Equals(textBox.Text));
Or if you know that you have unique player names you could just store the whole lot in a dictionary....?
if ( players.ContainsKey(textBox.Text) )
{
var player = players[textBox.Text];
}
Anyway, just some thoughts.
The only problem you have at the moment, as far as I can see, it that you say you are looking to fetch "a particular object", but in your last line you are using Where which is going to return an IEnumerable containing one, none or many such objects. If you want to fetch one uniquely named object you should use Single or SingleOrDefault, e.g.
var myPlayer = players.Single(x => x.Name == label6.Text);
The answer you have accepted has merely converted your query from method syntax to the equivalent query syntax and introduced a compilation error by attempting to convert the resulting IEnumerable<Player> to a Player.
In anticipation of a problem you may be about to have, you might also want to look into Microsoft.VisualBasic.FileIO.TextFieldParser (you'll need to add a reference to Microsoft.VisualBasic.dll), which will allow you to parse a CSV in a more robust way, i.e. handle fields containing commas etc., using something like the following:
using (var parser = new TextFieldParser(filename))
{
parser.SetDelimiters(",");
while (!parser.EndOfData)
{
var p = parser.ReadFields();
players.Add(new Player(p[0], p[1], int.Parse(p[2]), int.Parse(p[3]), int.Parse(p[4])));
}
}
Have you tried something like
Player selected_player = from pl in players
where pl.Name == label6.Text
select pl;
?
I use this to get a list of materials from my database....
public IQueryable<MaterialsObj> FindAllMaterials()
{
var materials = from m in db.Materials
join Mt in db.MeasurementTypes on m.MeasurementTypeId equals Mt.Id
select new MaterialsObj()
{
Id = Convert.ToInt64(m.Mat_id),
Mat_Name = m.Mat_Name,
Mes_Name = Mt.Name,
};
return materials;
}
But i have seen in an example that has this,
public IQueryable<MaterialsObj> FindAllMaterials()
{
return from m in db.Materials
join Mt in db.MeasurementTypes on m.MeasurementTypeId equals Mt.Id
select new MaterialsObj()
{
Id = Convert.ToInt64(m.Mat_id),
Mat_Name = m.Mat_Name,
Mes_Name = Mt.Name,
};
}
Is there a real big difference between the two methods... Assigning my linq query to a variable and returning it... Is it a good/bad practise? Any suggestion which should i use?
No real difference. In a release / optimized build I would expect the compiler to remove the extra local, anyway. Having the variable is useful if you want to put a breakpoint in and inspect the value prior to return, or if you want to apply additional conditional filtering, for example:
if(applySort) { materials = materials.OrderBy(x => x.Name); }
In your example it doesn't add anything, but it also doesn't cost anything either. Feel free to keep it there; especially if you think it makes the code easier to read.
There's no difference, but usually I use the 1st version which makes it easier to set a watch or breakpoint in visual studio if I want to look at the data before it's returned.