Join 6 other subscribers
Top Posts
Archive
- July 2011 (2)
- April 2011 (1)
- March 2011 (5)
- February 2011 (3)
- January 2011 (3)
- November 2010 (1)
- October 2010 (7)
- August 2010 (2)
- July 2010 (1)
- June 2010 (6)
Blog Stats
- 12,392 hits
cultivate passion for everything else that goes on around programming
Tracking changes in Entity Framework 3.5 is pretty straight forward. I used the techniques described here: Audit Trail Using EF Part 1
Everything was fine except that the code in this article did not track changes to EntityReferences. So given an object A that has a reference to object B, the Entity Framework does not mark the reference from A to B as changed. Instead, when the reference changes, it deletes the previous reference and adds the new reference as an ObjectStateEntry. This way, when calling ObjectStateManager.GetObjectStateEntries(), there will be 2 objects returned, one with the EntityState.Deleted state and one with the EntityState.Added state. Additionally, the ObjectStateEntry.GetModifiedProperties() method does not return any information about the change of the reference.
So I adapted the code from the article to retrieve the changed reference values too. The following code shows how I did that, based on the code from the original article.
1: /// <summary>
2: /// Gets the entity properties as XML, either the original values or the current values.
3: /// </summary>
4: /// <param name="entry">The entry.</param>
5: /// <param name="isOrginal">if set to <c>true</c> then the original values will be serialized.</param>
6: /// <returns>XML string of serialized entity</returns>
7: private string GetEntityPropertiesAsXml(ObjectStateEntry entry, bool isOrginal)
8: {
9: if (entry.Entity is EntityObject)
10: {
11: object target = HelperClasses.CloneEntity((EntityObject)entry.Entity);
12: foreach (string propName in entry.GetModifiedProperties())
13: {
14: object setterValue = null;
15: if (isOrginal)
16: {
17: //Get original value
18: setterValue = entry.OriginalValues[propName];
19: }
20: else
21: {
22: //Get original value
23: setterValue = entry.CurrentValues[propName];
24: }
25:
26: //Find property to update
27: PropertyInfo propInfo = target.GetType().GetProperty(propName);
28:
29: //update property with original value
30: if (setterValue == DBNull.Value)
31: {
32: setterValue = null;
33: }
34: propInfo.SetValue(target, setterValue, null);
35: }
36:
37: // references are not returned by GetModifiedProperties(), so we need to search through all
38: // object state entries for those matching our properties and get the deleted ones
39: var deletedRelations = entry.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted).Where(ose => ose.IsRelationship);
40: var addedRelations = entry.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Where(ose => ose.IsRelationship);
41: foreach (PropertyInfo pi in target.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(pi => pi.PropertyType.BaseType == typeof(EntityReference)))
42: {
43: // try to get the original values
44: if (isOrginal)
45: {
46: EntityReference currentReference = (EntityReference)pi.GetValue(target, null);
47:
48: // find the value of the old relation in the deletedRelations
49: var oldRelation = deletedRelations
50: .Where(relation => relation.EntitySet.ElementType.FullName == currentReference.RelationshipName)
51: .FirstOrDefault();
52:
53: if (oldRelation != null)
54: {
55: // set the current value to the old reference
56: currentReference.EntityKey = (oldRelation.OriginalValues[0] as EntityKey);
57: }
58: else
59: {
60: var addedRelation = addedRelations
61: .Where(relation => relation.EntitySet.ElementType.FullName == currentReference.RelationshipName)
62: .FirstOrDefault();
63:
64: // if an added relation was found, but a deleted one was not found
65: if (addedRelation != null)
66: {
67: // object did not have any relation attached previously
68: currentReference.EntityKey = null;
69: }
70: }
71: }
72: }
73:
74: XmlSerializer formatter = new XmlSerializer(target.GetType());
75: XDocument document = new XDocument();
76: using (XmlWriter xmlWriter = document.CreateWriter())
77: {
78: formatter.Serialize(xmlWriter, target);
79: }
80:
81: return document.Root.ToString();
82: }
83:
84: return null;
85:
86: }
The interesting part begins at line 39, where I try to find all relationships, added or deleted, that are related to the given entry. By finding the deleted references we are able to set the original value of the EntityKey. If no deleted reference is found, we try to find an added relation. If an added relation exists, but no deleted one, than we can assume that the reference was not set before the change, so we set it’s EntityKey to null.
Thus we can store our OldData and our NewData into the audit trail database.
While trying to encrypt and decrypt some strings in PHP and in C# using the same algorithm, I wasn’t able to found a good hint on the Internet. After some research I came to the following conclusions:
The following encryption codes are equivalent and will produce the same result. But with one additional note, worth to mention: Although in PHP the key’s length is not important, in C# you must use a key with the length of 32 bytes!
1: $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
2: $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
3: $key = $pwd_key;//"This is a very secret key";
4: return base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv));
1:
2: // set up encrytion object
3: RijndaelManaged aes256 = new RijndaelManaged();
4: aes256.KeySize = 256;
5: aes256.BlockSize = 256;
6: aes256.Padding = PaddingMode.Zeros;
7: aes256.Mode = CipherMode.ECB;
8: aes256.Key = ASCII_ENCODING.GetBytes(passKey);
9: aes256.GenerateIV();
10:
11: // form the string for encrypting
12: // and put into byte array
13: byte[] plainTextBytes = ASCII_ENCODING.GetBytes(message);
14:
15: // encrypt the data
16: ICryptoTransform encryptor = aes256.CreateEncryptor();
17: MemoryStream ms = new MemoryStream();
18:
19: CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);
20: StreamWriter mSWriter = new StreamWriter(cs);
21:
22: mSWriter.Write(message);
23: mSWriter.Flush();
24: cs.FlushFinalBlock();
25:
26: // convert our encrypted data from a memory stream into a byte array.
27: byte[] cypherTextBytes = ms.ToArray();
28:
29: // close memory stream
30: ms.Close();
31:
32: return Convert.ToBase64String(cypherTextBytes);
Additional resources:
Using Forms Authentication with Reporting Services 2008 worked fine based on the sample from code project:
But after I installed a second instance of Reporting Services 2008 on the same machine, the security extension did no longer work as expected. The problem was that the newly installed instance was using SSL, the first one did not. When I tried to login to the Reporting Services 2008 first instance, I received “The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel”. In the log files there was this error: “Remote certificate error RemoteCertificateChainErrors encountered for url”. This is how I found out that the security extension did not use the proper report server URL!
After debugging, I found the following problem and solution:
The security extension uses WMI in the following method to determine the ReportServerUrl:
internal static string GetReportServerUrl(string machineName, string instanceName)
When multiple instances are running, we have 2 ManagementObjects, even if we use the correct WMI path! The following picture shows that there are 2 MSReportsServer_Instance instances. So
I changed the code to this:
foreach (ManagementObject instance in instances) { instance.Get(); // we are interested only for a specific instance var instanceNameProperty = instance.GetPropertyValue("InstanceName"); if (instanceNameProperty != null && String.Compare(instanceNameProperty.ToString(), instanceName, true) != 0) { continue; }
...
}
This way I correctly retrieve the reports’ server url of the specified instance.
For performance reasons, I think it would be much easier to hard code the URLs value into the web.config and not query the value through WMI. The disadvantage would be that if you change the Reporting Services 2008 configuration, you need to manually edit the web.config of the ReportServer too.
Thank god I found this link: http://blogs.infosupport.com/blogs/marks/archive/2010/03/29/report-builder-2-0-security-extension-issues.aspx
I stumbled upon the same problem which is described there: Report Builder used to freeze when using Forms Authentication and using Report Builder 2.0.
As it turned out, the Report Builder does not send the authentication cookie with each request. Because of this, the authentication extension returned a object moved error, but the Report Builder 2.0 did not treat it in any way. Making sure that we return null as mentioned in the blog, the extension throws an exception (visible in the Report Server log files), but the Report Builder 2.0 works as expected.
Additional notes: I created the custom security extension based on the steps described here:
Besides what’s described there, I think I also gave change rights to NETWORK SERVICE for
c:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\
and in AuthenticationUtilities.cs, I changed the following line:
1: string fullWmiNamespace = @"\\" + machineName + string.Format(wmiNamespace, "RS_" + instanceName);
Now everything seems to work properly using Forms Authentication on Reporting Services!
I starting working with Reporting Services 2008 in a project at work. I didn’t really knew much about the whole technology around SSRS, but after some weeks/month of researching I managed to get everything working.
The customers’ requirements were not that trivial. Starting from “simple” ones like multi-language and up to more difficult ones like reports getting data from multiple databases, I learned that there are a lot of options and different ways to implement reports.
The journey began, like every project, with the client’s whishes, listed in a document, known as the specifications document. The main requirements were:
Broken into smaller pieces, each requirement has been investigated and possible solutions were analyzed. After some Internet researching, some e-books and even a printed book (in German language), I have gathered some answers to some of the problems, but unfortunately even more questions than I expected. The difficult part was that I didn’t have enough experience with reporting tools (I played with Crystal Reports some years ago, but never did any web based reports). Diving into the SSRS technology, I begun to understand the concept behind a report server, why someone should use it after all and what the benefits are.
During the research, I gathered a list of useful links:
Coming next: information about (almost) every problem I stumbled upon using SSRS.
In
a struggle to squeeze the most of EF, I found this link:
http://msdn.microsoft.com/en-us/library/bb896240.aspx
The
performance problem for the first ObjectContext initialization is the
generating of the context’s views. These are some internal used views, which
the developer should not know about. But the generation at run-time is
time-consuming and can be done up-front, at compile/build time. So I followed
the steps mentioned in the link above and finally managed to get the following
measured performance improvement:
The
highlighted line is a ObjectContext with about 60 entities. I’m pretty
satisfied with the Speedup improvement J
All in one, I could say that after pre-compiling the view, for the particular
scenario I profiled, the response was about 1 second faster.
After
doing all this I found the following link on CodeProject:
http://www.codeproject.com/KB/database/PerfEntityFramework.aspx
As
mentioned there, the pre- generation of the view is the practice with the most
impact on the performance of the Entity Framework. Another optimization I will
implement next: precompiled queries!