Bring a bit of the Subsonic power to Entity Framework by adding automatic audit and logical delete fields

Posted November 4th, 2011 in .NET, ADO.NET, Productivity, SubSonic, Uncategorized by admin

Here is the code to do the same thing than in my previous article “Bring a bit of the Subsonic power to Linq to sql by adding automatic audit and logical delete fields”


namespace YouNamespace.DAL
{
 using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Data;
 using System.Data.Common;
 using System.Data.Objects;
 using System.Linq;

 public partial class YOURCONTEXTEntities
 {
 /// <summary>
 /// System fields for automatic audit and logical delete
 /// </summary>
 private struct SystemFields
 {
 public const string CreatedOn = "CREATEDON";
 public const string ModifiedOn = "MODIFIEDON";
 public const string CreatedBy = "CREATEDBY";
 public const string ModifiedBy = "MODIFIEDBY";
 public const string IsDeleted = "ISDELETED";
 }

 /// <summary>
 /// Overriding the SaveChanges method to automaticaly set system fields if any.
 /// </summary>
 /// <param name="options"></param>
 /// <returns></returns>
 public override int SaveChanges(System.Data.Objects.SaveOptions options)
 {
 IEnumerable<ObjectStateEntry> newEntries = this.ObjectStateManager.GetObjectStateEntries(EntityState.Added);

 foreach (ObjectStateEntry entry in newEntries)
 {
 ReadOnlyCollection<FieldMetadata> fieldsMetaData = entry.CurrentValues
 .DataRecordInfo.FieldMetadata;

 FieldMetadata createdOnField = fieldsMetaData
 .Where(f => string.Equals(f.FieldType.Name, SystemFields.CreatedOn, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

 if (createdOnField.FieldType != null)
 {
 entry.CurrentValues.SetValue(createdOnField.Ordinal, DateTime.Now);
 }

 FieldMetadata createdByField = fieldsMetaData
 .Where(f => string.Equals(f.FieldType.Name, SystemFields.CreatedBy, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

 if (createdByField.FieldType != null)
 {
 entry.CurrentValues.SetValue(createdByField.Ordinal, "Sam");
 }

 FieldMetadata deletedField = fieldsMetaData
 .Where(f => string.Equals(f.FieldType.Name, SystemFields.IsDeleted, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

 if (deletedField.FieldType != null)
 {
 entry.CurrentValues.SetValue(deletedField.Ordinal, false);
 }
 }

 IEnumerable<ObjectStateEntry> modifiedEntries = this.ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
 foreach (ObjectStateEntry entry in modifiedEntries)
 {
 ReadOnlyCollection<FieldMetadata> fieldsMetaData = entry.CurrentValues
 .DataRecordInfo.FieldMetadata;

 FieldMetadata createdOnField = fieldsMetaData
 .Where(f => string.Equals(f.FieldType.Name, SystemFields.ModifiedOn, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

 if (createdOnField.FieldType != null)
 {
 entry.CurrentValues.SetValue(createdOnField.Ordinal, DateTime.Now);
 }

 FieldMetadata createdByField = fieldsMetaData
 .Where(f => string.Equals(f.FieldType.Name, SystemFields.ModifiedBy, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

 if (createdByField.FieldType != null)
 {
 entry.CurrentValues.SetValue(createdByField.Ordinal, "Sam");
 }
 }

 IEnumerable<ObjectStateEntry> deletedEntries = this.ObjectStateManager.GetObjectStateEntries(EntityState.Deleted);
 foreach (ObjectStateEntry entry in deletedEntries)
 {
 // change from deleted to modified (!important)
 this.ObjectStateManager.ChangeObjectState(entry.Entity, EntityState.Modified);

 ReadOnlyCollection<FieldMetadata> fieldsMetaData = entry.CurrentValues
 .DataRecordInfo.FieldMetadata;

 FieldMetadata deletedField = fieldsMetaData
 .Where(f => string.Equals(f.FieldType.Name, SystemFields.IsDeleted, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

 if (deletedField.FieldType != null)
 {
 entry.CurrentValues.SetValue(deletedField.Ordinal, true);
 }
else
 {
 // change back from modified to deleted (!important)
 this.ObjectStateManager.ChangeObjectState(entry.Entity, EntityState.Deleted);
 }
 }

 return base.SaveChanges(options);
 }
 }
}

Enum HasFlag method extension for < 4.0 Framework

Posted August 3rd, 2011 in .NET by admin

In the same idea than the previous article, here is an extension method to mimic the 4.0 HasFlag method (http://msdn.microsoft.com/en-us/library/system.enum.hasflag.aspx).

     /// <summary>
    /// Extentions for enums.
    /// </summary>
    public static class EnumExtensions
    {
        /// <summary>
        /// A FX 3.5 way to mimic the FX4 "HasFlag" method.
        /// </summary>
        /// <param name="variable">The tested enum.</param>
        /// <param name="value">The value to test.</param>
        /// <returns>True if the flag is set. Otherwise false.</returns>
        public static bool HasFlag(this Enum variable, Enum value)
        {
            // check if from the same type.
            if (variable.GetType() != value.GetType())
            {
                throw new ArgumentException("The checked flag is not from the same type as the checked variable.");
            }

            Convert.ToUInt64(value);
            ulong num = Convert.ToUInt64(value);
            ulong num2 = Convert.ToUInt64(variable);

            return (num2 & num) == num;
        }
    }

Stream.CopyTo method for < 4.0 framework

Posted August 2nd, 2011 in .NET by Sam Beauvois

The .NET Framework 4.0 introduced the CopyTo() method for the Stream class. (http://msdn.microsoft.com/en-us/library/system.io.stream.copyto.aspx)

You copy a stream into one other this way:


   MemoryStream memoryStream = new MemoryStream();
   using (Stream stream = new FileStream(@"c:\input.txt", FileMode.Open))
   {
      stream.CopyTo(memoryStream);
   }

If you are stucked with a lower version of the framework, you might want to use it anyway.

A bit of reflector is realy usefull to see how the implementation is done in 4.0.

The CopyTo method :


public void CopyTo(Stream destination)
{
    if (destination == null)
    {
        throw new ArgumentNullException("destination");
    }
    if (!this.CanRead && !this.CanWrite)
    {
        throw new ObjectDisposedException(null, Environment.GetResourceString("ObjectDisposed_StreamClosed"));
    }
    if (!destination.CanRead && !destination.CanWrite)
    {
        throw new ObjectDisposedException("destination", Environment.GetResourceString("ObjectDisposed_StreamClosed"));
    }
    if (!this.CanRead)
    {
        throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnreadableStream"));
    }
    if (!destination.CanWrite)
    {
        throw new NotSupportedException(Environment.GetResourceString("NotSupported_UnwritableStream"));
    }
    this.InternalCopyTo(destination, 0x1000);
}

We see that the InternalCopyTo is used :


private void InternalCopyTo(Stream destination, int bufferSize)
{
    int num;
    byte[] buffer = new byte[bufferSize];
    while ((num = this.Read(buffer, 0, buffer.Length)) != 0)
    {
        destination.Write(buffer, 0, num);
    }
}

Remark :
0×1000 is in hexadecimal, which corresponds to 4096 in decimal.

So we can retrieve that for use with lower versions of the language

For .NET FX >= 3.0 you can create an extension method


    using System;
    using System.IO;

    /// <summary>
    /// Extension methods for streams.
    /// </summary>
    public static class StreamExtensions
    {
        /// <summary>
        /// Reads all the bytes from the current stream and writes them to the destination stream.
        /// </summary>
        /// <param name="original">The current stream.</param>
        /// <param name="destination">The stream that will contain the contents of the current stream.</param>
        /// <exception cref="System.ArgumentNullException">Destination is null.</exception>
        /// <exception cref="System.NotSupportedException">The current stream does not support reading.-or-destination does not support Writing.</exception>
        /// <exception cref="System.ObjectDisposedException">Either the current stream or destination were closed before the System.IO.Stream.CopyTo(System.IO.Stream) method was called.</exception>
        /// <exception cref="System.IO.IOException">An I/O error occurred.</exception>
        public static void CopyTo(this Stream original, Stream destination)
        {
            if (destination == null)
            {
                throw new ArgumentNullException("destination");
            }
            if (!original.CanRead && !original.CanWrite)
            {
                throw new ObjectDisposedException("ObjectDisposedException");
            }
            if (!destination.CanRead && !destination.CanWrite)
            {
                throw new ObjectDisposedException("ObjectDisposedException");
            }
            if (!original.CanRead)
            {
                throw new NotSupportedException("NotSupportedException source");
            }
            if (!destination.CanWrite)
            {
                throw new NotSupportedException("NotSupportedException destination");
            }

            byte[] array = new byte[4096];
            int count;
            while ((count = original.Read(array, 0, array.Length)) != 0)
            {
                destination.Write(array, 0, count);
            }
        }
    }

You use it this way (same ways as the FX 4.0):


   MemoryStream memoryStream = new MemoryStream();
   using (Stream stream = new FileStream(@"c:\input.txt", FileMode.Open))
   {
      stream.CopyTo(memoryStream);
   }

For lower version, you can create an helper class.


    using System;
    using System.IO;

    /// <summary>
    /// An helper class for streams.
    /// </summary>
    public class StreamHelper
    {
        /// <summary>
        /// Reads all the bytes from the current stream and writes them to the destination stream.
        /// </summary>
        /// <param name="original">The original stream.</param>
        /// <param name="destination">The stream that will contain the contents of the current stream.</param>
        /// <exception cref="System.ArgumentNullException">Destination is null.</exception>
        /// <exception cref="System.NotSupportedException">The current stream does not support reading.-or-destination does not support Writing.</exception>
        /// <exception cref="System.ObjectDisposedException">Either the current stream or destination were closed before the System.IO.Stream.CopyTo(System.IO.Stream) method was called.</exception>
        /// <exception cref="System.IO.IOException">An I/O error occurred.</exception>
        public static void CopyStreamTo(Stream original, Stream destination)
        {
            if (destination == null)
            {
                throw new ArgumentNullException("destination");
            }
            if (!original.CanRead && !original.CanWrite)
            {
                throw new ObjectDisposedException("ObjectDisposedException");
            }
            if (!destination.CanRead && !destination.CanWrite)
            {
                throw new ObjectDisposedException("ObjectDisposedException");
            }
            if (!original.CanRead)
            {
                throw new NotSupportedException("NotSupportedException source");
            }
            if (!destination.CanWrite)
            {
                throw new NotSupportedException("NotSupportedException destination");
            }

            byte[] array = new byte[4096];
            int count;
            while ((count = original.Read(array, 0, array.Length)) != 0)
            {
                destination.Write(array, 0, count);
            }
        }
    }

Use it this way:


   MemoryStream memoryStream = new MemoryStream();
   using (Stream stream = new FileStream(@"c:\input.txt", FileMode.Open))
   {
        StreamHelper.CopyStreamTo(stream, memoryStream);
   }

Chanel 9 | Rx Workshop: Unified Programming Model

Posted August 2nd, 2011 in .NET, Quick posts, Reminders by admin

Learn how to wrap existing event sources, including tasks, asynchronous methods, .NET events, etc. in observable sequences.

ASP.NET videos : Choosing the Right Programming Model

Posted June 12th, 2011 in .NET, ASP.NET by Sam Beauvois

As mentioned by Kris van der Mast on the asp.net forums, this video was on the get started page before, but don’t appears to be there anymore.

This video is great to know in 5 minutes which model is good for you.

So, because I don’t want to search for it, I post this video here :

Install Silverlight

(direct link)