Haciendo que la clase de entidad esté cerrada por cambios

Tengo una relación de base de datos como se muestra a continuación. Los objetos de dominio se crean en base a LINQ a SQL ORM.

Un pago se compone de pagos en efectivo y cupones de regalo. Supongamos que la cantidad total de compra es 550. Puede pagarse como los siguientes componentes

1 Gift Coupon Valued 300 1 Gift Coupon Valued 200 I Cash Currency Valued 50 

enter image description here

Estoy insertando nuevos registros de pago usando la función “InsertOnSubmit” de ORM. El siguiente código funciona bien. Sin embargo, si la empresa presenta un nuevo componente de pago con tarjeta de crédito, debo realizar cambios en mi clase de dominio “Pago”. ¿Cómo realizo la clase de pago Abrir para la extensión y Cerrado para los cambios todavía usando ORM ?

Nota: La clase de pago tiene comportamientos (por ejemplo, GetTotalAmountCollected). Estoy intentando hacer la clase “Pago” para satisfacer OCP.

Nota: Hay un comportamiento específico para el tipo de Cupón. Si la fecha de emisión del cupón es inferior a 1/1/2000, no se debe utilizar en el cálculo de la cantidad total (es decir, CouponValue debe ser cero). Refiera el código de Refactoring usando el Patrón de Estrategia también.

Nota: Estoy usando .Net 4.0

Referencia:

  1. Obteniendo un error al usar ObjectContext.AddObject con Entity Framework
  2. Código de refactorización utilizando el patrón de estrategia
  3. ¿Prefiere la composición sobre la herencia?
  4. Código primero frente a modelo / base de datos primero
  5. Patrón de estrategia e dependency injection usando la unidad
  6. Patrón de diseño de estrategia C # por delegado vs OOP
  7. ¿Cómo usar el patrón de estrategia con C #?
  8. Herencia con el código EF primero: Parte 2: tabla por tipo (TPT) http://weblogs.asp.net/manavi/archive/2010/12/28/inheritance-mapping-strategies-with-entity-framework-code-first -ctp5-part-2-table-per-type-tpt.aspx

C # Code:

 public class PaymentAppService { public RepositoryLayer.ILijosPaymentRepository Repository { get; set; } public void MakePayment() { DBML_Project.Payment paymentEntity = new DBML_Project.Payment(); paymentEntity.PaymentID = 1; paymentEntity.PaymentType = "PurchaseP"; DBML_Project.CashPayment cashObj = new DBML_Project.CashPayment(); cashObj.CashPaymentID = 1; cashObj.CurrencyNumber = 123; cashObj.CurrencyValue = 100; DBML_Project.GiftCouponPayment giftCouponObj = new DBML_Project.GiftCouponPayment(); giftCouponObj.GiftCouponPaymentID = 1; giftCouponObj.CouponValue = 200; giftCouponObj.CouponNumber = 124; paymentEntity.CashPayments = new System.Data.Linq.EntitySet(); paymentEntity.CashPayments.Add(cashObj); paymentEntity.GiftCouponPayments = new System.Data.Linq.EntitySet(); paymentEntity.GiftCouponPayments.Add(giftCouponObj); Repository.InsertEntity(paymentEntity); Repository.SubmitChanges(); } } 

Repositorio:

 public class LijosPaymentRepository : ILijosPaymentRepository { public System.Data.Linq.DataContext MyDataContext { get; set; } public void InsertEntity(DBML_Project.Payment payment) { //Insert the entity MyDataContext.GetTable().InsertOnSubmit(payment); } public void SubmitChanges() { MyDataContext.SubmitChanges(); } } 

Para el problema que @Lijo intenta resolver el enfoque abstracto sería mejor

Creo que puedes hacer una clase parcial en el tipo de CashPayment que implementa tu propia interfaz de IPayment, que se puede usar a través de toda la aplicación. Esta interfaz también puede estar en CreditCardPayment:

Ejemplo:

 public interface IPayment { int Id { get; set; } int PaymentId { get; set; } //Other payment specific properties or methods } public partial class CashPayment : IPayment { public int Id { get { return CashPaymentId ; } set { CashPaymentId = value; } } //Other properties } public partial class CreditCardPayment : IPayment { //more code ... } 

Algo en su contexto EF para obtener todos los pagos

 public partial class PaymentEntities //The name of your EF entities { public IQueryable AllPayments { return this.CashPayment.Union(this.CreditCardPayment); //This is not good, but just an example. The abstract class approach would be better here. } public void InsertPayment(IPayment payment) { this.AddObject(payment.GetType().Name, payment); } } 

Tal vez una alternativa sería aprovechar la herencia, también en el ORM. De modo que en lugar de tener N colecciones, una para cada tipo en la entidad de pago. Tendría todos los subtipos en la misma colección. Y todos esos, sumdos representarían el pago completo.

Tal vez sería más fácil nombrar las cosas un poco diferente. Por ejemplo, consideremos el concepto de una Compra . La compra debe tener una colección de pagos . Y el pago podría ser una clase abstracta, de la cual Cash , Coupon , CreditCard , todos heredan.

Tener el modelo configurado de esa manera abre una gran cantidad de posibilidades con respecto a cómo puede resolver ciertos problemas. Puede tratar todos los pagos de la misma manera y olvidarse de colecciones distintas, además de tener un gran control a través del polymorphism y el doble despacho .

De esta forma, si surge un nuevo tipo de pago, su modelo seguirá siendo el mismo, solo tendrá un nuevo subtipo.

La mayoría de los ORM en la actualidad admiten diferentes esquemas de persistencia para la herencia, que ayudarán a mantener su estructura de datos limpia también.

¿Cuál es el valor agregado de crear un nuevo tipo para cada tipo de pago? Puedo ver la distinción entre un ‘Pago por dinero’ y un ‘Pago realizado a través de un cupón’. Puedo aceptar tener dos tipos diferentes para hacer esa distinción.

Sin embargo, ¿por qué hacer la distinción entre un CashPayment , un CreditCardPayment , etc.? ¿Tiene que almacenar información adicional según el tipo de pago? ¿El comportamiento cambia?

¿Por qué no lo mantiene simple y agrega una propiedad adicional al tipo regular de ” Payment “, que actúa como un discriminador y le brinda la información sobre cómo se realizó el pago (a través de tarjeta de crédito, efectivo, …)?

 public class Payment {} public class VoucherPayment : Payment {} public class MoneyPayment : Payment { public PaymentMode { get; set; } } public enum PaymentMode { Cash, CreditCard } 

Puede definir su modelo usando tabla por jerarquía (http://msdn.microsoft.com/en-us/library/bb738443) que utiliza una columna discriminator (es decir, PaymentType en su caso) para especificar el sabor particular de la pago que está haciendo. Entonces estaría obligado a agregar columnas si los nuevos tipos de pago requieren más datos; sin embargo, EF es bastante flexible en términos de cómo define las entidades y cómo esas entidades se asignan a su almacén de datos. Probablemente pueda tener un conjunto común de columnas como Número y Valor y luego vaya a ser totalmente amigable y agregue columnas con nombres generics como StringValue1 a la base de datos. Luego puede asignar esas columnas a propiedades mejor nombradas en su modelo conceptual que tengan más sentido para su dominio.

Esto básicamente aplana su esquema de base de datos en una sola tabla, pero su modelo sigue siendo clases separadas. Puede subclasificar su clase de pago para tipos de pago adicionales sin afectar el almacén de datos o la clase de pago; sin embargo, deberá modificar el modelo para agregar nuevos tipos de pago y asignarlos a las columnas apropiadas.