Analyzer creating multiple diagnostics for compilation unit - roslyn-code-analysis

I am writing a Roslyn Diagnostic to turn on/off option strict. Since there can only be one per file, I am using the compilation for the node to be examined:
context.RegisterSyntaxNodeAction(CompilationUnitCheck, SyntaxKind.CompilationUnit);
I am seeing multiple diagnostics displayed in the error list pane when running the development hive, at times as many as 3, but always at least 2 per file. They show the same location. What could be causing this, and what can I do to fix it?
private void CompilationUnitCheck(SyntaxNodeAnalysisContext context)
{
var orgRoot = (CompilationUnitSyntax) context.Node;
var fileName = System.IO.Path.GetFileNameWithoutExtension(orgRoot.SyntaxTree.FilePath) ;
if ((fileName?.EndsWith("designer", StringComparison.CurrentCultureIgnoreCase)).GetValueOrDefault() ||
"Reference".Equals(fileName, StringComparison.CurrentCultureIgnoreCase))
{
return;
}
if (fileName != "TestFile") return;
var newErrors = fileName == "TestFile";
var location = orgRoot.GetLocation();
string strictMsg = null ?? "Off";
var diagnostic = Diagnostic.Create(Rule, location, strictMsg);
context.ReportDiagnostic(diagnostic);
}
My current workaround is to get the hashcode of the root, and store that in a static list, if it's in the list, I don't check again.

Related

Entity Core and SaveChanges works only once

I have problem with Entity Core 3.1.2.
I have code like this:
SQL.Database.EnsureCreated();
var ThisCollector = SQL.CollectorServers
.Where(esa => esa.ServerName == ServerCollectorName)
.FirstOrDefault();
while (foo)
{
await SQL.Entry(ThisCollector)
.ReloadAsync();
DateTime dtTimeOut = DateTime.UtcNow.AddMinutes(-1);
//check status of current Worker
var CurrentLB = SQL.CollectorServers
.Where(esa => esa.isWorker == true
&& esa.LastSeenLB < dtTimeOut)
.FirstOrDefault();
if (CurrentLB!=null) //Current Worker is dead!
{
CurrentLB.isWorker = false;
ThisCollector.isWorker = true;
SQL.SaveChanges(); //This works allways
}
var Collectors = SQL.CollectorServers
.Where(Esa => Esa.isWorker == true);
if (Collectors.Count() == 0)
{
ThisCollector.isWorker = true;
SQL.SaveChanges();
}
if (Collectors.Count() >= 2)
{
foreach(CollectorServer cs in Collectors)
{
cs.isWorker = false;
//why this is requied?
SQL.Entry(cs).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
}
ThisCollector.isWorker = true;
//<--This works only once, without manually setting State to
// modified!!! Why? Values has been changed from external program.
//(Management studio in this case)
SQL.SaveChanges();
}
await Task.Delay(10000, stoppingToken);
}
Problem is that last SaveChanges works only first time it has been called without I set Entry state to Modified. After that it does not make SQL query (I can see that in SQL Profiler).
In this case this can be fixed by this way, but I'm trying to undestand why this happends. My software saves lot data to SQL, and I need to know can I trust to this code without opening all queries and adding this modified state.
I didin't have this kind of problems in full version (6) of Entity, this is something quite new for me.
The described behavior would make sense if the entities returned by CollectorServers are not tracked.
ThisCollector is attached on every loop by the call to SQL.Entry(ThisCollector) :
await SQL.Entry(ThisCollector) //Attaching
.ReloadAsync(); //Reloading
Any changes made to it would be tracked and saved by the first call to DbContext.SaveChanges().
On the other hand, the entities returned by the Collectors query :
var Collectors = SQL.CollectorServers
.Where(Esa => Esa.isWorker == true);
Would remain untracked, until they get reattached by the call to SQL.Entry(cs) :
foreach(CollectorServer cs in Collectors)
{
cs.isWorker = false;
SQL.Entry(cs).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
}
That's equivalent to calling DbContext.Update to attach and set the state to Modified. Update is easier to read though :
foreach(CollectorServer cs in Collectors)
{
cs.isWorker = false;
SQL.Update(cs);
}

