If your app needs to impersonate, the capabilities are limited. Setting impersonation in the identity element causes the Windows Identity to be IUSR_
Wednesday, June 22, 2005
ASP.NET Apps within SharePoint
If your app needs to impersonate, the capabilities are limited. Setting impersonation in the identity element causes the Windows Identity to be IUSR_
ASP.NET Impersonation and Principals
Assumptions:
- VRoot requires authentication (anonymous disabled)
- VRoot's App Pool identity using NETWORK SERVICE
- "IEUser" is the end user
- "ImpersonatedUser" is the user config'd in the identity element
| Scenario | Page User | Thread CurrentPrincipal | WindowsIdentity |
| impersonate=false | IEUser | IEUser | NETWORK SERVICE |
| impersonate=true; userName not set | IEUser | IEUser | IEUser |
| impersonate=true; userName set | IEUser | IEUser | ImpersonatedUser |
So, the identity of System.Security.Principal.WindowsIdentity is the only one that changes. Page.User should typically be used for IsInRole checks.
Tuesday, March 08, 2005
SharePoint & WSS_Medium
Applications in SharePoint
WebParts are very similar to Server Controls in ASP.NET -- code in an assembly writes HTML to the output stream. No big deal from the output standpoint, but you don't get as much of the input benefits from ASP.NET controls (DataGrid is a good example).
Thursday, October 14, 2004
XSLT, XPath and GUID's
<xsl:template match="//Person[@PersonID='3'] >
...
</xsl:template>
But what if the unique identifier for Person nodes is a GUID?
<xsl:template match="//Person[@PersonID='
{4C22F4FA-0C4A-4FCF-85DF-F9B7A902244E}'] >
...
</xsl:template>
- What GUID format is used in the XML?
- Bracket notation?
- Hyphenated?
- Mixtures?
- With which casing are the alphabetic portion of the hex values stored in the XML?
- Upper case?
- Lower case?
- Mixture?
As you can see from just these two items, the matrix of problems expands rapidly. In the particular case I am dealing with, I am able to control format (bracketed, hyphenated), but not case. I have had to assume that case will be either upper or lower, but that mixed case will not occur (a fairly reasonable assumption since the GUIDs are not manually edited; for code to render mixed case it has to do extra work). So, my XSLT file finds the appropriate node by this method:
<xsl:param name='FindThisGuid'>
<xsl:variable name='UCaseGuid' select='translate($FindThisGuid, "abcdef", "ABCDEF")'>
<xsl:variable name='LCaseGuid' select='translate($FindThisGuid, "ABCDEF", "abcdef")'>
<xsl:template match='Person[@PersonID = $UCaseGuid) Person[@PersonID = ($LCaseGuid)]>
...
</xsl:template>
Now the Good News: XSLT 2.0 will hopefully resolve this issue by promoting GUID to a first class citizen (actual type).
Thursday, September 16, 2004
IE Gotcha! Do Not Use Self-Closing Script Tag
<script type='text/JavaScript' src='uitools.js' />
<script type='text/JavaScript'>
function DoSomething() {
//...
}
</script>
Seems harmless enough, right? For the longest time we could not determine why DoSomething was not accessible to page elements. We tried all kinds of things until we stumbled upon a surprising resolution. We began to drill in on the issue when I moved the DoSomething function into the upper script block (which required breaking open the self-closing script tag). Suddenly, page elements could use the function successfully. After we moved the function back to its rightful home, everything still worked. Then I realized that the only difference was that the upper script block was no longer self-closing. When I converted it back to self-closing, sure enough the page stopped working again.
For some reason IE (and possibly other browsers) does not handle self-closing script tags in an XML compliant manner. Go figure!
BP: 15 Minutes Isn't Enough
The person who does these builds seems to think that everyone receives the email instantaneously and that they will act on the information immediately. As is predictable, however, the builder frequently sends email along these lines, "Everybody stop checking in! I haven't been able to build." or "We will not have a build today because too many checkins occurred after the cut-off."
The problem is that people are not immediately in tune with email in some Borg-like fashion. We need consistent, dependable builds; but we're trying to deliver randomly.
Monday, September 13, 2004
How much will break?
Wednesday, September 08, 2004
Best Practices
.NET Related
Ensure all XSDs (XML Schemas) are DataSet compatible.
This doesn't mean that XSDs should be generated by a DataSet (DS) nor is msdata:IsDataSet='true' attribution required. The intent of this practice is to make XML data readily available to .NET UI components (e.g., DataGrid). If the schema is compatible, then you can create a DataSet, initialize it with the XSD, and then load a conformant XML data. Viola! Now just hook the DataSet up to the DataGrid (or other UI control) and bind.
Note also that you can create a typed DataSet from the XSD and load XML directly into it. The typed DataSet gives you a bit more capability (e.g., dataSet.People["Name"] where People is a DataTable and Name is a column in the DT)
Source Control
Even very small teams (2 people) should use a good source control mechanism. The cost of source control systems (on a per seat basis) pales in comparison to the risk of devs losing code to oversight (accidental deletion or overwrite), disk failure, time loss due to out-of-sync code, etc. It amazes me that so many teams try to "survive" without source control.
Check In Frequently
Once source control is in place, all developers should check in frequently -- at least daily. Code should be checked in after some levels of validation (it compiles, generally works, etc.), but it also needs to be acceptable to check in code that is distinctly work-in-progress. A good rule of thumb I use is: The greater the upstream dependencies, the higher the check in standard. Checking in changes to a thread pool, for example, must always compile and work very well (unless it is early enough in the project that there are no dependencies yet).
Branch Code
Branching code involves penalties, but it is your friend. My current project is losing time and is risking code losses because they don't want to branch. A major demo is coming up, so most developers are coding for days on end without checking in. Creating a dead-end branch and taking targeted changes would allow devs to continue unhindered in the mainline. Another mechanism is to have a dedicated build machine for a specific purpose. In our current case, for example, just using the demo machine to build the code for itself frees the restrictions on the mainline. Of course you reduce opportunities to refine deployment procedures, but everything has its cons.
Monday, June 21, 2004
Crystal Reports' -- Auto-gen'd code files
- Delete all code files associated with report files (in this case, delete Inventory*.cs)
- In Visual Studio, R-click each report file and select the "Run Custom Tool" context menu item
- Rebuild your solution
Wednesday, June 16, 2004
Setting Crystal Report's Database Info at Run-Time
- Modifying even just a few reports with this mechanism is time consuming
- Changing a report's data source, although infrequent, is not a one-time event
- Each developer has different test databases to run the reports against
- Moving the ASP.NET app through Development, Testing, and Production phases requires using different data stores for the reports
SqlConnection conn = EnterpriseDataStore.GetConnection(<DataStore Name>);
CrystalDecisions.Shared.ConnectionInfo connInfo = new ConnectionInfo();
connInfo.DatabaseName = conn.Database;
connInfo.ServerName = conn.DataSource;
// Leave UserID & Password alone if report was designed for Trusted Security (NT Integrated)
connInfo.UserID = <user name>;
connInfo.Password = <user password>;
foreach(CrystalDecisions.CrystalReports.Engine.Table tbl in rpt.Database.Tables) {
CrystalDecisions.Shared.TableLogOnInfo logOnInfo = tbl.LogOnInfo;
logOnInfo.ConnectionInfo = connInfo;
tbl.ApplyLogOnInfo(logOnInfo);
}
The combination is powerful. Now I can change the connection string info as part of the app config and have the reports point to the right data store dynamically. Each developer and app environment (Test, Production) has the config setting in machine.config or web.config, so the code can move seamlessly among all environments.
Saturday, June 12, 2004
Crystal Reports: Navigation in ASP.NET
To resolve this problem, I only had to make slight modifications: detect the navigation event & skip a bit of code during navigation.
On the page's PreRender event:
- Load the Report Document (.rpt)
- If not navigating (use CRV's Navigate event & set flag>...
- Set the date range info on the Report Doc
- Set CRV.ReportSource to the Report Doc
- And, of course, call base.OnPreRender to give CRV it's opportunity to do prerender processing
The primary issue of the viewer always rendering page 1 was caused by setting the date info again.
Tuesday, June 01, 2004
Stored Procedures for DAAB's SqlHelper.UpdateDataset
- You can't add a row to the DataTable with a null value for the identity column (since you should have set the DataTable's PrimaryKey to the identity column)
- Using a flag value (e.g., -1) for the new row's identity column works fine until you try to add more than one row. (An exception occurs announcing a primary key violation due to the 2nd new row's identity column being -1)
- Trying to manually update the persisted new row's identity column in the DataSet, although a good first guess, is not a good way to go
The big trick for inserts is to structure the stored procedure (sproc) to update the data, and then return the entire row for the updated item. (Just returning the new identity value doesn't suffice). Consider a simple table: Phonelist (EntryId, Name, PhoneNumber). This table contains three columns:
- EntryId -- an auto-generated identity column for uniqueness.
- Name -- a person's name
- PhoneNumber -- the person's phone number
An appropriate sproc for adding new rows to this table would look like this:
create stored procedure Phonelist
(@name varchar(50), @phoneNumber varchar(15))
as
insert into Phonelist (Name, PhoneNumber)
values (@name, @phoneNumber)
select EntryId, Name, PhoneNumber
from Phonelist
where EntryId = @@IDENTITY
return
Notice that each of the columns used to originally populate the DataTable in the DataSet are selected and returned filled with the data just inserted (including the auto-generated identity column). Once you set up the correct SqlCommands for each of the three types of operations (insert, delete, update), then just call SQLHelper.UpdateDataset. The underlying DataSet will auto-magically populate the identity column for the new row based on the row returned from the sproc. Very nice!
One last point: Don't forget to grant Execute permission to the appropriate user account (i.e.,
