Meeting the needs of your business from a distance

Extending Visual Studio Context Menus

by mark shiffer 19. July 2010 23:29

Just a few quick things that I learned while trying to create a new context menu item on the solution explorer in visual studio:

First, there is a post on how to enable VSIP Logging that comes in handy to figure out the Guids and Ids of the menu items in visual studio. The full post is: Using EnableVSIPLogging to identify menus and commands with VS 2005 + SP1, but this part applies to Visual Studio 2010 as well. Essentially you flip a flag in the registry, restart Visual Studio and then using Ctrl-Shift, Right Click on menu items and a dialog appears with the information that you need. Here is the registry key:
[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0\General]
"EnableVSIPLogging"=dword:00000001

Second, from an article entitled Dynamic Menu Command in Visual Studio Packages – Part 2, which is where I found the first reference from, it shows  how to define a GuidSymbol, Group and Button appropriately. I am not going to repeat that here. Really it is just repeating what Visual Studio does for you automatically when you create a Package.

Tags:

Fun Coding Challenges

by mark shiffer 16. July 2010 17:03

Pex is an automated unit testing generation tool that Microsoft Research has had out for a while now. Essentially Pex generates various input parameters for your methods helping to find the variations that should be testing to get good code coverage.

The PexForFun website provides an interesting set of code challenges while using Pex to drive it. You are given a starter method that is stripped down to be barely functional and that fails the Pex generated tests. Your goal is to alter the method such that it will pass the tests without any outside knowledge as to what the code is supposed to do and without knowing the Pex unit tests until they fail. It is an interesting approach to advertising what capabilities Pex has and can be a little addicting too.

Tags:

Research | Programming

Development Resources for Any Language!

by mark shiffer 16. July 2010 16:56

Came across a website today called DevCheatSheet that contains a nice compilation of quick references for just about any language (and more) you can think of including: programming, scripting, markup, databases, version control, servers, ORM, blogs, websites, text editors and many other miscellaneous application.

Although fairly comprehensive there are missing pieces. I noticed Team Foundation Server (TFS) was missing from version control and Entity Framework (EF) was missing from ORM. Regardless should be a good place to find a reference for that language that you rarely use and need to remind your self of the syntax for.

Tags:

Programming | Research

Examining Entity Framework’s SQL Generation

by mark shiffer 30. June 2010 16:00

