none
Efficient C# packaging process RRS feed

  • Question

  • Hi,

    I have to build a manufacture packaging process in C# which will handle pallets, boxes and items through a DbContext which is related to a variable connection string.

    I have created a well-working process but if my classes are to change in the future to customize clients needs, I have no way of reusing my current process code but to copy it into the client's application project and make the relevant changes in order to satisfy his needs.  

    I am pretty sure this isn't a good strategy because it annoys me pretty much and I am not an expert in C# but I guess that INTERFACES and ABSTRACT and T4TEMPLATES could be of a help for me but don't really know how to implement start up strategy.

    I don't mind on the way it is achieve but I'd like to get good programming practices in order to be more accurate in the future projects.

    So here we go, I'm showing you my current working process and then I'm going to point the possible changes that may occur in the future.  Keep in mind that this process might change for clients but not for others and that I'd like to be reusable and customizable without the need to recopy the whole classes to satisfy those changes :

    ---------------------------------------------------------------------------------------------------------------------------------------

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Data.Entity;
    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;

    namespace Whatsoever.Packaging
    {     
        public class Pallet 
        {
            public string pallet_no { get; set; }
            public DateTime pallet_date { get; set; }
            [Key]
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public decimal id { get; set; } 
        }

        public class Box : IContainer, IContainable 
        {
            public string box_no { get; set; }
            public DateTime box_date { get; set; }
            [Key]
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]       
            public decimal id { get; set; }

            public decimal? main_box_id { get; set; }
            public virtual Box main_box { get; set; }

            public decimal? pallet_id { get; set; }
            public virtual Pallet pallet { get; set; }
        }

        public class Item : IContainable
        {
            public string item_no { get; set; }
            public DateTime item_date { get; set; }
            [Key]
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public decimal id { get; set; }
            public decimal? box_id { get; set; }
            public virtual Box box { get; set; }

            public decimal? pallet_id { get; set; }
            public virtual Pallet pallet { get; set; }
        }


        public class PackagingContext : DbContext, IDisposable
        {
            public PackagingContext(string connstring, string ProductName)
                : base(connstring)
            {
                _ProductName = ProductName;
            }

            public PackagingContext()
            {
                _ProductName = ProductName;
            }

            private string _ProductName;
            public string ProductName
            {
                get { return _ProductName.ToUpper(); }
                set { value = _ProductName; }
            }


            ///<summary­>
            ///Méthode permettant de contrôler la création des tables dans la base de données
            ///</summary­>
            ///<remarks>
            ///Cette méthode provient de la classe de base DbContext et peut être personnalisée        
            ///</remarks>
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Pallet>().Property(P => P.id).HasPrecision(18, 0);
                //Convention de nom de table SQL server (CF = Code First) : dbo.NOMDEPRODUIT_PALLETS_CF
                //Notez que le nom de table est tout en majuscule, convention utilisée pour uniformiser la procédure   
                modelBuilder.Entity<Pallet>().ToTable(ProductName + "_PALLETS" + "_CF");
                modelBuilder.Entity<Box>().Property(P => P.id).HasPrecision(18, 0);
                modelBuilder.Entity<Box>().ToTable(ProductName + "_BOXES" + "_CF");
                modelBuilder.Entity<Box>().HasOptional(x => x.pallet).WithMany().HasForeignKey(r => r.pallet_id);
                modelBuilder.Entity<Box>().HasOptional(x => x.main_box).WithMany().HasForeignKey(c => c.main_box_id);
                modelBuilder.Entity<Item>().Property(P => P.id).HasPrecision(18, 0);
                modelBuilder.Entity<Item>().ToTable(ProductName + "_ITEMS" + "_CF");
                modelBuilder.Entity<Item>().HasOptional(x => x.box).WithMany().HasForeignKey(r => r.box_id);
                modelBuilder.Entity<Item>().HasOptional(x => x.pallet).WithMany().HasForeignKey(r => r.pallet_id);
            }

            public DbSet<Pallet> Pallets { get; set; }
            public DbSet<Box> Boxes { get; set; }
            public DbSet<Item> Items { get; set; }
        }
    }

    ---------------------------------------------------------------------------------------------------------------------------------------

    Here is what could happen : add a new property to the Pallet class  public string pallet_name { get; set; } for example

    What I don't want is to change my defaults strategy which is what I've pasted above but the problem come when I try to implement a generic but not so generic PackagingContext class when overriding OnModelCreating and DbSet<Pallet> is already pointing to a well defined class and this is forcing me to copy the whole thing into another project and change the Pallet class with my new property instead of creating something more clever (which I can't figure out at the moment) that would just use this code as a base or default behavior and the if the need to add property or more functionality, I could do it by appending what I need.

    Does any one knows how I could achieve this and show me a bit of code in order to give me the knowledge I'm missing to achieve this ? 

    I know that this issue is a lack of knowledge of my part but I'd really like to get into good programming practices.

    Thanks again, 

    Eric


    Rick


    • Moved by CoolDadTx Monday, January 13, 2014 2:58 PM EF related
    • Edited by CuriousRick Monday, February 20, 2017 1:16 PM
    Monday, January 13, 2014 2:39 PM

Answers

  • >So there is no possible approach in the manner I wrote the process in your point of view, correct?  

    >Is that what you are saying?

    No.  There are approaches.  But most application development tools and methodologies are build for the case where code and schema change only at design-time.

    First, customizing code at design-time is not a bad option sometimes.  You need to think carefully about how you manage the codebase and work to minimize the amount of custom code.  But it can work fine.  For instance you can have a base DbContext implemented in a partial class, then for each customer have a separate project that uses the shared DbContext partial file, but augments it with customer-specific types.  The same goes for your entity definitions. 

    The soundest approach to extending and customizing a schema at implementation-time is to dynamically generate all the same assets that would exist at design-time.  So in this case to start with a base schema, and build application admin screens to define new attributes, and then to generate all the application assets required to make them useful, including database changes, code and UI changes.  This is the approach taken by, for instance, Microsoft Dynamics CRM, and SQL Server Master Data Services.  This has a relatively high cost of implementation, as you need to build the application to manage the schema customizations, but it has the best performance and it's simple at runtime.

    Another approach is to use a fixed schema that stores untyped data, like XML, name/value pairs, etc.  You get a fixed schema, but you must now implement your own type system to track what data is stored.  And the runtime performance of this approach is generally inferior.  Microsoft SharePoint is an example of this strategy.  It has a whole type system for "lists", complete with referential integrity, but all lists items are stored in a single table.  The storage system has no knowledge of the list types, which exist only in the application tier.  This approach tends to have inferior runtime performance.

    Both approaches require you to generate UI dynamically, since the shape of your data is not known at design-time.  And present complexity around implementing business logic. 

    David


    David http://blogs.msdn.com/b/dbrowne/


    Monday, January 13, 2014 4:59 PM

All replies

  • Sorry I forgot to say that Pallet, Box and Item classes may be extendable but not modified from it's base implementation as shown in the pasted code.  Maybe Interface or things like that may help but DbSet doesn't like interface I guess....  anyway please HELPPPP  :)

    Rick

    Monday, January 13, 2014 2:44 PM
  • Typically applications don't change the entity and database design for each customer.  Instead, the schema evolves over time adding attributes in newer versions and eventually upgrading existing customers.

    Any real requirements that require more run-time, or implementation-time flexibility could use weakly-typed data, like an XML attribute, or a comma-delimited string.  But these come with added complexity.

    David


    David http://blogs.msdn.com/b/dbrowne/

    Monday, January 13, 2014 3:15 PM
  • So there is no possible approach in the manner I wrote the process in your point of view, correct?  Is that what you are saying?

    Rick

    Monday, January 13, 2014 3:59 PM
  • >So there is no possible approach in the manner I wrote the process in your point of view, correct?  

    >Is that what you are saying?

    No.  There are approaches.  But most application development tools and methodologies are build for the case where code and schema change only at design-time.

    First, customizing code at design-time is not a bad option sometimes.  You need to think carefully about how you manage the codebase and work to minimize the amount of custom code.  But it can work fine.  For instance you can have a base DbContext implemented in a partial class, then for each customer have a separate project that uses the shared DbContext partial file, but augments it with customer-specific types.  The same goes for your entity definitions. 

    The soundest approach to extending and customizing a schema at implementation-time is to dynamically generate all the same assets that would exist at design-time.  So in this case to start with a base schema, and build application admin screens to define new attributes, and then to generate all the application assets required to make them useful, including database changes, code and UI changes.  This is the approach taken by, for instance, Microsoft Dynamics CRM, and SQL Server Master Data Services.  This has a relatively high cost of implementation, as you need to build the application to manage the schema customizations, but it has the best performance and it's simple at runtime.

    Another approach is to use a fixed schema that stores untyped data, like XML, name/value pairs, etc.  You get a fixed schema, but you must now implement your own type system to track what data is stored.  And the runtime performance of this approach is generally inferior.  Microsoft SharePoint is an example of this strategy.  It has a whole type system for "lists", complete with referential integrity, but all lists items are stored in a single table.  The storage system has no knowledge of the list types, which exist only in the application tier.  This approach tends to have inferior runtime performance.

    Both approaches require you to generate UI dynamically, since the shape of your data is not known at design-time.  And present complexity around implementing business logic. 

    David


    David http://blogs.msdn.com/b/dbrowne/


    Monday, January 13, 2014 4:59 PM