here is a very simple LINQ To SQL model Validator.
Specifically, it checks if any tables have an IDENTITY clause set, where it is not expected.
This is script was used to sanity check a project, where LINQ To SQL was used to copy data into tables, and we wanted to preserve the ID of each record - hence we do NOT want IDENTITY property switched on.
This script might be handy as a template for validating a LINQ To SQL Model, as part of a build step.
Dependencies: Python 2.7
Specifically, it checks if any tables have an IDENTITY clause set, where it is not expected.
This is script was used to sanity check a project, where LINQ To SQL was used to copy data into tables, and we wanted to preserve the ID of each record - hence we do NOT want IDENTITY property switched on.
This script might be handy as a template for validating a LINQ To SQL Model, as part of a build step.
Dependencies: Python 2.7
""" ValidateLinqDesigner.py Script to validate a LINQ Designer.cs source code file, to see are any table classes set to have IDENTITY when they should not do so. USAGE: ValidateLinqDesigner.py <Designer.cs file to validate> <file listing classes to ignore> [OPTIONS] where OPTIONS are: -w -warnings Show warnings only (non-verbose output) """ import csv import getopt import getpass import re import os import pyodbc import shutil import subprocess import sys import win32api from string import split ############################################################### # settings: #LOG_WARNINGS_ONLY - this means, only output if the verbosity is LOG_WARNINGS LOG_WARNINGS, LOG_WARNINGS_ONLY, LOG_NORMAL, LOG_VERBOSE = range(4) #enum to track current state of the source code parser: PARSE_OUTSIDE_CLASS, PARSE_IN_CLASS = range(2) logVerbosity = LOG_NORMAL #LOG_VERBOSE numWarnings = 0 pathToDesignerCSfile = "" #some classes should be ignored (as they are NOT targets for writing) pathToIgnoreClassesFile = "" #need to check for these illegal phrases within each class: illegalPhrases = ['IDENTITY"', 'IsDbGenerated=true'] ############################################################### #usage() - prints out the usage text, from the top of this file :-) def usage(): print __doc__ ############################################################### #main() - main program entry point #args = <Designer.cs file to validate> <file listing classes to ignore> [OPTIONS] def main(argv): global pathToDesignerCSfile, pathToIgnoreClassesFile try: opts, args = getopt.getopt(argv, "w", ["warnings"]) except getopt.GetoptError: usage() sys.exit(2) if(len(args) != 2): usage() sys.exit(3) #assign the args to variables: pathToDesignerCSfile = args[0] pathToIgnoreClassesFile = args[1] for opt, arg in opts: if opt in ("-w", "--warnings"): setLogVerbosity(LOG_WARNINGS) if __name__ == "__main__": main(sys.argv[1:]) ############################################################### #FUNCTIONS - support def addWarning(warningMsg): global numWarnings numWarnings = numWarnings + 1 printOut("! WARNING - " + warningMsg, LOG_WARNINGS) #provide access to the global that is in this module: def getNumWarnings(): return numWarnings #printOut() #this function prints out, according to user's options for verbosity def printOut(txt, verb = LOG_NORMAL, bNewLine = True): global logVerbosity #txt = "> " + txt #prefix to make it easier to grep our output from 7zip's if(bNewLine): txt = txt + "\n" if verb == LOG_WARNINGS_ONLY: if logVerbosity == LOG_WARNINGS: #special case :-( sys.stdout.write(txt) elif(logVerbosity >= verb): sys.stdout.write(txt) #get the global in this module def getLogVerbosity(): global logVerbosity return logVerbosity #set the global in this module def setLogVerbosity(verbosity): global logVerbosity logVerbosity = verbosity #LOG_VERBOSE only - this is for debugging really, to see does the class parser work OK: def printOutSourceCodeByClass(sourceCodeByClass): separator = "*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x" for className in sourceCodeByClass: printOut("class " + className + separator, LOG_VERBOSE) printOut(sourceCodeByClass[className], LOG_VERBOSE) printOut("", LOG_VERBOSE) ############################################################### #FUNCTIONS - implementation def readClassesToIgnoreFile(pathToIgnoreClassesFile): classesToIgnore = [] listFileReader = csv.reader(open(pathToIgnoreClassesFile, 'rb'), delimiter=',') for row in listFileReader: if(len(row) > 0): classToIgnore = row[0] if(classToIgnore[0] == '#'): continue # a comment line classesToIgnore.append(classToIgnore) return classesToIgnore def parseSourceCode(pathToDesignerCSfile): if pathToDesignerCSfile.lower().endswith(".cs"): return parseSourceCodeCS(pathToDesignerCSfile) else: raise Exception("NotImplemented - cannot parse source code file with this extension: " + pathToDesignerCSfile) def parseClassNameCS(line): #take the first word that occurs after "class ": words = line.split() #consequtive whitespace is one delimiter bFoundClass = False className = "" for word in words: if word == "class": bFoundClass = True elif bFoundClass: className = word break return className def isLineCommentCS(line): line = line.strip() return line.startswith("//") def isClassStartLineCS(line): classStartToken = " class " if not isLineCommentCS(line): return classStartToken in line else: return False def parseSourceCodeCS(pathToDesignerCSfile): sourceCodeByClass = dict() sourceFile = open(pathToDesignerCSfile, 'rb') sourceCodeThisClass = "" thisClassName = "" parseState = PARSE_OUTSIDE_CLASS # PARSE_IN_CLASS for line in sourceFile: if isClassStartLineCS(line): newClassName = parseClassNameCS(line) if parseState == PARSE_OUTSIDE_CLASS: parseState = PARSE_IN_CLASS thisClassName = newClassName elif parseState == PARSE_IN_CLASS: #we found a new class: (lazy way to divide up code by START of a class, which is good enough for our needs) sourceCodeByClass[thisClassName] = sourceCodeThisClass thisClassName = newClassName sourceCodeThisClass = "" else: raise Exception("unhandled parse state "+ str(parseState)) if parseState == PARSE_IN_CLASS: sourceCodeThisClass = sourceCodeThisClass + line #handle the last class: if len(thisClassName) > 0: sourceCodeByClass[thisClassName] = sourceCodeThisClass printOutSourceCodeByClass(sourceCodeByClass) return sourceCodeByClass def filterSourceCodeByClass(sourceCodeByClass, classesToIgnore): filteredSourceCodeByClass = dict() for className in sourceCodeByClass: if not className in classesToIgnore: filteredSourceCodeByClass[className] = sourceCodeByClass[className] return filteredSourceCodeByClass def validateClassSourceCodeCS(className, sourceCodeForClass, illegalPhrases): for illegalPhrase in illegalPhrases: if illegalPhrase in sourceCodeForClass: raise Exception("!!! Illegal phrase " + illegalPhrase + " found in class " + className) return def validateSourceCodeByClass(filteredSourceCodeByClass, illegalPhrases): for className in filteredSourceCodeByClass: printOut("Validating class " + className) validateClassSourceCodeCS(className, filteredSourceCodeByClass[className], illegalPhrases) ############################################################### #main classesToIgnore = readClassesToIgnoreFile(pathToIgnoreClassesFile) sourceCodeByClass = parseSourceCode(pathToDesignerCSfile) filteredSourceCodeByClass = filterSourceCodeByClass(sourceCodeByClass, classesToIgnore) validateSourceCodeByClass(filteredSourceCodeByClass, illegalPhrases) printOut("[ok]") ###############################################################
Comments
Post a Comment