I am just getting started with this, but so far I am less than impressed with the SQL that is being generated by Entity Framework. The following is an example of a query that is generated by Entity Framework for a system that I am currently working on.

   1: SELECT 
   2: [UnionAll4].[C2] AS [C1], 
   3: ...
   4: [UnionAll4].[C590] AS [C590]
   5: FROM  (SELECT 
   6:     [UnionAll2].[C1] AS [C1], 
   7:         ...
   8:     [UnionAll2].[C590] AS [C590]
   9:     FROM  (SELECT 
  10:         [UnionAll1].[C1] AS [C1], 
  11:         ...
  12:         [UnionAll1].[C547] AS [C590]
  13:         FROM  (SELECT 
  14:             CASE WHEN ([Extent5].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1], 
  15:             [Extent2].[Id] AS [Id], 
  16:             [Extent3].[Id] AS [Id1], 
  17:             0 AS [C2], 
  18:             '0X0X0X0X' AS [C3], 
  19:             0 AS [C4], 
  20:             [Extent1].[Id] AS [Id2], 
  21:             [Extent1].[ModifiedBy] AS [ModifiedBy], 
  22:             [Extent1].[LastModified] AS [LastModified], 
  23:             [Extent1].[TaxId] AS [TaxId], 
  24:             [Extent1].[Version] AS [Version], 
  25:             [Extent2].[LastName] AS [LastName], 
  26:             [Extent2].[FirstName] AS [FirstName], 
  27:             [Extent2].[BirthDate] AS [BirthDate], 
  28:             [Extent2].[MiddleName] AS [MiddleName], 
  29:             [Extent2].[Gender] AS [Gender], 
  30:             [Extent2].[PreferredName] AS [PreferredName], 
  31:             [Extent3].[SrsStartDate] AS [SrsStartDate], 
  32:             [Extent3].[VacationBaseDate] AS [VacationBaseDate], 
  33:             [Extent3].[CredibleServiceDate] AS [CredibleServiceDate], 
  34:             [Extent3].[ContinuousServiceDate] AS [ContinuousServiceDate], 
  35:             [Extent3].[TerminationDate] AS [TerminationDate], 
  36:             [Extent3].[MonthlySalary] AS [MonthlySalary], 
  37:             [Extent3].[PayStep] AS [PayStep], 
  38:             [Extent3].[IsMeritComp] AS [IsMeritComp], 
  39:             [Extent3].[SeniorityDate] AS [SeniorityDate], 
  40:             [Extent3].[WorkScheduleTypeId] AS [WorkScheduleTypeId], 
  41:             [Extent3].[SupervisorEmployeeId] AS [SupervisorEmployeeId], 
  42:             CAST(NULL AS varchar(1)) AS [C5], 
  43:             CAST(NULL AS varchar(1)) AS [C6], 
  44:             CAST(NULL AS varchar(1)) AS [C7], 
  45:             CAST(NULL AS uniqueidentifier) AS [C8], 
  46:             CAST(NULL AS uniqueidentifier) AS [C9], 
  47:             CAST(NULL AS varbinary(1)) AS [C10], 
  48:             CAST(NULL AS varchar(1)) AS [C11], 
  49:             CAST(NULL AS varchar(1)) AS [C12], 
  50:             CAST(NULL AS varchar(1)) AS [C13], 
  51:             CAST(NULL AS varchar(1)) AS [C14], 
  52:             CAST(NULL AS uniqueidentifier) AS [C15], 
  53:             CAST(NULL AS uniqueidentifier) AS [C16], 
  54:             ...
  55:             CAST(NULL AS varbinary(1)) AS [C53], 
  56:             CASE WHEN ([Extent4].[Id] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE '0X3X' END AS [C54], 
  57:             CASE WHEN ([Extent4].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 4 END AS [C55], 
  58:             [Extent4].[Id] AS [Id3], 
  59:             [Extent4].[ModifiedBy] AS [ModifiedBy1], 
  60:             [Extent4].[LastModified] AS [LastModified1], 
  61:             CAST(NULL AS varchar(1)) AS [C56], 
  62:             ...
  63:             CAST(NULL AS varbinary(1)) AS [C89], 
  64:             [Extent4].[Sequence] AS [Sequence], 
  65:             [Extent4].[TitleId] AS [TitleId], 
  66:             [Extent4].[AgencyId] AS [AgencyId], 
  67:             [Extent4].[SectionId] AS [SectionId], 
  68:             [Extent4].[EqualEmploymentOpportunityCodeId] AS [EqualEmploymentOpportunityCodeId], 
  69:             [Extent4].[BargainingUnitCodeId] AS [BargainingUnitCodeId], 
  70:             [Extent4].[Version] AS [Version1], 
  71:             CAST(NULL AS datetime2) AS [C90], 
  72:             ...
  73:             CAST(NULL AS varbinary(1)) AS [C116], 
  74:             CASE WHEN ([Extent5].[Id] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE '0X1X' END AS [C117], 
  75:             CASE WHEN ([Extent5].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 5 END AS [C118], 
  76:             [Extent5].[Id] AS [Id4], 
  77:             [Extent5].[ModifiedBy] AS [ModifiedBy2], 
  78:             [Extent5].[LastModified] AS [LastModified2], 
  79:             CAST(NULL AS varchar(1)) AS [C119], 
  80:             ...
  81:             CAST(NULL AS uniqueidentifier) AS [C137], 
  82:             [Extent5].[CountryCode] AS [CountryCode], 
  83:             ...
  84:             CAST(NULL AS varbinary(1)) AS [C547]
  85:             FROM     [dbo].[Orgsons] AS [Extent1]
  86:             INNER JOIN [dbo].[Orgsons_Person] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Id]
  87:             INNER JOIN [dbo].[Orgsons_Employee] AS [Extent3] ON [Extent1].[Id] = [Extent3].[Id]
  88:             LEFT OUTER JOIN [dbo].[Positions] AS [Extent4] ON [Extent3].[Position_Id] = [Extent4].[Id]
  89:             LEFT OUTER JOIN [dbo].[PhoneNumbers] AS [Extent5] ON [Extent1].[Id] = [Extent5].[OrgsonId]
  90:             WHERE [Extent1].[Id] = '4F65BC85-06FD-4CC7-BB2B-95F2536EF132'
  91:         UNION ALL
  92:             SELECT 
  93:             2 AS [C1], 
  94:             [Extent7].[Id] AS [Id], 
  95:             [Extent8].[Id] AS [Id1], 
  96:             0 AS [C2], 
  97:             '0X0X0X0X' AS [C3], 
  98:             0 AS [C4], 
  99:             [Extent6].[Id] AS [Id2], 
 100:             [Extent6].[ModifiedBy] AS [ModifiedBy], 
 101:             [Extent6].[LastModified] AS [LastModified], 
 102:             [Extent6].[TaxId] AS [TaxId], 
 103:             [Extent6].[Version] AS [Version], 
 104:             [Extent7].[LastName] AS [LastName], 
 105:             [Extent7].[FirstName] AS [FirstName], 
 106:             [Extent7].[BirthDate] AS [BirthDate], 
 107:             [Extent7].[MiddleName] AS [MiddleName], 
 108:             [Extent7].[Gender] AS [Gender], 
 109:             [Extent7].[PreferredName] AS [PreferredName], 
 110:             [Extent8].[SrsStartDate] AS [SrsStartDate], 
 111:             [Extent8].[VacationBaseDate] AS [VacationBaseDate], 
 112:             [Extent8].[CredibleServiceDate] AS [CredibleServiceDate], 
 113:             [Extent8].[ContinuousServiceDate] AS [ContinuousServiceDate], 
 114:             [Extent8].[TerminationDate] AS [TerminationDate], 
 115:             [Extent8].[MonthlySalary] AS [MonthlySalary], 
 116:             [Extent8].[PayStep] AS [PayStep], 
 117:             [Extent8].[IsMeritComp] AS [IsMeritComp], 
 118:             [Extent8].[SeniorityDate] AS [SeniorityDate], 
 119:             [Extent8].[WorkScheduleTypeId] AS [WorkScheduleTypeId], 
 120:             [Extent8].[SupervisorEmployeeId] AS [SupervisorEmployeeId], 
 121:             CAST(NULL AS varchar(1)) AS [C5], 
 122:             ...
 123:             CAST(NULL AS varbinary(1)) AS [C53], 
 124:             CASE WHEN ([Extent9].[Id] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE '0X3X' END AS [C54], 
 125:             CASE WHEN ([Extent9].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 4 END AS [C55], 
 126:             [Extent9].[Id] AS [Id3], 
 127:             [Extent9].[ModifiedBy] AS [ModifiedBy1], 
 128:             [Extent9].[LastModified] AS [LastModified1], 
 129:             CAST(NULL AS varchar(1)) AS [C56], 
 130:             ...
 131:             CAST(NULL AS varbinary(1)) AS [C89], 
 132:             [Extent9].[Sequence] AS [Sequence], 
 133:             [Extent9].[TitleId] AS [TitleId], 
 134:             [Extent9].[AgencyId] AS [AgencyId], 
 135:             [Extent9].[SectionId] AS [SectionId], 
 136:             [Extent9].[EqualEmploymentOpportunityCodeId] AS [EqualEmploymentOpportunityCodeId], 
 137:             [Extent9].[BargainingUnitCodeId] AS [BargainingUnitCodeId], 
 138:             [Extent9].[Version] AS [Version1], 
 139:             CAST(NULL AS datetime2) AS [C90], 
 140:             ...
 141:             CAST(NULL AS varbinary(1)) AS [C216], 
 142:             [Extent10].[Street1] AS [Street1], 
 143:             [Extent10].[Street2] AS [Street2], 
 144:             [Extent10].[City] AS [City], 
 145:             [Extent10].[ZipCode] AS [ZipCode], 
 146:             [Extent10].[OrgsonId] AS [OrgsonId], 
 147:             [Extent10].[AddressTypeId] AS [AddressTypeId], 
 148:             [Extent10].[StateId] AS [StateId], 
 149:             [Extent10].[CountryId] AS [CountryId], 
 150:             [Extent10].[Version] AS [Version2], 
 151:             CAST(NULL AS varchar(1)) AS [C217], 
 152:             ... 
 153:             CAST(NULL AS varbinary(1)) AS [C544]
 154:             FROM     [dbo].[Orgsons] AS [Extent6]
 155:             INNER JOIN [dbo].[Orgsons_Person] AS [Extent7] ON [Extent6].[Id] = [Extent7].[Id]
 156:             INNER JOIN [dbo].[Orgsons_Employee] AS [Extent8] ON [Extent6].[Id] = [Extent8].[Id]
 157:             LEFT OUTER JOIN [dbo].[Positions] AS [Extent9] ON [Extent8].[Position_Id] = [Extent9].[Id]
 158:             INNER JOIN [dbo].[Addresses] AS [Extent10] ON [Extent6].[Id] = [Extent10].[OrgsonId]
 159:             WHERE [Extent6].[Id] = '4F65BC85-06FD-4CC7-BB2B-95F2536EF132') AS [UnionAll1]
 160:     UNION ALL
 161:         SELECT 
 162:         3 AS [C1], 
 163:         [Extent12].[Id] AS [Id], 
 164:         [Extent13].[Id] AS [Id1], 
 165:         0 AS [C2], 
 166:         '0X0X0X0X' AS [C3], 
 167:         0 AS [C4], 
 168:         [Extent11].[Id] AS [Id2], 
 169:         [Extent11].[ModifiedBy] AS [ModifiedBy], 
 170:         [Extent11].[LastModified] AS [LastModified], 
 171:         [Extent11].[TaxId] AS [TaxId], 
 172:         [Extent11].[Version] AS [Version], 
 173:         [Extent12].[LastName] AS [LastName], 
 174:         [Extent12].[FirstName] AS [FirstName], 
 175:         [Extent12].[BirthDate] AS [BirthDate], 
 176:         [Extent12].[MiddleName] AS [MiddleName], 
 177:         [Extent12].[Gender] AS [Gender], 
 178:         [Extent12].[PreferredName] AS [PreferredName], 
 179:         [Extent13].[SrsStartDate] AS [SrsStartDate], 
 180:         [Extent13].[VacationBaseDate] AS [VacationBaseDate], 
 181:         [Extent13].[CredibleServiceDate] AS [CredibleServiceDate], 
 182:         [Extent13].[ContinuousServiceDate] AS [ContinuousServiceDate], 
 183:         [Extent13].[TerminationDate] AS [TerminationDate], 
 184:         [Extent13].[MonthlySalary] AS [MonthlySalary], 
 185:         [Extent13].[PayStep] AS [PayStep], 
 186:         [Extent13].[IsMeritComp] AS [IsMeritComp], 
 187:         [Extent13].[SeniorityDate] AS [SeniorityDate], 
 188:         [Extent13].[WorkScheduleTypeId] AS [WorkScheduleTypeId], 
 189:         [Extent13].[SupervisorEmployeeId] AS [SupervisorEmployeeId], 
 190:         CAST(NULL AS varchar(1)) AS [C5], 
 191:         ...
 192:         CAST(NULL AS varbinary(1)) AS [C53], 
 193:         CASE WHEN ([Extent14].[Id] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE '0X3X' END AS [C54], 
 194:         CASE WHEN ([Extent14].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 4 END AS [C55], 
 195:         [Extent14].[Id] AS [Id3], 
 196:         [Extent14].[ModifiedBy] AS [ModifiedBy1], 
 197:         [Extent14].[LastModified] AS [LastModified1], 
 198:         CAST(NULL AS varchar(1)) AS [C56], 
 199:         ...
 200:         CAST(NULL AS varbinary(1)) AS [C89], 
 201:         [Extent14].[Sequence] AS [Sequence], 
 202:         [Extent14].[TitleId] AS [TitleId], 
 203:         [Extent14].[AgencyId] AS [AgencyId], 
 204:         [Extent14].[SectionId] AS [SectionId], 
 205:         [Extent14].[EqualEmploymentOpportunityCodeId] AS [EqualEmploymentOpportunityCodeId], 
 206:         [Extent14].[BargainingUnitCodeId] AS [BargainingUnitCodeId], 
 207:         [Extent14].[Version] AS [Version1], 
 208:         CAST(NULL AS datetime2) AS [C90], 
 209:         ...
 210:         CAST(NULL AS varbinary(1)) AS [C262], 
 211:         '0X4X' AS [C263], 
 212:         10 AS [C264], 
 213:         [Extent15].[Id] AS [Id4], 
 214:         [Extent15].[ModifiedBy] AS [ModifiedBy2], 
 215:         [Extent15].[LastModified] AS [LastModified2], 
 216:         CAST(NULL AS varchar(1)) AS [C265], 
 217:         ...
 218:         CAST(NULL AS varbinary(1)) AS [C305], 
 219:         [Extent15].[SentDate] AS [SentDate], 
 220:         [Extent15].[ReturnedDate] AS [ReturnedDate], 
 221:         [Extent15].[EmployeeId] AS [EmployeeId], 
 222:         [Extent15].[EvaluationTypeId] AS [EvaluationTypeId], 
 223:         [Extent15].[Version] AS [Version2], 
 224:         CAST(NULL AS datetime2) AS [C306], 
 225:         ...
 226:         CAST(NULL AS varbinary(1)) AS [C548]
 227:         FROM     [dbo].[Orgsons] AS [Extent11]
 228:         INNER JOIN [dbo].[Orgsons_Person] AS [Extent12] ON [Extent11].[Id] = [Extent12].[Id]
 229:         INNER JOIN [dbo].[Orgsons_Employee] AS [Extent13] ON [Extent11].[Id] = [Extent13].[Id]
 230:         LEFT OUTER JOIN [dbo].[Positions] AS [Extent14] ON [Extent13].[Position_Id] = [Extent14].[Id]
 231:         INNER JOIN [dbo].[Evaluations] AS [Extent15] ON [Extent11].[Id] = [Extent15].[EmployeeId]
 232:         WHERE [Extent11].[Id] = '4F65BC85-06FD-4CC7-BB2B-95F2536EF132') AS [UnionAll2]
 233: UNION ALL
 234:     SELECT 
 235:     4 AS [C1], 
 236:     [Project6].[Id1] AS [Id], 
 237:     [Project6].[Id2] AS [Id1], 
 238:     [Project6].[C2] AS [C2], 
 239:     [Project6].[C1] AS [C3], 
 240:     [Project6].[C2] AS [C4], 
 241:     [Project6].[Id] AS [Id2], 
 242:     [Project6].[ModifiedBy] AS [ModifiedBy], 
 243:     [Project6].[LastModified] AS [LastModified], 
 244:     [Project6].[TaxId] AS [TaxId], 
 245:     [Project6].[Version] AS [Version], 
 246:     [Project6].[LastName] AS [LastName], 
 247:     [Project6].[FirstName] AS [FirstName], 
 248:     [Project6].[BirthDate] AS [BirthDate], 
 249:     [Project6].[MiddleName] AS [MiddleName], 
 250:     [Project6].[Gender] AS [Gender], 
 251:     [Project6].[PreferredName] AS [PreferredName], 
 252:     [Project6].[SrsStartDate] AS [SrsStartDate], 
 253:     [Project6].[VacationBaseDate] AS [VacationBaseDate], 
 254:     [Project6].[CredibleServiceDate] AS [CredibleServiceDate], 
 255:     [Project6].[ContinuousServiceDate] AS [ContinuousServiceDate], 
 256:     [Project6].[TerminationDate] AS [TerminationDate], 
 257:     [Project6].[MonthlySalary] AS [MonthlySalary], 
 258:     [Project6].[PayStep] AS [PayStep], 
 259:     [Project6].[IsMeritComp] AS [IsMeritComp], 
 260:     [Project6].[SeniorityDate] AS [SeniorityDate], 
 261:     [Project6].[WorkScheduleTypeId] AS [WorkScheduleTypeId], 
 262:     [Project6].[SupervisorEmployeeId] AS [SupervisorEmployeeId], 
 263:     [Project6].[C3] AS [C5], 
 264:     ...
 265:     [Project6].[Id3] AS [Id3], 
 266:     [Project6].[ModifiedBy1] AS [ModifiedBy1], 
 267:     [Project6].[LastModified1] AS [LastModified1], 
 268:     [Project6].[C54] AS [C56], 
 269:     ... 
 270:     [Project6].[C86] AS [C88], 
 271:     [Project6].[C87] AS [C89], 
 272:     [Project6].[Sequence] AS [Sequence], 
 273:     [Project6].[TitleId] AS [TitleId], 
 274:     [Project6].[AgencyId] AS [AgencyId], 
 275:     [Project6].[SectionId] AS [SectionId], 
 276:     [Project6].[EqualEmploymentOpportunityCodeId] AS [EqualEmploymentOpportunityCodeId], 
 277:     [Project6].[BargainingUnitCodeId] AS [BargainingUnitCodeId], 
 278:     [Project6].[Version1] AS [Version1], 
 279:     [Project6].[C88] AS [C90], 
 280:     ...     
 281:     CAST(NULL AS varbinary(1)) AS [C335], 
 282:     ...
 283:     [UnionAll3].[C199] AS [C556]
 284:     FROM   (SELECT 
 285:         [Extent16].[Id] AS [Id], 
 286:         [Extent16].[TaxId] AS [TaxId], 
 287:         [Extent16].[Version] AS [Version], 
 288:         [Extent16].[ModifiedBy] AS [ModifiedBy], 
 289:         [Extent16].[LastModified] AS [LastModified], 
 290:         [Extent17].[LastName] AS [LastName], 
 291:         [Extent17].[FirstName] AS [FirstName], 
 292:         [Extent17].[BirthDate] AS [BirthDate], 
 293:         [Extent17].[MiddleName] AS [MiddleName], 
 294:         [Extent17].[Gender] AS [Gender], 
 295:         [Extent17].[PreferredName] AS [PreferredName], 
 296:         [Extent17].[Id] AS [Id1], 
 297:         [Extent18].[SrsStartDate] AS [SrsStartDate], 
 298:         [Extent18].[VacationBaseDate] AS [VacationBaseDate], 
 299:         [Extent18].[CredibleServiceDate] AS [CredibleServiceDate], 
 300:         [Extent18].[ContinuousServiceDate] AS [ContinuousServiceDate], 
 301:         [Extent18].[TerminationDate] AS [TerminationDate], 
 302:         [Extent18].[MonthlySalary] AS [MonthlySalary], 
 303:         [Extent18].[PayStep] AS [PayStep], 
 304:         [Extent18].[IsMeritComp] AS [IsMeritComp], 
 305:         [Extent18].[SeniorityDate] AS [SeniorityDate], 
 306:         [Extent18].[WorkScheduleTypeId] AS [WorkScheduleTypeId], 
 307:         [Extent18].[SupervisorEmployeeId] AS [SupervisorEmployeeId], 
 308:         [Extent18].[Id] AS [Id2], 
 309:         '0X0X0X0X' AS [C1], 
 310:         0 AS [C2], 
 311:         CAST(NULL AS varchar(1)) AS [C3], 
 312:         ... 
 313:         CAST(NULL AS varbinary(1)) AS [C51], 
 314:         [Extent19].[Id] AS [Id3], 
 315:         [Extent19].[Sequence] AS [Sequence], 
 316:         [Extent19].[TitleId] AS [TitleId], 
 317:         [Extent19].[AgencyId] AS [AgencyId], 
 318:         [Extent19].[SectionId] AS [SectionId], 
 319:         [Extent19].[EqualEmploymentOpportunityCodeId] AS [EqualEmploymentOpportunityCodeId], 
 320:         [Extent19].[BargainingUnitCodeId] AS [BargainingUnitCodeId], 
 321:         [Extent19].[Version] AS [Version1], 
 322:         [Extent19].[ModifiedBy] AS [ModifiedBy1], 
 323:         [Extent19].[LastModified] AS [LastModified1], 
 324:         CASE WHEN ([Extent19].[Id] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE '0X3X' END AS [C52], 
 325:         CASE WHEN ([Extent19].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 4 END AS [C53], 
 326:         CAST(NULL AS varchar(1)) AS [C54], 
 327:         ... 
 328:         CAST(NULL AS varbinary(1)) AS [C114]
 329:         FROM    [dbo].[Orgsons] AS [Extent16]
 330:         INNER JOIN [dbo].[Orgsons_Person] AS [Extent17] ON [Extent16].[Id] = [Extent17].[Id]
 331:         INNER JOIN [dbo].[Orgsons_Employee] AS [Extent18] ON [Extent16].[Id] = [Extent18].[Id]
 332:         LEFT OUTER JOIN [dbo].[Positions] AS [Extent19] ON [Extent18].[Position_Id] = [Extent19].[Id]
 333:         WHERE [Extent16].[Id] = '4F65BC85-06FD-4CC7-BB2B-95F2536EF132' ) AS [Project6]
 334:     CROSS APPLY  (SELECT 
 335:         CASE WHEN ([Extent21].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1], 
 336:         12 AS [C2], 
 337:         '0X5X' AS [C3], 
 338:         12 AS [C4], 
 339:         [Extent20].[Id] AS [Id], 
 340:         [Extent20].[ModifiedBy] AS [ModifiedBy], 
 341:         [Extent20].[LastModified] AS [LastModified], 
 342:         CAST(NULL AS varchar(1)) AS [C5], 
 343:         ... 
 344:         CAST(NULL AS varbinary(1)) AS [C61], 
 345:         CASE WHEN ([Extent21].[Id] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE '0X6X' END AS [C62], 
 346:         CASE WHEN ([Extent21].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 14 END AS [C63], 
 347:         [Extent21].[Id] AS [Id1], 
 348:         [Extent21].[ModifiedBy] AS [ModifiedBy1], 
 349:         [Extent21].[LastModified] AS [LastModified1], 
 350:         CAST(NULL AS varchar(1)) AS [C64], 
 351:         ... 
 352:         CAST(NULL AS varbinary(1)) AS [C120], 
 353:         [Extent21].[Description] AS [Description], 
 354:         [Extent21].[Created] AS [Created1], 
 355:         [Extent21].[CreatedBy] AS [CreatedBy], 
 356:         [Extent21].[LeaveOfAbsenceId] AS [LeaveOfAbsenceId], 
 357:         [Extent21].[Version] AS [Version1], 
 358:         CAST(NULL AS datetime2) AS [C121], 
 359:         ... 
 360:         CAST(NULL AS varbinary(1)) AS [C199]
 361:         FROM  [dbo].[LeaveOfAbsences] AS [Extent20]
 362:         LEFT OUTER JOIN [dbo].[Comments] AS [Extent21] ON [Extent20].[Id] = [Extent21].[LeaveOfAbsenceId]
 363:         WHERE ([Project6].[C2] = 0) AND ([Project6].[Id] = [Extent20].[EmployeeId])
 364:     UNION ALL
 365:         SELECT 
 366:         2 AS [C1], 
 367:         12 AS [C2], 
 368:         '0X5X' AS [C3], 
 369:         12 AS [C4], 
 370:         [Extent22].[Id] AS [Id], 
 371:         [Extent22].[ModifiedBy] AS [ModifiedBy], 
 372:         [Extent22].[LastModified] AS [LastModified], 
 373:         CAST(NULL AS varchar(1)) AS [C5], 
 374:         ... 
 375:         CAST(NULL AS varbinary(1)) AS [C50], 
 376:         [Extent22].[RequestReceivedDate] AS [RequestReceivedDate], 
 377:         [Extent22].[PacketSentDate] AS [PacketSentDate], 
 378:         [Extent22].[PacketReceivedDate] AS [PacketReceivedDate], 
 379:         [Extent22].[ReviewSentDate] AS [ReviewSentDate], 
 380:         [Extent22].[ReviewReceivedDate] AS [ReviewReceivedDate], 
 381:         [Extent22].[ApprovedDate] AS [ApprovedDate], 
 382:         [Extent22].[EmployeeId] AS [EmployeeId], 
 383:         [Extent22].[Created] AS [Created], 
 384:         [Extent22].[LeaveTypeId] AS [LeaveTypeId], 
 385:         [Extent22].[LeaveStatusId] AS [LeaveStatusId], 
 386:         [Extent22].[Version] AS [Version], 
 387:         CAST(NULL AS varchar(1)) AS [C51], 
 388:         ...
 389:         CAST(NULL AS varbinary(1)) AS [C134], 
 390:         '0X7X' AS [C135], 
 391:         16 AS [C136], 
 392:         [Extent23].[Id] AS [Id1], 
 393:         [Extent23].[ModifiedBy] AS [ModifiedBy1], 
 394:         [Extent23].[LastModified] AS [LastModified1], 
 395:         CAST(NULL AS varchar(1)) AS [C137], 
 396:         ... 
 397:         CAST(NULL AS varbinary(1)) AS [C198], 
 398:         [Extent23].[StartDate] AS [StartDate], 
 399:         [Extent23].[StopDate] AS [StopDate], 
 400:         [Extent23].[DaysOff] AS [DaysOff], 
 401:         [Extent23].[Created] AS [Created1], 
 402:         [Extent23].[LeaveOfAbsenceId] AS [LeaveOfAbsenceId], 
 403:         [Extent23].[Version] AS [Version1]
 404:         FROM  [dbo].[LeaveOfAbsences] AS [Extent22]
 405:         INNER JOIN [dbo].[LeaveOutOfOffices] AS [Extent23] ON [Extent22].[Id] = [Extent23].[LeaveOfAbsenceId]
 406:         WHERE ([Project6].[C2] = 0) AND ([Project6].[Id] = [Extent22].[EmployeeId])) AS [UnionAll3]) AS [UnionAll4]
 407: ORDER BY [UnionAll4].[C2] ASC, [UnionAll4].[C3] ASC, [UnionAll4].[C7] ASC, [UnionAll4].[C80] ASC, [UnionAll4].[C1] ASC, [UnionAll4].[C373] ASC, [UnionAll4].[C444] ASC;

That amounts to almost 4800 lines of SQL (much of it repetitive and omitted above) for a simple retrieve of an employee by id with some related data! Wow, what can be said about that!

The following query does the same thing in about 20 lines and runs 3 times faster:

   1: SELECT 
   2:     * 
   3: FROM 
   4:     Orgsons_Employee e 
   5:         JOIN 
   6:     Orgsons_Person p ON (e.Id = p.Id) 
   7:         JOIN 
   8:     Orgsons o ON (o.Id = p.Id) 
   9:         LEFT JOIN 
  10:     PhoneNumbers pn ON (pn.OrgsonId = o.Id) 
  11:         LEFT JOIN 
  12:     Addresses a ON (a.OrgsonId = o.Id) 
  13:         LEFT JOIN 
  14:     Positions pos ON (e.Position_Id = pos.Id) 
  15:         LEFT JOIN 
  16:     Evaluations eval ON (eval.EmployeeId = e.Id) 
  17:         LEFT JOIN 
  18:     LeaveOfAbsences loa ON (loa.EmployeeId = e.Id) 
  19:         LEFT JOIN 
  20:     LeaveOutOfOffices loo ON (loo.LeaveOfAbsenceId = loa.Id) 
  21:         LEFT JOIN 
  22:     Comments c ON (c.LeaveOfAbsenceId = loa.Id) 
  23: WHERE 
  24:     e.Id = '4F65BC85-06FD-4CC7-BB2B-95F2536EF132';

The execution plan on SQL Server shows a drastic difference between the two. If I find any hidden tweaking options for this in Entity Framework I’ll update this post.

Tags: ,

Nifty Microsoft Internet Explorer Advertising

by mark shiffer 29. June 2010 20:49

I usually browse in Chrome, and recently came across this example of advertising for Microsoft Internet Explorer.

image

Although it is doubtful I’ll switch back to Internet Explorer in the near term, I found this form of automatically advertising for IE when you hit Microsoft pages from a browser other than IE is rather clever. If you open this same page in IE, you don’t see the banner at the top. Good job Microsoft! Very slick.

Tags:

Unit of Work Pattern for Entity Framework 4 and Unity

by Mark Shiffer 28. June 2010 13:43

Continuing from my last post on the Repository pattern…here is some background.

I am in the process of bringing up a new architecture for a project that I am working on that uses MVVM with WPF, WCF, Unity and Entity Framework 4 with Self Tracking Entities. One of the foundational items that is necessary to create as part of this architecture is a Repository to abstract the data store. As a related item, a proper Unit of Work pattern needs to be instantiated in order to provide transactional boundaries for some of the data processes. I will focus on the Unit of Work in this post, for more information on my repository implementation please see my previous post.

Before I started designing my approach I read several different attempts by others at implementing these patterns. Some were better than others, but in the end no one approach seemed to be a best fit for my application. However, the heaviest influence on my design came from NCommon, a library that contains implementations of commonly used design patterns when developing applications. If you haven’t looked at NCommon before, I would suggest taking a gander as it has some interesting stuff. However, for my purposes, it introduced too much complexity by trying to be a generic framework that applied to several different technologies, whereas, directing my approach to my specific set of technologies (while still abstracting mind you), made the code much more straight forward.

With Entity Framework it naturally works out that the ObjectContext is the unit of work itself. The ObjectContext is already caching all of the data changes that occur during its lifetime and only commits those when asked. So, at the core of my Unit of Work implementation is the ObjectContext. I have defined an IUnitOfWork interface which the ObjectContext implements to further abstract the concept. After that there are 3 core classes involved in my implementation of Unit of Work:

1. UnitOfWorkScope – The purpose of this class is to provide a quick fluid interface to place a unit of work around a block of code. This is the core class that is used by the ‘outside’ world to create transactional code. It’s intended usage pattern is:

   1: using (var scope = new UnitOfWorkScope())
   2: {
   3:     ...
   4:     scope.Commit();
   5: }

2. TransactionManager – The TransactionManager is in charge of placing a UnitOfWorkScope into a UnitOfWorkTransaction whether it be new or existing.

3. UnitOfWorkTransaction – This is the actual transaction that controls when, and if, work completed within a set of UnitOfWorkScopes will be committed to the data store. When a UnitOfWorkTransaction is instantiated it creates a new IUnitOfWork instance (ObjectContext) on which to act.

So the developer creates scopes, nesting additional scopes where needed or creating side-by-side scopes of a separate transactional boundary. When a scope is created, it asks the TransactionManager to be enlisted in a transaction. The TransactionManager finds or creates an UnitOfWorkTransaction and associates it with the UnitOfWorkScope. The UnitOfWorkTransaction then keeps a list of active scopes for the transaction. When any of the scopes is rolled back, the entire transaction is torn down. Once the last scope is committed, assuming no rollbacks have occurred, the UnitOfWorkTransaction flushes the IUnitOfWork effectively calling SaveChanges on the ObjectContext.

If you’ve read the Repository post prior to this, you may have noticed where unit of work was tied in making the process very seamless for the end developer. The CurrentObjectSet property of the repository class coordinates with the TransactionManager such that regardless of where the repository instance was created, it will join a transaction on the fly if one is active. In addition, ApplyChanges will only save to the data store if the repository instance is not under a transaction at that time.

So, let’s start with the interfaces for what set’s all of this up:

IUnitOfWork:

   1: /// <summary>
   2:  /// Unit of work interface. The EF ObjectContext serves as the unit of work. This interface provides a contract to interact with the EF ObjectContext
   3:  /// as a unit of work.s
   4:  /// </summary>
   5:  public interface IUnitOfWork : IDisposable
   6:  {
   7:      /// <summary>
   8:      /// Gets the context for this unit of work (EF Context serves as the actual unit of work).
   9:      /// </summary>
  10:      /// <value>The context.</value>
  11:      ObjectContext Context { get; }
  12:  
  13:      /// <summary>
  14:      /// Commits changes to the Context to the data store.
  15:      /// </summary>
  16:      void Flush();
  17:  
  18:      /// <summary>
  19:      /// Rollbacks changes to the Context.
  20:      /// </summary>
  21:      void Rollback();
  22:  }

IUnitOfWorkScope:

   1: /// <summary>
   2: /// Unit of work scope is the outward facing wrapper for developers to begin/enlist in a transaction. Scopes can be nested, in which case, once the last or outtermost
   3: /// scope is committed then the unit of work will be flushed/committed. If any scope exists/disposes before committing it will be rolled back, and any
   4: /// scopes upward in the chain will be rolled back as well. 
   5: /// </summary>
   6: public interface IUnitOfWorkScope : IDisposable
   7: {
   8:     /// <summary>
   9:     /// Marks the scope committed, triggering the unit of work to commit if this is the outtermost scope.
  10:     /// </summary>
  11:     void Commit();
  12:  
  13:     /// <summary>
  14:     /// Rollbacks this scope, triggering the unit of work to be rolled back.
  15:     /// </summary>
  16:     void Rollback();
  17: }

ITransactionManager:

   1: /// <summary>
   2: /// The Transaction Manager holds a list of active, isolated transactions within the system and acts as the entry point for a new unit of work scope. 
   3: /// </summary>
   4: public interface ITransactionManager
   5: {
   6:     /// <summary>
   7:     /// Gets the current EF context based upon the most recent transacion.
   8:     /// </summary>
   9:     /// <value>The current context.</value>
  10:     ObjectContext CurrentContext { get; }
  11:  
  12:     /// <summary>
  13:     /// Gets or sets the thread static operation context, the unique identifier for a wcf call across our system.
  14:     /// </summary>
  15:     /// <value>The operation context.</value>
  16:     /// <param name="operationContext">The operation context.</param>
  17:     Guid OperationContext { get; set; }
  18:  
  19:     /// <summary>
  20:     /// Enlists a <see cref="UnitOfWorkScope"/> instance with the transaction manager, either beginning a new transaction or attaching to an existing transaction.
  21:     /// </summary>
  22:     /// <param name="scope">bool. True if the scope should be enlisted in a new transaction, else
  23:     /// false if the scope should participate in the existing transaction when available</param>
  24:     /// <param name="newTransaction">if set to <c>true</c> [new transaction].</param>
  25:     /// <returns>
  26:     /// Transcation created or assigned to the scope.
  27:     /// </returns>
  28:     IUnitOfWorkTransaction EnlistScope(IUnitOfWorkScope scope, bool newTransaction);
  29: }

IUnitOfWorkTransaction:

   1: /// <summary>
   2: /// Unit Of Work Transaction keeps a list of active scopes for a given transaction and commits the unit of work if all scopes have been committed.
   3: /// </summary>
   4: public interface IUnitOfWorkTransaction : IDisposable
   5: {
   6:     /// <summary>
   7:     /// Occurs when [transaction disposing].
   8:     /// </summary>
   9:     event EventHandler<EventArgs> TransactionDisposing;
  10:  
  11:     /// <summary>
  12:     /// Gets the unit of work.
  13:     /// </summary>
  14:     /// <value>The unit of work.</value>
  15:     IUnitOfWork UnitOfWork { get; }
  16:  
  17:     /// <summary>
  18:     /// Gets a unique identifier for a series of related operations. Under WCF this is provided by its operation context which is unique for each call made to the service.
  19:     /// </summary>
  20:     /// <value>The operation context identifier.</value>
  21:     Guid OperationContext { get; }
  22:  
  23:     /// <summary>
  24:     /// Enlists the scope in the transaction
  25:     /// </summary>
  26:     /// <param name="scope">The scope.</param>
  27:     void EnlistScope(IUnitOfWorkScope scope);
  28:  
  29:     /// <summary>
  30:     /// Commits the scope, if this is the last scope that was enlisted, the unit of work will be flushed.
  31:     /// </summary>
  32:     /// <param name="scope">The scope.</param>
  33:     void CommitScope(IUnitOfWorkScope scope);
  34:  
  35:     /// <summary>
  36:     /// Rollbacks the scope, effectively rolling back the unit of work.
  37:     /// </summary>
  38:     /// <param name="scope">The scope.</param>
  39:     void RollbackScope(IUnitOfWorkScope scope);
  40: }

All pretty straight forward. The only special note here is the OperationContext. This is what helps to make the pattern work across threads. This is especially important with WCF involved, as each call to the server will be on its own thread. In addition, server job processes could spawn additional threads to perform their work and one would expect work completed on a child thread should join the transaction of its parent. I’ll talk more about this as we look at the implementations of the interfaces.

<UnitOfWork>:

   1: /// <summary> Provide business logic for the MSSWCModelContainer class 
   2: /// </summary>
   3: [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Need interface with IDisposable so that code can call base disposable generically.")]
   4: public partial class MSSWCModelContainer : IUnitOfWork
   5: {
   6:     /// <summary>
   7:     /// Gets the context.
   8:     /// </summary>
   9:     /// <value>The context.</value>
  10:     public ObjectContext Context
  11:     {
  12:         get { return this; }
  13:     }
  14:  
  15:     /// <summary> Persists all updates to the data source with the specified <see cref="T:System.Data.Objects.SaveOptions"/>.
  16:     /// </summary>
  17:     /// <param name="options">A <see cref="T:System.Data.Objects.SaveOptions"/> value that determines the behavior of the operation.</param>
  18:     /// <returns>
  19:     /// The number of objects in an <see cref="F:System.Data.EntityState.Added"/>, <see cref="F:System.Data.EntityState.Modified"/>, or <see cref="F:System.Data.EntityState.Deleted"/> state when <see cref="M:System.Data.Objects.ObjectContext.SaveChanges"/> was called.
  20:     /// </returns>
  21:     /// <exception cref="T:System.Data.OptimisticConcurrencyException">An optimistic concurrency violation has occurred.</exception>
  22:     public override int SaveChanges(SaveOptions options)
  23:     {
  24:         // Validate the state of each entity in the context
  25:         // before SaveChanges can succeed.
  26:         foreach (ObjectStateEntry entry in ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified))
  27:         {
  28:             if (!entry.IsRelationship)
  29:             {
  30:                 if (entry.Entity is IValidatingEntity)
  31:                 {
  32:                     ((IValidatingEntity)entry.Entity).Validate();
  33:                 }
  34:  
  35:                 if (entry.Entity is ISrsEntity)
  36:                 {
  37:                     ApplyLastModifiedInformation((ISrsEntity)entry.Entity);
  38:                 }
  39:             }
  40:         }
  41:  
  42:         List<IObjectWithChangeTracker> entitiesToReset = null;
  43:  
  44:         if ((options & SaveOptions.AcceptAllChangesAfterSave) == SaveOptions.AcceptAllChangesAfterSave)
  45:         {
  46:             var q = from entry in ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified)
  47:                     where entry.Entity is IObjectWithChangeTracker
  48:                     select (IObjectWithChangeTracker)entry.Entity;
  49:  
  50:             entitiesToReset = q.ToList();
  51:         }
  52:  
  53:         var result = base.SaveChanges(options);
  54:  
  55:         if (entitiesToReset != null)
  56:         {
  57:             foreach (var entity in entitiesToReset)
  58:             {
  59:                 entity.AcceptChanges();
  60:             }
  61:         }
  62:  
  63:         return result;
  64:     }
  65:  
  66:     /// <summary>
  67:     /// Commits this instance.
  68:     /// </summary>
  69:     public void Flush()
  70:     {
  71:         this.SaveChanges();
  72:     }
  73:  
  74:     /// <summary>
  75:     /// Rollbacks this instance.
  76:     /// </summary>
  77:     public void Rollback()
  78:     {
  79:         // rolling back invalidates this object context for any future use, so dispose it.
  80:         Dispose();
  81:     }
  82:  
  83:     /// <summary>
  84:     /// Writes the last modified date/time and user information to the entity.
  85:     /// </summary>
  86:     /// <param name="entity">Entity to apply last modified info to.</param>
  87:     private static void ApplyLastModifiedInformation(ISrsEntity entity)
  88:     {
  89:         entity.LastModified = DateTime.Now;
  90:         if (ServiceSecurityContext.Current != null)
  91:         {
  92:             entity.ModifiedBy = ServiceSecurityContext.Current.WindowsIdentity.Name;
  93:         }
  94:     }
  95: }

Not much to see here, moving on… Ignore the nastiness of the SaveChanges call and the AcceptChanges loop. Once again, Entity Framework has some issues.

UnitOfWorkScope:

   1: /// <summary>
   2: /// Unit of work scope is the outward facing wrapper for developers to begin/enlist in a transaction. Scopes can be nested, in which case, once the last or outtermost
   3: /// scope is committed then the unit of work will be flushed/committed. If any scope exists/disposes before committing it will be rolled back, and any
   4: /// scopes upward in the chain will be rolled back as well.
   5: /// </summary>
   6: public class UnitOfWorkScope : IUnitOfWorkScope, IDisposable
   7: {
   8:     private bool disposed;
   9:     private bool commitAttempted;
  10:     private IUnitOfWorkTransaction transaction;
  11:     private ITransactionManager transactionManager;
  12:  
  13:     /// <summary>
  14:     /// Initializes a new instance of the <see cref="UnitOfWorkScope"/> class with the <see cref="System.Data.IsolationLevel.Serializable"/>
  15:     /// transaction isolation level.
  16:     /// </summary>
  17:     public UnitOfWorkScope()
  18:         : this(false)
  19:     {
  20:     }
  21:  
  22:     /// <summary>
  23:     /// Initializes a new instance of the <see cref="UnitOfWorkScope"/> class.
  24:     /// </summary>
  25:     /// <param name="newTransaction">To create a new scope that does not enlist in an existing ambient
  26:     /// <see cref="UnitOfWorkScope"/> or <see cref="TransactionScope"/>, specify new, otherwise specify false.</param>
  27:     public UnitOfWorkScope(bool newTransaction)
  28:     {
  29:         transaction = TransactionManager.EnlistScope(this, newTransaction);
  30:         transaction.TransactionDisposing += new EventHandler<EventArgs>(OnTransactionDisposing);
  31:         ScopeStatus = UnitOfWorkScopeStatus.Active;
  32:     }
  33:  
  34:     /// <summary>
  35:     /// Initializes a new instance of the <see cref="UnitOfWorkScope"/> class and enlists in a specific existing transaction.
  36:     /// </summary>
  37:     /// <param name="transaction">The transaction to enlist the new scope in.</param>
  38:     public UnitOfWorkScope(IUnitOfWorkTransaction transaction)
  39:     {
  40:         if (transaction != null)
  41:         {
  42:             this.transaction = transaction;
  43:             transaction.EnlistScope(this);
  44:             ScopeStatus = UnitOfWorkScopeStatus.Active;
  45:             transaction.TransactionDisposing += new EventHandler<EventArgs>(OnTransactionDisposing);
  46:         }
  47:         else
  48:         {
  49:             // if the transaction wasn't specified then call the transaction manager to enlist the scope as it will find/create a transaction for the scope to attach to.
  50:             transaction = TransactionManager.EnlistScope(this, false);
  51:             ScopeStatus = UnitOfWorkScopeStatus.Active;
  52:             transaction.TransactionDisposing += new EventHandler<EventArgs>(OnTransactionDisposing);
  53:         }
  54:     }
  55:  
  56:     /// <summary>
  57:     /// Event fired when the scope is comitting.
  58:     /// </summary>
  59:     public event EventHandler<EventArgs> ScopeComitting;
  60:  
  61:     /// <summary>
  62:     /// Event fired when the scope is rollingback.
  63:     /// </summary>
  64:     public event EventHandler<EventArgs> ScopeRollingback;
  65:  
  66:     /// <summary>
  67:     /// Gets the current scope status.
  68:     /// </summary>
  69:     /// <value>The scope status.</value>
  70:     public UnitOfWorkScopeStatus ScopeStatus
  71:     {
  72:         get;
  73:         private set;
  74:     }
  75:  
  76:     /// <summary>
  77:     /// Gets the transaction manager.
  78:     /// </summary>
  79:     /// <value>The transaction manager.</value>
  80:     private ITransactionManager TransactionManager
  81:     {
  82:         get
  83:         {
  84:             if (transactionManager == null)
  85:             {
  86:                 transactionManager = ServiceLocator.Current.GetInstance<ITransactionManager>();
  87:             }
  88:  
  89:             return transactionManager;
  90:         }
  91:     }
  92:  
  93:     /// <summary>
  94:     /// Commits the current running transaction in the scope.
  95:     /// </summary>
  96:     public void Commit()
  97:     {
  98:         if (disposed)
  99:         {
 100:             throw new ObjectDisposedException("Cannot commit a disposed UnitOfWorkScope instance.");
 101:         }
 102:  
 103:         commitAttempted = true;
 104:  
 105:         if (ScopeComitting != null)
 106:         {
 107:             ScopeComitting(this, EventArgs.Empty);
 108:         }
 109:  
 110:         if (transaction != null)
 111:         {
 112:             transaction.CommitScope(this);
 113:         }
 114:  
 115:         ScopeStatus = UnitOfWorkScopeStatus.Complete;
 116:     }
 117:  
 118:     /// <summary>
 119:     /// Rollback the unit of work.
 120:     /// </summary>
 121:     public void Rollback()
 122:     {
 123:         if (ScopeRollingback != null)
 124:         {
 125:             ScopeRollingback(this, EventArgs.Empty);
 126:         }
 127:  
 128:         if (transaction != null)
 129:         {
 130:             transaction.RollbackScope(this);
 131:         }
 132:  
 133:         ScopeStatus = UnitOfWorkScopeStatus.Complete;
 134:     }
 135:  
 136:     /// <summary>
 137:     /// Disposes off the <see cref="UnitOfWorkScope"/> insance.
 138:     /// </summary>
 139:     public void Dispose()
 140:     {
 141:         Dispose(true);
 142:         GC.SuppressFinalize(this);
 143:     }
 144:  
 145:     /// <summary>
 146:     /// Disposes off the managed and un-managed resources used.
 147:     /// </summary>
 148:     /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
 149:     private void Dispose(bool disposing)
 150:     {
 151:         if (disposed)
 152:         {
 153:             return;
 154:         }
 155:  
 156:         if (disposing)
 157:         {
 158:             try
 159:             {
 160:                 if (!commitAttempted)
 161:                 {
 162:                     Rollback();
 163:                 }
 164:             }
 165:             finally
 166:             {
 167:                 ScopeComitting = null;
 168:                 ScopeRollingback = null;
 169:                 disposed = true;
 170:             }
 171:         }
 172:     }
 173:  
 174:     /// <summary>
 175:     /// Handles the TransactionDisposing event of the transaction control.
 176:     /// </summary>
 177:     /// <param name="sender">The source of the event.</param>
 178:     /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
 179:     private void OnTransactionDisposing(object sender, EventArgs e)
 180:     {
 181:         transaction.TransactionDisposing -= new EventHandler<EventArgs>(OnTransactionDisposing);
 182:         transaction = null;
 183:         ScopeStatus = UnitOfWorkScopeStatus.Complete;
 184:     }
 185: }

Again pretty straight forward. The TransactionManager that the scope uses for enlisting is service located, eventually pulled from Unity. This is done to keep the usage scenario fluent, otherwise each time a scope is instantiated a TransactionManager would have to be found to inject into the scope. That would either result in a lot of calls to service location spread around the code, or in the Unity container being passed around. Both of which are undesirable.

Take note, too, that disposing a scope will automatically trigger a rollback unless the scope was explicitly committed prior to the disposal via a call to Commit(). Also note, that additional flexibility is provided via the constructor overloads, allowing a UnitOfWorkScope to explicitly begin a new transaction or to enlist in a specific existing transaction. The default behavior is to enlist in the existing ambient transaction if one exists, otherwise to create one.

TransactionManager:

   1: /// <summary>
   2: /// The Transaction Manager holds a list of active, isolated transactions within the system and acts as the entry point for a new unit of work scope. 
   3: /// </summary>
   4: public class TransactionManager : ITransactionManager, IDisposable
   5: {
   6:     /// <summary>
   7:     /// Dictionary of operation contexts to transactions operating under that context
   8:     /// </summary>
   9:     private readonly Dictionary<Guid, List<IUnitOfWorkTransaction>> transactionLists = new Dictionary<Guid, List<IUnitOfWorkTransaction>>();
  10:  
  11:     [ThreadStatic]
  12:     private static Guid operationContext;
  13:  
  14:     private bool disposed;
  15:     private object transactionsLockObject = new object();
  16:     private IUnitOfWorkTransactionFactory transactionFactory;
  17:  
  18:     /// <summary>
  19:     /// Initializes a new instance of the <see cref="TransactionManager"/> class.
  20:     /// </summary>
  21:     /// <param name="transactionFactory">The transaction factory.</param>
  22:     public TransactionManager(IUnitOfWorkTransactionFactory transactionFactory)
  23:     {
  24:         this.transactionFactory = transactionFactory;
  25:     }
  26:  
  27:     /// <summary>
  28:     /// Gets the current context of the most recent transaction's unit of work.
  29:     /// </summary>
  30:     /// <value>The current context.</value>
  31:     public ObjectContext CurrentContext
  32:     {
  33:         get
  34:         {
  35:             if (CurrentTransaction != null)
  36:             {
  37:                 return CurrentTransaction.UnitOfWork.Context;
  38:             }
  39:             else
  40:             {
  41:                 return null;
  42:             }
  43:         }
  44:     }
  45:  
  46:     /// <summary>
  47:     /// Gets or sets the operation context that the current thread is running under.
  48:     /// Transactions are tied to an operation context, so that additional scopes that join a transaction should only join if they belong to the same context.
  49:     /// This value is currently being set in the AuthorizationPolicy so that each call to WCF tags its thread with a unique context. If new threads are spawned
  50:     /// by processes, they must manually copy the operation context from the main thread to their sub-threads.
  51:     /// </summary>
  52:     /// <value>The operation context, unique per wcf call.</value>
  53:     /// <param name="operationContext">The operation context.</param>
  54:     public Guid OperationContext
  55:     {
  56:         get
  57:         {
  58:             return operationContext;
  59:         }
  60:  
  61:         set
  62:         {
  63:             operationContext = value;
  64:         }
  65:     }
  66:  
  67:     /// <summary>
  68:     /// Gets the most recent transaction.
  69:     /// </summary>
  70:     /// <value>The current transaction.</value>
  71:     private IUnitOfWorkTransaction CurrentTransaction
  72:     {
  73:         get
  74:         {
  75:             lock (transactionsLockObject)
  76:             {
  77:                 List<IUnitOfWorkTransaction> transactions;
  78:                 if (transactionLists.TryGetValue(OperationContext, out transactions))
  79:                 {
  80:                     if (transactions.Count > 0)
  81:                     {
  82:                         return transactions[transactions.Count - 1];
  83:                     }
  84:                 }
  85:  
  86:                 return null;
  87:             }
  88:         }
  89:     }
  90:  
  91:     /// <summary>
  92:     /// Enlists a <see cref="UnitOfWorkScope"/> instance with the transaction manager.
  93:     /// </summary>
  94:     /// <param name="scope">bool. True if the scope should be enlisted in a new transaction, else
  95:     /// false if the scope should participate in the existing transaction</param>
  96:     /// <param name="newTransaction">if set to <c>true</c> [new transaction].</param>
  97:     /// <returns>
  98:     /// Transcation created or assigned to the scope.
  99:     /// </returns>
 100:     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed outside of this scope.")]
 101:     public IUnitOfWorkTransaction EnlistScope(IUnitOfWorkScope scope, bool newTransaction)
 102:     {
 103:         if (newTransaction || CurrentTransaction == null)
 104:         {
 105:             IUnitOfWorkTransaction transaction = transactionFactory.CreateUnitOfWorkTransaction(OperationContext);
 106:             transaction.TransactionDisposing += OnTransactionDisposing;
 107:  
 108:             lock (transactionsLockObject)
 109:             {
 110:                 transaction.EnlistScope(scope);
 111:                 List<IUnitOfWorkTransaction> transactions;
 112:                 if (!transactionLists.TryGetValue(OperationContext, out transactions))
 113:                 {
 114:                     transactions = new List<IUnitOfWorkTransaction>();
 115:                     transactionLists.Add(OperationContext, transactions);
 116:                 }
 117:  
 118:                 transactions.Add(transaction);
 119:             }
 120:  
 121:             return transaction;
 122:         }
 123:         else
 124:         {
 125:             CurrentTransaction.EnlistScope(scope);
 126:             return CurrentTransaction;
 127:         }
 128:     }
 129:  
 130:     /// <summary>
 131:     /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
 132:     /// </summary>
 133:     /// <filterpriority>2</filterpriority>
 134:     public void Dispose()
 135:     {
 136:         Dispose(true);
 137:         GC.SuppressFinalize(this);
 138:     }
 139:  
 140:     /// <summary>
 141:     /// Releases unmanaged and - optionally - managed resources
 142:     /// </summary>
 143:     /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
 144:     private void Dispose(bool disposing)
 145:     {
 146:         if (disposed)
 147:         {
 148:             return;
 149:         }
 150:  
 151:         if (disposing)
 152:         {
 153:             lock (transactionsLockObject)
 154:             {
 155:                 if (transactionLists != null && transactionLists.Count > 0)
 156:                 {
 157:                     foreach (var kvp in transactionLists)
 158:                     {
 159:                         foreach (var transaction in kvp.Value)
 160:                         {
 161:                             transaction.TransactionDisposing -= OnTransactionDisposing;
 162:                             transaction.Dispose();
 163:                         }
 164:                     }
 165:  
 166:                     transactionLists.Clear();
 167:                 }
 168:             }
 169:         }
 170:  
 171:         disposed = true;
 172:     }
 173:  
 174:     /// <summary>
 175:     /// Called when [transaction disposing].
 176:     /// </summary>
 177:     /// <param name="sender">The sender.</param>
 178:     /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
 179:     private void OnTransactionDisposing(object sender, EventArgs e)
 180:     {
 181:         IUnitOfWorkTransaction transaction = sender as IUnitOfWorkTransaction;
 182:         if (transaction != null)
 183:         {
 184:             transaction.TransactionDisposing -= OnTransactionDisposing;
 185:             lock (transactionsLockObject)
 186:             {
 187:                 List<IUnitOfWorkTransaction> transactions;
 188:                 if (transactionLists.TryGetValue(transaction.OperationContext, out transactions))
 189:                 {
 190:                     if (transactions.Contains(transaction))
 191:                     {
 192:                         transactions.Remove(transaction);
 193:                     }
 194:                 }
 195:             }
 196:         }
 197:     }

It’s getting a little more interesting now. One TransactionManager will exist for the entire WCF service, requiring some coordination of the active transactions, especially when one thinks about the threading that is happening in the service. The private field transactionLists, tracks all open transactions by their OperationContext. The OperationContext is simply a Guid that is set at the very beginning of each Wcf Service call. Take special note that the context is marked as ThreadStatic, so that one instance of operationContext exists for each thread, even though there is only one instance of the TransactionManager for the service as a whole. If a job process instantiates child threads to complete its work, it is the responsibility of the coder at that time to carry the operation context through and to set it on the TransactionManager.

UnitOfWorkTransaction:

   1: /// <summary>
   2: /// Unit Of Work Transaction keeps a list of active scopes for a given transaction and commits the unit of work if all scopes have been committed.
   3: /// </summary>
   4: public class UnitOfWorkTransaction : IUnitOfWorkTransaction, IDisposable
   5: {
   6:     private bool disposed;
   7:     private IUnitOfWork unitOfWork;
   8:     private IList<IUnitOfWorkScope> attachedScopes = new List<IUnitOfWorkScope>();
   9:     private object attachedScopesLock = new object();
  10:  
  11:     /// <summary>
  12:     /// Initializes a new instance of the <see cref="UnitOfWorkTransaction"/> class.
  13:     /// </summary>
  14:     /// <param name="unitOfWorkFactory">The context factory; used to create a new unit of work instance.</param>
  15:     /// <param name="operationContext">The operation context, a unique identifier for each WCF call in our system, stored in TransactionManager's thread static property.</param>
  16:     public UnitOfWorkTransaction(IUnitOfWorkFactory unitOfWorkFactory, Guid operationContext)
  17:     {
  18:         if (unitOfWorkFactory == null)
  19:         {
  20:             throw new ArgumentNullException("unitOfWorkFactory");
  21:         }
  22:  
  23:         this.unitOfWork = unitOfWorkFactory.CreateUnitOfWork();
  24:  
  25:         this.OperationContext = operationContext;
  26:     }
  27:  
  28:     /// <summary>
  29:     /// Raised when the transaction is disposing.
  30:     /// </summary>
  31:     public event EventHandler<EventArgs> TransactionDisposing;
  32:  
  33:     /// <summary>
  34:     /// Gets the <see cref="IUnitOfWork"/> instance managed by the 
  35:     /// <see cref="UnitOfWorkTransaction"/> instance.
  36:     /// </summary>
  37:     public IUnitOfWork UnitOfWork
  38:     {
  39:         get { return unitOfWork; }
  40:     }
  41:  
  42:     /// <summary>
  43:     /// Gets the thread id for the transaction. Transactions are tied to a thread, so that additional scopes that join a transaction should only join if they belong to the same thread.
  44:     /// </summary>
  45:     /// <value>The thread id.</value>
  46:     public Guid OperationContext
  47:     {
  48:         get;
  49:         private set;
  50:     }
  51:  
  52:     /// <summary>
  53:     /// Attaches a <see cref="UnitOfWorkScope"/> instance to the 
  54:     /// <see cref="UnitOfWorkTransaction"/> instance.
  55:     /// </summary>
  56:     /// <param name="scope">The <see cref="UnitOfWorkScope"/> instance to attach.</param>
  57:     public void EnlistScope(IUnitOfWorkScope scope)
  58:     {
  59:         if (scope == null)
  60:         {
  61:             throw new ArgumentNullException("scope");
  62:         }
  63:  
  64:         if (!attachedScopes.Contains(scope))
  65:         {
  66:             lock (attachedScopesLock)
  67:             {
  68:                 if (!attachedScopes.Contains(scope))
  69:                 {
  70:                     attachedScopes.Add(scope);
  71:                 }
  72:             }
  73:         }
  74:     }
  75:  
  76:     /// <summary>
  77:     /// Callback executed when an enlisted scope has comitted.
  78:     /// </summary>
  79:     /// <param name="scope">The scope.</param>
  80:     public void CommitScope(IUnitOfWorkScope scope)
  81:     {
  82:         if (disposed)
  83:         {
  84:             throw new ObjectDisposedException("The transaction attached to the scope has already been disposed.");
  85:         }
  86:  
  87:         lock (attachedScopesLock)
  88:         {
  89:             if (!attachedScopes.Contains(scope))
  90:             {
  91:                 throw new InvalidOperationException("The scope being comitted is not attached to the current transaction.");
  92:             }
  93:  
  94:             attachedScopes.Remove(scope);
  95:             if (attachedScopes.Count == 0)
  96:             {
  97:                 try
  98:                 {
  99:                     unitOfWork.Flush();
 100:                 }
 101:                 finally
 102:                 {
 103:                     // Dispose the transaction after comitting.
 104:                     Dispose();
 105:                 }
 106:             }
 107:         }
 108:     }
 109:  
 110:     /// <summary>
 111:     /// Callback executed when an enlisted scope is rolledback.
 112:     /// </summary>
 113:     /// <param name="scope">The scope.</param>
 114:     public void RollbackScope(IUnitOfWorkScope scope)
 115:     {
 116:         if (disposed)
 117:         {
 118:             throw new ObjectDisposedException("The transaction attached to the scope has already been disposed.");
 119:         }
 120:  
 121:         // dispose this transcation which will notify via TransactionDisposing all scopes that were attached that the 
 122:         // transaction is being rolled back
 123:         Dispose();
 124:     }
 125:  
 126:     /// <summary>
 127:     /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
 128:     /// </summary>
 129:     /// <filterpriority>2</filterpriority>
 130:     public void Dispose()
 131:     {
 132:         Dispose(true);
 133:         GC.SuppressFinalize(this);
 134:     }
 135:  
 136:     /// <summary>
 137:     /// Releases unmanaged and - optionally - managed resources
 138:     /// </summary>
 139:     /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
 140:     private void Dispose(bool disposing)
 141:     {
 142:         if (disposed)
 143:         {
 144:             return;
 145:         }
 146:  
 147:         if (disposing)
 148:         {
 149:             // notify all who care that this transaction is done.
 150:             if (TransactionDisposing != null)
 151:             {
 152:                 TransactionDisposing(this, EventArgs.Empty);
 153:             }
 154:  
 155:             // dispose of the unit of work since it was created for this transaction specifically
 156:             if (unitOfWork != null)
 157:             {
 158:                 unitOfWork.Dispose();
 159:             }
 160:  
 161:             lock (attachedScopesLock)
 162:             {
 163:                 attachedScopes.Clear();
 164:                 attachedScopes = null;
 165:             }
 166:  
 167:             TransactionDisposing = null;
 168:             unitOfWork = null;
 169:             disposed = true;
 170:         }
 171:     }
 172: }

Nothing surprising in this implementation given the previous discussion. The UnitOfWorkTransaction then keeps a list of active scopes for the transaction. When any of the scopes is rolled back, the entire transaction is torn down. Once the last scope is committed, assuming no roll backs have occurred, the UnitOfWorkTransaction flushes the IUnitOfWork effectively calling SaveChanges on the ObjectContext.

That’s it folks. I left out a few of the factor interfaces as they are just boring.

Tags: , ,

Programming

Repository Pattern with Entity Framework 4

by Mark Shiffer 28. June 2010 13:41

An original post for once! :-)

I am in the process of bringing up a new architecture for a project that I am working on that uses MVVM with WPF, WCF, Unity and Entity Framework 4 with Self Tracking Entities. One of the foundational items that is necessary to create as part of this architecture is a Repository to abstract the data store. As a related item, a proper Unit of Work pattern needs to be instantiated in order to provide transactional boundaries for some of the data processes. I will talk more about Unit of Work in my next post, but the following is what I came up with to implement the Repository pattern.

Before I started designing my approach I read several different attempts by others at implementing these patterns. Some were better than others, but in the end no one approach seemed to be a best fit for my application. However, the heaviest influence on my design came from NCommon, a library that contains implementations of commonly used design patterns when developing applications. If you haven’t looked at NCommon before, I would suggest taking a gander as it has some interesting stuff. However, for my purposes, it introduced too much complexity by trying to be a generic framework that applied to several different technologies, whereas, directing my approach to my specific set of technologies (while still abstracting mind you), made the code much more straight forward.

The Repository interface contains the standard set of Find methods that you would expect a repository to have using predicate Expressions in order to pass through to LINQ. This makes it extremely flexible and easy to use while still remaining type safe to call. In addition, two ApplyChanges methods are provided to allow for add, modify, deletes via Self-Tracking Entities, and a Refresh method to force an entity to re-load its values from the data store.

Repository Interface:

   1: /// <summary>Base interface for all repositories 
   2: /// </summary>
   3: /// <typeparam name="T">Aggregate type the repository is serving</typeparam>
   4: public interface IRepository<T> : IDisposable where T : class, IObjectWithChangeTracker
   5: {
   6:     /// <summary>
   7:     /// Return the entire set of persisted items
   8:     /// </summary>
   9:     /// <param name="includes">Sub-entities to include.</param>
  10:     /// <returns>Collection of all persisted items</returns>
  11:     IList<T> FindAll(params string[] includes);
  12:  
  13:     /// <summary>
  14:     /// The first element in source that passes the test in predicate
  15:     /// </summary>
  16:     /// <param name="predicate">The predicate.</param>
  17:     /// <param name="includes">The includes.</param>
  18:     /// <returns>The first element in source that passes the test in predicate.</returns>
  19:     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Intended")]
  20:     T First(Expression<Func<T, bool>> predicate, params string[] includes);
  21:  
  22:     /// <summary>
  23:     /// Gets the first element of a sequence, or a default value if the sequence
  24:     /// contains no elements.
  25:     /// </summary>
  26:     /// <param name="predicate">The predicate.</param>
  27:     /// <param name="includes">The includes.</param>
  28:     /// <returns>Returns the first element of a sequence, or a default value if the sequence
  29:     /// contains no elements.</returns>
  30:     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Intended")]
  31:     T FirstOrDefault(Expression<Func<T, bool>> predicate, params string[] includes);
  32:  
  33:     /// <summary>
  34:     /// Gets the single element of the input sequence.
  35:     /// </summary>
  36:     /// <param name="predicate">The predicate.</param>
  37:     /// <param name="includes">The includes.</param>
  38:     /// <returns>The single element of the input sequence.</returns>
  39:     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Intended"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "Single", Justification = "Intended")]
  40:     T Single(Expression<Func<T, bool>> predicate, params string[] includes);
  41:  
  42:     /// <summary>
  43:     /// Gest a collection of all persisted items that pass the test in the predicate
  44:     /// </summary>
  45:     /// <param name="predicate">The predicate.</param>
  46:     /// <param name="includes">The includes.</param>
  47:     /// <returns>Collection of all persisted items that pass the test in the predicate</returns>
  48:     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "Intended")]
  49:     IList<T> Find(Expression<Func<T, bool>> predicate, params string[] includes);
  50:  
  51:     /// <summary>
  52:     /// Persist changes a single entity to storage
  53:     /// </summary>
  54:     /// <param name="toSave">Entity to save</param>
  55:     void ApplyChanges(T toSave);
  56:  
  57:     /// <summary> Persist changes a set of entities to storage
  58:     /// </summary>
  59:     /// <param name="toSave">Collection of entities to save</param>
  60:     void ApplyChanges(IEnumerable<T> toSave);
  61:  
  62:     /// <summary>
  63:     /// Refreshes the specified entities from the data store.
  64:     /// </summary>
  65:     /// <param name="entities">The entities.</param>
  66:     void Refresh(params T[] entities);
  67: }

Two generic implementations are provided. In order to deal with how Entity Framework handles the hierarchy of an aggregate, we need to know about both the base type of the aggregate and the final concrete type. For example, if you have an Employee entity that descends from a person Entity, the ObjectSet in Entity Framework is retrieved via Person and the OfType<T>() method is used with the concrete type to get only Employee entities. So, Repository<BaseType> is provided as a quick way to access the repository when there is no hierarchy (BaseType=ConcreteType). Repository<BaseType, ConcreteType> is where the meat of the implementation is.

The majority of the class is self-explanatory from the code, just a couple of things to mention:

1. Object Sets – I customized the T4 template for the Entity Framework ObjectContext to include a GetObjectSet method. This method allows me to tap into the helper object sets that the context already contains in a generic manner and reuse them across the life of a context.

2. ApplyToContext – This method is a bit messy, frankly, because Entity Framework itself is a bit messy at times. The ApplyChanges method of the ObjectContext in Entity Framework only works when the entity you are trying to save was loaded from outside of the context, otherwise the changes are not recognized properly. So I make a call to TryGetObjectStateEntry to see if my entity was previously loaded and then call ChangeObjectState for a delete or ApplyCurrentValues for a modify based upon the Self Tracking Entities state.

Repository Implementation:

   1: /// <summary>
   2: /// Simple entity repository
   3: /// </summary>
   4: /// <typeparam name="BaseType">Base class type of entity being managed by the repository</typeparam>
   5: [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleClass", Justification = "Makes sense in this circumstance.")]
   6: public class Repository<BaseType> : Repository<BaseType, BaseType>
   7:     where BaseType : class, IObjectWithChangeTracker
   8: {
   9:     /// <summary>
  10:     /// Initializes a new instance of the <see cref="Repository&lt;BaseType&gt;"/> class.
  11:     /// </summary>
  12:     /// <param name="contextFactory">The context factory.</param>
  13:     /// <param name="transactionManager">The transaction manager.</param>
  14:     public Repository(IContextFactory contextFactory, ITransactionManager transactionManager)
  15:         : base(contextFactory, transactionManager)
  16:     {
  17:     }
  18: }
  19:  
  20: /// <summary>Base class for entity repositories 
  21: /// </summary>
  22: /// <typeparam name="BaseType">Base class type of entity being managed by the repository</typeparam>
  23: /// <typeparam name="ConcreteType">Concrete (derived) class type of entity being managed by the repository</typeparam>
  24: public class Repository<BaseType, ConcreteType> : IRepository<ConcreteType>, IDisposable
  25:     where BaseType : class, IObjectWithChangeTracker
  26:     where ConcreteType : class, BaseType
  27: {
  28:     private ITransactionManager transactionManager;
  29:     private ObjectContext localContext;
  30:     private bool disposed;
  31:  
  32:     // hide the context from descendants, they should not be able to get to a context directly if we can help it. They can get to it through the ObjectSet which
  33:     // we can't hide, but make it difficult to keep the repository single focused.
  34:     private IContextFactory contextFactory;
  35:  
  36:     /// <summary>
  37:     /// Initializes a new instance of the <see cref="Repository{BaseType,ConcreteType}"/> class.
dding-right: 0px; font-family: 'Courier New', courier, monospace; direction: ltr; border-top-style: none; color: black; font-size: 8pt; border-left-style: none; overflow: visible; padding-top: 0px">  38:     /// </summary>
  39:     /// <param name="contextFactory">Factory that generates ObjectContext objects.</param>
  40:     /// <param name="transactionManager">The transaction manager.</param>
  41:     public Repository(IContextFactory contextFactory, ITransactionManager transactionManager)
  42:     {
  43:         this.transactionManager = transactionManager;
  44:         this.contextFactory = contextFactory;
  45:     }
  46:  
  47:     /// <summary>
  48:     /// Gets the object set to act on given the current transaction state
  49:     /// </summary>
  50:     /// <value>The current object set.</value>
  51:     /// <returns>The ObjectSet to use for this repository</returns>
  52:     protected ObjectSet<BaseType> CurrentObjectSet
  53:     {
  54:         get
  55:         {
  56:             // if currently in a transaction, then assume it's context, otherwise use a locally created one.
  57:             ObjectContext context = TransactionManager != null ? TransactionManager.CurrentContext : null;
  58:  
  59:             // create a context and manage its life if we are not already under a transaction scope
  60:             if (context == null)
  61:             {
  62:                 if (localContext == null)
  63:                 {
  64:                     localContext = contextFactory.CreateContext();
  65:                 }
  66:  
  67:                 context = localContext;
  68:             }
  69:  
  70:             return ((SrsModelContainer)context).GetObjectSet<BaseType>();
  71:         }
  72:     }
  73:  
  74:     /// <summary>
  75:     /// Gets the transaction manager.
  76:     /// </summary>
  77:     /// <value>The transaction manager.</value>
  78:     private ITransactionManager TransactionManager
  79:     {
  80:         get
  81:         {
  82:             return transactionManager;
  83:         }
  84:     }
  85:  
  86:     /// <summary>
  87:     /// Gets the first element in source that passes the test in predicate.
  88:     /// </summary>
  89:     /// <param name="predicate">The predicate.</param>
  90:     /// <param name="includes">The includes.</param>
  91:     /// <returns>
  92:     /// The first element in source that passes the test in predicate.
  93:     /// </returns>
  94:     public ConcreteType First(Expression<Func<ConcreteType, bool>> predicate, params string[] includes)
  95:     {
  96:         return GetObjectSet(includes).First(predicate);
  97:     }
  98:  
  99:     /// <summary>
 100:     /// Returns the first element of a sequence, or a default value if the sequence
 101:     /// contains no elements
 102:     /// </summary>
 103:     /// <param name="predicate">The predicate.</param>
 104:     /// <param name="includes">The includes.</param>
 105:     /// <returns>Returns the first element of a sequence, or a default value if the sequence
 106:     /// contains no elements.</returns>
 107:     public ConcreteType FirstOrDefault(Expression<Func<ConcreteType, bool>> predicate, params string[] includes)
 108:     {
 109:         return GetObjectSet(includes).FirstOrDefault(predicate);
 110:     }
 111:  
 112:     /// <summary>
 113:     /// The single element of the input sequence
 114:     /// </summary>
 115:     /// <param name="predicate">The predicate.</param>
 116:     /// <param name="includes">The includes.</param>
 117:     /// <returns>The single element of the input sequence.</returns>
 118:     public ConcreteType Single(Expression<Func<ConcreteType, bool>> predicate, params string[] includes)
 119:     {
 120:         return GetObjectSet(includes).Single(predicate);
 121:     }
 122:  
 123:     /// <summary>
 124:     /// Gets a collection of all persisted items that pass the test in the predicate
 125:     /// </summary>
 126:     /// <param name="predicate">The predicate.</param>
 127:     /// <param name="includes">The includes.</param>
 128:     /// <returns>Collection of all persisted items that pass the test in the predicate</returns>
 129:     public IList<ConcreteType> Find(Expression<Func<ConcreteType, bool>> predicate, params string[] includes)
 130:     {
 131:         return GetObjectSet(includes).Where(predicate).ToList();
 132:     }
 133:  
 134:     /// <summary>
 135:     /// Gets a collection of all persisted items
 136:     /// </summary>
 137:     /// <param name="includes">Sub-entities to include.</param>
 138:     /// <returns>Collection of all persisted items</returns>
 139:     public virtual IList<ConcreteType> FindAll(params string[] includes)
 140:     {
 141:         return GetObjectSet(includes).ToList();
 142:     }
 143:  
 144:     /// <summary>
 145:     /// Applies the changes for a single entity to the context
 146:     /// </summary>
 147:     /// <param name="toSave">Modified entity to apply changes for.</param>
 148:     public virtual void ApplyChanges(ConcreteType toSave)
 149:     {
 150:         ValidateAggregate(toSave);
 151:  
 152:         ApplyToContext(toSave);
 153:  
 154:         // if no current transaction, then save, otherwise it belongs to the unit of work scope
 155:         if (TransactionManager == null || TransactionManager.CurrentContext == null)
 156:         {
 157:             GetObjectSet().Context.SaveChanges();
 158:         }
 159:     }
 160:  
 161:     /// <summary> Applies the changes for a collection of entities to the context
 162:     /// </summary>
 163:     /// <param name="toSave">Collection of modified entities to apply changes for.</param>
 164:     public virtual void ApplyChanges(IEnumerable<ConcreteType> toSave)
 165:     {
 166:         foreach (var item in toSave)
 167:         {
 168:             ValidateAggregate(item);
 169:  
 170:             ApplyToContext(item);
 171:         }
 172:  
 173:         // if no current transaction, then save, otherwise it belongs to the unit of work scope
 174:         if (TransactionManager == null || TransactionManager.CurrentContext == null)
 175:         {
 176:             GetObjectSet().Context.SaveChanges();
 177:         }
 178:     }
 179:  
 180:     /// <summary>
 181:     /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
 182:     /// </summary>
 183:     public void Dispose()
 184:     {
 185:         this.Dispose(true);
 186:         GC.SuppressFinalize(this);
 187:     }
 188:  
 189:     /// <summary>
 190:     /// Refreshes the specified entities from the data store.
 191:     /// </summary>
 192:     /// <param name="entities">The entities.</param>
 193:     public void Refresh(params ConcreteType[] entities)
 194:     {
 195:         CurrentObjectSet.Context.Refresh(RefreshMode.StoreWins, entities);
 196:     }
 197:  
 198:     /// <summary>
 199:     /// Validates the specified record to save and throws ValidationException if condition is not met
 200:     /// </summary>
 201:     /// <param name="toSave">The record to save.</param>
 202:     protected virtual void ValidateAggregate(ConcreteType toSave)
 203:     {
 204:         // no default implementation
 205:     }
 206:  
 207:     /// <summary>
 208:     /// Gets the object set.
 209:     /// </summary>
 210:     /// <param name="includes">The includes.</param>
 211:     /// <returns>
 212:     /// Object set with the specified dependent objects included.
 213:     /// </returns>
 214:     protected ObjectQuery<ConcreteType> GetObjectSet(params string[] includes)
 215:     {
 216:         var objectSet = CurrentObjectSet.OfType<ConcreteType>();
 217:         foreach (var include in includes)
 218:         {
 219:             objectSet = objectSet.Include(include);
 220:         }
 221:  
 222:         return objectSet;
 223:     }
 224:  
 225:     /// <summary>
 226:     /// Applies changes for the given entity to context.
 227:     /// </summary>
 228:     /// <param name="entityToSave">The entity to save.</param>
 229:     protected void ApplyToContext(ConcreteType entityToSave)
 230:     {
 231:         // If the object toSave was an existing record loaded from the same context that we are attempting to save it back to 
 232:         // then some special considerations are needed.
 233:         ObjectStateEntry entityStateEntry;
 234:         if (CurrentObjectSet.Context.ObjectStateManager.TryGetObjectStateEntry(entityToSave, out entityStateEntry))
 235:         {
 236:             object contextEntity = entityStateEntry.Entity;
 237:  
 238:             if (entityToSave.ChangeTracker.State == ObjectState.Deleted)
 239:             {
 240:                 // it's possible to have a different reference to the same entity (entity keys are the same). This will cause an error
 241:                 // when trying to delete, so we need to make sure that we are deleting the entity that is already in the store.
 242:                 CurrentObjectSet.Context.ObjectStateManager.ChangeObjectState(contextEntity, EntityState.Deleted);
 243:             }
 244:             else if (entityToSave.ChangeTracker.State == ObjectState.Modified)
 245:             {
 246:                 // if the entity already in the store is not the same reference as the entity we are trying to save (even though they represent the same record in the store)
 247:                 // we need to move the changes over to the record already in the store. If they are the same, the changes are already there.                        
 248:                 if (!object.ReferenceEquals(contextEntity, entityToSave))
 249:                 {
 250:                     CurrentObjectSet.Context.ApplyCurrentValues(CurrentObjectSet.EntitySet.Name, entityToSave);
 251:                 }
 252:             }
 253:         }
 254:         else
 255:         {
 256:             // Apply changes only works when the object toSave does not already have a reference in the context's ObjectStateManager
 257:             CurrentObjectSet.ApplyChanges(entityToSave);
 258:         }
 259:     }
 260:  
 261:     /// <summary>
 262:     /// Releases unmanaged and - optionally - managed resources
 263:     /// </summary>
 264:     /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
 265:     private void Dispose(bool disposing)
 266:     {
 267:         if (disposed)
 268:         {
 269:             return;
 270:         }
 271:  
 272:         if (disposing)
 273:         {
 274:             if (localContext != null)
 275:             {
 276:                 localContext.Dispose();
 277:             }
 278:         }
 279:  
 280:         disposed = true;
 281:     }
 282: }

That’s it. Next time we’ll look at the Unit of Work Pattern implementation that I came up with which is a bit more involved than the Repository.

Tags: ,

Programming

IntelliSense for PowerShell

by mark shiffer 22. June 2010 14:00

CodePlex - PowerGUI Visual Studio Extension

Project Description
The PowerGUI Visual Studio Extension adds PowerShell IntelliSense support to Visual Studio.
Make sure to install the latest version of PowerGUI before using the extension

Current Functions

  • IntelliSense support through a custom PowerShell editor
  • Syntax highlighting and script analysis
  • Supports PowerGUI settings
  • Supports PowerGUI imported modules
  • Supports PowerGUI Snippets

Tags:

Free Icons for Development

by mark shiffer 22. May 2010 04:32

Ran across an article here: 13,000+ Free Icons for Web and Desktop Developmentwith some links to free icon sets. They may come in handy at some point. The article shows the icon listings, but here are just the links:

ASP.NET Icons 
390 icons, 16x16, GIF
http://www.freeiconsdownload.com/Free_Downloads.asp?id=63

Acxialis – Web Mini, Part 1
267 icons, 16x16, PNG, GIF or ICO
http://www.axialis.com/free/icons/

Diagona
400 icons, 16x16 and 10x10, PNG
http://p.yusukekamiyamane.com/

FamFamFam – Silk Icons
1000 icons, 16x16, PNG
http://www.famfamfam.com/lab/icons/silk/

FatCow - Farm Fresh Web Icons
1400 icons, 16x16 and 32x32, PNG
http://www.fatcow.com/free-icons/

Fugue Icons
6000+ icons, 16x16, PNG
http://p.yusukekamiyamane.com/

Gallery 2 Icons
77 icons, 20x16, GIF
http://paularmstrongdesigns.com/projects/gallery-2-icons/

LED Icons Set
500+ icons, 16x16, PNG
http://led24.de/

Mini Pixel Icons
320 icons, 14x14, GIF
http://icondock.com/free/mini-pixel-icons

SplashyIcons
483 icons, 16x16, PNG
http://splashyfish.com/icons/

Tango Icon Library
645 icons, 16x16, 22x22, 32x32, PNG
http://tango.freedesktop.org/Tango_Icon_Library

TwoTiny + Expansion Pack
121 icons, 16x16, GIF
http://code.google.com/p/twotiny/

Vaga
60 icons, 16x16, PNG
http://www.tenbytwenty.com/products/icon-sets/vaga

Free Web Design Icons Set
310 icons, 16x16, PNG
http://semlabs.co.uk/journal/free-web-design-icon-set

Xiao Icon
72 icons, 16x16, PNG
http://www.ineversay.com/my-works/xiao-icon.html

Tags:

Making Your PC More Environmentally Friendly

by mark shiffer 30. April 2010 21:30

Repeated from a recent posting on Channel 10 For a Greener PC: Granola for Windows

Granola is a new software application that helps you reduce the energy waste that coming from using a computer and leaving it on all the time. The program, smaller than a single MP3, is a “set it and forget it” type of application that automatically optimizes system settings without compromising performance or availability. Granola typically lowers the total system energy use by 10-35% even when a system is 100% utilized, the website claims. It also tracks the energy saved in order to estimate cost savings and carbon emission reductions.

Granola uses a technology called dynamic voltage and frequency scaling or DVFS which is analogous to a “dimmer switch” on a light, says the company. When you don’t need the brightest light, you can dim it via a switch. DVFS works in a similar way – when it needs CPU at full speed, it allows that, but when the PC is in a lower state of activity (like reading a website instead of playing a game), it uses less energy.

You can download Granola for free from here.

Tags:

Copyright © 2001-2010 MS Consulting, Inc. All Rights Reserved.