get value for specific question/item in a Google Form using Google App Script in an on submit event

I have figured out how to run a Google App Script project/function on a form submit using the information at https://developers.google.com/apps-script/guides/triggers/events#form-submit_4.
Once I have e I can call e.response to get a FormResponse object and then call getItemResponses() to get an array of all of the responses.
Without iterating through the array and checking each one, is there a way to find the ItemResponse for a specific question?
I see getResponseForItem(item) but it looks like I have to somehow create an Item first?
Can I some how use e.source to get the Form object and then find the Item by question, without iterating through all of them, so I could get the Item object I can use with getResponseForItem(item)?
This is the code I use to pull the current set of answers into a object, so the most current response for the question Your Name becomes form.yourName which I found to be the easiest way to find responses by question:
function objectifyForm() {
//Makes the form info into an object
var myform = FormApp.getActiveForm();
var formResponses = myform.getResponses()
var currentResponse = formResponses[formResponses.length-1];
var responseArray = currentResponse.getItemResponses()
var form = {};
form.user = currentResponse.getRespondentEmail(); //requires collect email addresses to be turned on or is undefined.
form.timestamp = currentResponse.getTimestamp();
form.formName = myform.getTitle();
for (var i = 0; i < responseArray.length; i++){
var response = responseArray[i].getResponse();
var item = responseArray[i].getItem().getTitle();
var item = camelize(item);
form[item] = response;
}
return form;
}
function camelize(str) {
str = str.replace(/[\.,-\/#!$%\^&\*;:{}=\-_`~()#\+\?><\[\]\+]/g, '')
return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function(match, index) {
if (+match === 0) return ""; // or if (/\s+/.test(match)) for white spaces
return index == 0 ? match.toLowerCase() : match.toUpperCase();
});
}
//Use with installable trigger
function onSubmittedForm() {
var form = objectifyForm();
Logger.log(form);
//Put Code here
}
A couple of important things.
If you change the question on the form, you will need to update your
code
Non required questions may or may not have answers, so check if answer exists before you use it
I only use installable triggers, so I know it works with those. Not sure about with simple triggers
You can see the form object by opening the logs, which is useful for finding the object names

Google form that turns on and off each day automatically

I love Google Forms I can play with them for hours. I have spent days trying to solve this one, searching for an answer. It is very much over my head. I have seen similar questions but none that seemed to have helped me get to an answer. We have a café where I work and I created a pre-order form on Google Forms. That was the easy part. The Café can only accept pre-orders up to 10:30am. I want the form to open at 7am and close at 10:30am everyday to stop people pre ordering when the café isn't able to deal with their order. I used the very helpful tutorial from http://labnol.org/?p=20707 to start me off I have added and messed it up and managed to get back to the below which is currently how it looks. It doesn't work and I can't get my head around it. At one point I managed to turn it off but I couldn't turn it back on!! I'm finding it very frustrating and any help in solving this would be amazing. To me it seems very simple as it just needs to turn on and off at a certain time every day. I don't know! Please help me someone?
FORM_OPEN_DATE = "7:00";
FORM_CLOSE_DATE = "10:30";
RESPONSE_COUNT = "";
/* Initialize the form, setup time based triggers */
function Initialize() {
deleteTriggers_();
if ((FORM_OPEN_DATE !== "7:00") &&
((new Date()).getTime("7:00") < parseDate_(FORM_OPEN_DATE).getTime ("7:00"))) {
closeForm("10:30");
ScriptApp.newTrigger("openForm")
.timeBased("7:00")
.at(parseDate_(FORM_OPEN_DATE))
.create(); }
if (FORM_CLOSE_DATE !== "10:30") {
ScriptApp.newTrigger("closeForm")
.timeBased("10:30")
.at(parseDate_(FORM_CLOSE_DATE))
.create(); }
if (RESPONSE_COUNT !== "") {
ScriptApp.newTrigger("checkLimit")
.forForm(FormApp.getActiveForm())
.onFormSubmit()
.create(); } }
/* Delete all existing Script Triggers */
function deleteTriggers_() {
var triggers = ScriptApp.getProjectTriggers();
for (var i in triggers) {
ScriptApp.deleteTrigger(triggers[i]);
}
}
/* Allow Google Form to Accept Responses */
function openForm() {
var form = FormApp.getActiveForm();
form.setAcceptingResponses(true);
informUser_("Your Google Form is now accepting responses");
}
/* Close the Google Form, Stop Accepting Reponses */
function closeForm() {
var form = FormApp.getActiveForm();
form.setAcceptingResponses(false);
deleteTriggers_();
informUser_("Your Google Form is no longer accepting responses");
}
/* If Total # of Form Responses >= Limit, Close Form */
function checkLimit() {
if (FormApp.getActiveForm().getResponses().length >= RESPONSE_COUNT ) {
closeForm();
}
}
/* Parse the Date for creating Time-Based Triggers */
function parseDate_(d) {
return new Date(d.substr(0,4), d.substr(5,2)-1,
d.substr(8,2), d.substr(11,2), d.substr(14,2));
}
I don't think you can use .timebased('7:00'); And it is good to check that you don't have a trigger before you try creating a new one so I like to do this. You can only specify that you want a trigger at a certain hour like say 7. The trigger will be randomly selected somewhere between 7 and 8. So you really can't pick 10:30 either. It has to be either 10 or 11. If you want more precision you may have to trigger your daily triggers early and then count some 5 minute triggers to get you closer to the mark. You'll have to wait to see where the daily triggers are placed in the hour first. Once they're set they don't change.
I've actually played around with the daily timers in a log by creating new ones until I get one that close enough to my desired time and then I turn the others off and keep that one. You have to be patient. As long as you id the trigger by the function name in the log you can change the function and keep the timer going.
Oh and I generally created the log file with drive notepad and then open it up whenever I want to view the log.
function formsOnOff()
{
if(!isTrigger('openForm'))
{
ScriptApp.newTrigger('openForm').timeBased().atHour(7).create()
}
if(!isTrigger('closeForm'))
{
ScriptApp.newTrigger('closeForm').timeBased().atHour(11)
}
}
function isTrigger(funcName)
{
var r=false;
if(funcName)
{
var allTriggers=ScriptApp.getProjectTriggers();
var allHandlers=[];
for(var i=0;i<allTriggers.length;i++)
{
allHandlers.push(allTriggers[i].getHandlerFunction());
}
if(allHandlers.indexOf(funcName)>-1)
{
r=true;
}
}
return r;
}
I sometimes run a log entry on my timers so that I can figure out exactly when they're happening.
function logEntry(entry,file)
{
var file = (typeof(file) != 'undefined')?file:'eventlog.txt';
var entry = (typeof(entry) != 'undefined')?entry:'No entry string provided.';
if(entry)
{
var ts = Utilities.formatDate(new Date(), "GMT-6", "yyyy-MM-dd' 'hh:mm:ss a");
var s = ts + ' - ' + entry + '\n';
myUtilities.saveFile(s, file, true);//this is part of a library that I created. But any save file function will do as long as your appending.
}
}
This is my utilities save file function. You have to provide defaultfilename and datafolderid.
function saveFile(datstr,filename,append)
{
var append = (typeof(append) !== 'undefined')? append : false;
var filename = (typeof(filename) !== 'undefined')? filename : DefaultFileName;
var datstr = (typeof(datstr) !== 'undefined')? datstr : '';
var folderID = (typeof(folderID) !== 'undefined')? folderID : DataFolderID;
var fldr = DriveApp.getFolderById(folderID);
var file = fldr.getFilesByName(filename);
var targetFound = false;
while(file.hasNext())
{
var fi = file.next();
var target = fi.getName();
if(target == filename)
{
if(append)
{
datstr = fi.getBlob().getDataAsString() + datstr;
}
targetFound = true;
fi.setContent(datstr);
}
}
if(!targetFound)
{
var create = fldr.createFile(filename, datstr);
if(create)
{
targetFound = true;
}
}
return targetFound;
}

Simple.Data Select specific columns

I have a call made to Simple.Data where I want to limit the columns that are being brought back. However I am hitting problems..
This works fine:
var db = Database.Open();
var questionIdRow = db.Question.FindByFriendlyId(friendlyId);
if (questionIdRow == null) return Guid.Empty;
return questionIdRow.QuestionId;
However, the following doesn't work (I get a Simple.Data.UnresolvableObjectException 'Column not found')
var db = Database.Open();
var questionIdRow = db.Question.FindByFriendlyId(friendlyId)
.Select(db.Question.QuestionId);
if (questionIdRow == null) return Guid.Empty;
return questionIdRow.QuestionId;
I was under the impression from the Simple.Data documentation that this was all that I needed to do to limit the selected columns. Note that the selection is simply selecting the same column that is referenced later on.
The actual exception is thrown on the var questionIdRow = line.
Can anybody give me some guidance?
this is a common problem, and has actually led to FindBy being deprecated before we even get to 1.0. The problem is that FindBy returns a record straight away, so you can't continue to call query methods on it.
The correct approach is to call FindAllBy and end with a First or FirstOrDefault:
var db = Database.Open();
var questionIdRow = db.Question.FindAllByFriendlyId(friendlyId)
.Select(db.Question.QuestionId)
.FirstOrDefault();
if (questionIdRow == null) return Guid.Empty;
return questionIdRow.QuestionId;

IronRuby performance issue while using Variables

Here is code of very simple expression evaluator using IronRuby
public class BasicRubyExpressionEvaluator
{
ScriptEngine engine;
ScriptScope scope;
public Exception LastException
{
get; set;
}
private static readonly Dictionary<string, ScriptSource> parserCache = new Dictionary<string, ScriptSource>();
public BasicRubyExpressionEvaluator()
{
engine = Ruby.CreateEngine();
scope = engine.CreateScope();
}
public object Evaluate(string expression, DataRow context)
{
ScriptSource source;
parserCache.TryGetValue(expression, out source);
if (source == null)
{
source = engine.CreateScriptSourceFromString(expression, SourceCodeKind.SingleStatement);
parserCache.Add(expression, source);
}
var result = source.Execute(scope);
return result;
}
public void SetVariable(string variableName, object value)
{
scope.SetVariable(variableName, value);
}
}
and here is problem.
var evaluator = new BasicRubyExpressionEvaluator();
evaluator.SetVariable("a", 10);
evaluator.SetVariable("b", 1 );
evaluator.Evaluate("a+b+2", null);
vs
var evaluator = new BasicRubyExpressionEvaluator();
evaluator.Evaluate("10+1+2", null);
First Is 25 times slower than second. Any suggestions? String.Replace is not a solution for me.
I do not think the performance you are seeing is due to variable setting; the first execution of IronRuby in a program is always going to be slower than the second, regardless of what you're doing, since most of the compiler isn't loaded in until code is actually run (for startup performance reasons). Please try that example again, maybe running each version of your code in a loop, and you'll see the performance is roughly equivalent; the variable-version does have some overhead of method-dispatch to get the variables, but that should be negligible if you run it enough.
Also, in your hosting code, how come you are holding onto ScriptScopes in a dictionary? I would hold onto CompiledCode (result of engine.CreateScriptSourceFromString(...).Compile()) instead -- as that will help a lot more in repeat runs.
you can of course first build the string something like
evaluator.Evaluate(string.format("a={0}; b={1}; a + b + 2", 10, 1))
Or you can make it a method
if instead of your script you return a method then you should be able to use it like a regular C# Func object.
var script = #"
def self.addition(a, b)
a + b + 2
end
"
engine.ExecuteScript(script);
var = func = scope.GetVariable<Func<object,object,object>>("addition");
func(10,1)
This is probably not a working snippet but it shows the general idea.