lundi 22 septembre 2014

Managing default values for entities in EF II: the just on time way

You probably still know how to create an entity with default values. This is the 'from birth' way.
Well, there is another way to set default values: a 'just on time way'.
The principle is to override the SaveChanges method to explore entities in the tracker to find the one having unset values, just like in the 'from birth way'. Here it is how it can be done.
        //the default value for datetime properties
        public DateTime DefaultDate { get; set; }

        public override int SaveChanges() {
            Boolean defaultSet = false;

            //seeking only pertinent entities
            foreach (DbEntityEntry dbee in ChangeTracker.Entries().
                     Where(x => x.State != EntityState.Deleted && x.State != EntityState.Detached ) ) {
                defaultSet = false;
                Object o = dbee.Entity;
                Type t = o.GetType();
                foreach ( MemberInfo m in t.GetProperties() ) {
                    PropertyInfo p = t.GetProperty(m.Name);
                    switch (Type.GetTypeCode(p.PropertyType)) {
                        case TypeCode.String:
                            if (p.GetValue(o, null) == null) {
                                p.SetValue(o, String.Empty, null);
                                defaultSet = true;
                            }
                            break;
                        case TypeCode.DateTime:
                            if ((DateTime)p.GetValue(o, null) == DateTime.MinValue) {
                                p.SetValue(o, DefaultDate, null);
                                defaultSet = true;
                            }
                            break;
                    }
                    
                    //update the state if needed
                    if ( defaultSet ) {
                        switch ( dbee.State ) {
                            case EntityState.Unchanged :
                                dbee.State = EntityState.Modified;
                                break;
                        }
                    }
                }
            }

            return base.SaveChanges();
        }
This could be used to implement business rules, such as LastModificationDate.
Before EF6, you may have to look for the tracker in the underlying ObjectContext
A full piece of code:
using System;
using System.Linq;
using System.Data.Entity;
using System.Collections.Generic;
using System.Data.Entity.ModelConfiguration;
using System.Reflection;
using System.Data.Entity.Infrastructure;

namespace testef {
    public class Order {
        public Int32 Id { get; set; }
        public String Title { get; set; }

        public virtual ICollection Details { get; set; }

        public DateTime OrderDate { get; set; }

        public virtual OrderType Type { get; set; }
    }

    public class OrderType {
        public String Code { get; set; }
        public String Description { get; set; }
    }

    public class OrderDetail {
        public virtual Order Order { get; set; }
        public Int32 Id { get; set; }
        public String D { get; set; }
        public Boolean IsActive { get; set; }
    }

    public class OrderDetailConfiguration : EntityTypeConfiguration {
        public OrderDetailConfiguration()
            : base() {
            HasRequired(d => d.Order).WithMany(o => o.Details);
        }
    }

    public class OrderConfiguration : EntityTypeConfiguration {
        public OrderConfiguration()
            : base() {
            HasRequired(d => d.Type).WithMany();
        }
    }

    public class OrderTypeConfiguration : EntityTypeConfiguration {
        public OrderTypeConfiguration()
            : base() {
            Property(x => x.Code).HasColumnType("varchar").HasMaxLength(10);
            HasKey(x => x.Code);
        }
    }

    public class TestEFContext : DbContext {
        public DbSet Orders { get; set; }
        public DbSet Details { get; set; }

        public TestEFContext(String cs)
            : base(cs) {
            Database.SetInitializer(new DropCreateDatabaseAlways());
            //Database.SetInitializer(null);
            //Database.SetInitializer(new CreateDatabaseIfNotExists());
            //Database.SetInitializer(new CustomDataBaseInitializer());

        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        }

        public DateTime DefaultDate { get; set; }

        public override int SaveChanges() {

            Boolean defaultSet = false;
            foreach (DbEntityEntry dbee in ChangeTracker.Entries().
                     Where(x => x.State != EntityState.Deleted && x.State != EntityState.Detached ) ) {
                defaultSet = false;
                Object o = dbee.Entity;
                Type t = o.GetType();
                foreach ( MemberInfo m in t.GetProperties() ) {
                    PropertyInfo p = t.GetProperty(m.Name);
                    switch (Type.GetTypeCode(p.PropertyType)) {
                        case TypeCode.String:
                            if (p.GetValue(o, null) == null) {
                                p.SetValue(o, String.Empty, null);
                                defaultSet = true;
                            }
                            break;
                        case TypeCode.DateTime:
                            if ((DateTime)p.GetValue(o, null) == DateTime.MinValue) {
                                p.SetValue(o, DefaultDate, null);
                                defaultSet = true;
                            }
                            break;
                    }
                    if ( defaultSet ) {
                        switch ( dbee.State ) {
                            case EntityState.Unchanged :
                                dbee.State = EntityState.Modified;
                                break;
                        }
                    }
                }
            }

            return base.SaveChanges();
        }
    }

    public class CustomDataBaseInitializer : CreateDatabaseIfNotExists {
        public CustomDataBaseInitializer()
            : base() {
        }
    }
        
    class Program {
        static void Main(string[] args) {
            String cs = @"Data Source=ALIASTVALK;Initial Catalog=TestEF;Integrated Security=True; MultipleActiveResultSets=True";
            using (TestEFContext ctx = new TestEFContext(cs)) {
                ctx.DefaultDate = new DateTime(2000, 1, 1);

                OrderType t = new OrderType { 
                    Code = "T1",
                    Description = "type One"
                };
                ctx.Orders.Add(new Order { 
                    Title = "first order",
                    Type = t
                });

                ctx.SaveChanges();
            }

            using (TestEFContext ctx = new TestEFContext(cs)) {
                Order o = ctx.Orders.First();
                Console.WriteLine(o.OrderDate);
            }
        }
    }
}

Aucun commentaire:

Enregistrer un commentaire