Get all associate/composite objects inside an obje

2020-01-26 11:08发布

问题:

Business:

I have a payment system in which payment can be made though GiftCoupon, ClubMembershipCard etc. One payment itself can have multiple payment components

Class:

I have a Payment class. It has payment components like GiftCouponPayment, ClubMembershipCardPayment, CashPayment and so on. Each component type satisfy a common interface IPaymentComponent. I have implemented it using the knowledge about the existing types.

Questions

1) How to implement this function in a abstract way – without knowing what all are the types that exist? That means it need to work for all types that implement IPaymentComponent interface.

2) If it is not possible to achieve it in LINQ to SQL, is it possible in Entity Framework?

3) Is it association / aggregation or composition when LINQ to SQL generate GiftCouponPayment entities inside Payment object?

Note: I am using LINQ to SQL as ORM. GiftCouponPayment and Payment are autogenerated classes and these objects are created by ORM. I have added more functionality to these classes by using partial classes.

Note: In database each PaymentComponent (E.g. GiftCouponPayment) has its own properties (e.g CouponValue,CardValue etc). Hence Table-Per-Hierarchy will not be good. We need separate tables. Is there a solution in that line?

Note: GiftCouponPayment already exist in the database prior to this payment. We need to identify the GiftCouponPayment object by using GiftCouponPaymentID provided by the customer. We just need to update the PaymentID column in this table.

A leaky abstraction refers to any implemented abstraction, intended to reduce (or hide) complexity, where the underlying details are not completely hidden

LINQ to SQL Diagram

REFERENCE:

  1. Entity Framework 4, inheriting vs extending?
  2. How to choose an Inheritance Strategy http://blogs.msdn.com/b/alexj/archive/2009/04/15/tip-12-choosing-an-inheritance-strategy.aspx
  3. Fluent API Samples - http://blogs.msdn.com/b/adonet/archive/2010/12/14/ef-feature-ctp5-fluent-api-samples.aspx

C# CODE

public interface IPaymentComponent
{
     int MyID { get; set; }
     int MyValue { get; set; }
     int GetEffectiveValue();
}


public partial class GiftCouponPayment : IPaymentComponent
{
    public int MyID
    {
        get 
        { 
            return this.GiftCouponPaymentID; 
        }
        set 
        { 
            this.GiftCouponPaymentID = value; 
        }
    }

    public int MyValue
    {
        get 
        { 
            return this.CouponValue; 
        }
        set 
        { 
            this.CouponValue = value; 
        }
    }

    public int GetEffectiveValue()
    {
        if (this.CouponNumber < 2000)
        {
            return 0;
        }
        return this.CouponValue;
    }
}

public partial class Payment
{
    public List<IPaymentComponent> AllPaymentComponents()
    {
        List<IPaymentComponent> allPayComps = new List<IPaymentComponent>();


        List<GiftCouponPayment> giftCouponPaymentList = new List<GiftCouponPayment>();
        List<CashPayment> cashPaymentList = new List<CashPayment>();

        foreach (GiftCouponPayment g in this.GiftCouponPayments)
        {
            giftCouponPaymentList.Add(g);
            allPayComps.Add(g);
        }

        foreach (CashPayment c in this.CashPayments)
        {
            cashPaymentList.Add(c);
            allPayComps.Add(c);
        }

        return allPayComps;


    }
}

回答1:

I think you might want to step back from the design for a moment. What I've heard is this:

A payment consists of one or more components, and each component can be one of a variety of types

What it sounds like you need is a Payment table, then a PaymentComponent table with a foreign key relation back to the Payment table. You can then implement inheritance on the PaymentComponent table for your various forms of payment.



回答2:

You can try to use an abstraction layer or a data acces layer that will be generic of type T. Or at least make the methods generic.



回答3:

You basically have a few of issues here:

  1. How to model the payment types

    Lets assume we want to go at this the classic OOP way:

    You need a base class, Payment (or PaymentBase) which is abstract and various class which inherit from it e.g. PaymentInCash, PaymentWithCreditCard and etc.

    An alternative could be adding PaymentDetails to Payment and creating a hierarchy of PaymentDetails, if you choose to do this, the replace Payment with PaymentDetails in all the following points.

    For payments with multiple methods, you could either:

    a. Have a collection of PaymentDetails under a Payment
    or
    b. Create a type called AggregatePayment which has a list of Payments.

  2. How to map the payment types to tables

    Both TPT and TPH are valid here...

    For TPT use one table for Payment and one table for each kind of payment.
    All the inheriting type's tables PK's should be an FK to the base type's table.
    If you have multiple levels of hierarchy, you can use either TPT or TPH on the second (or any other) level if you are using EF.

    For TPH use one table with a discriminator column (e.g. PaymentType), and mark each column that is not shared between all entities in the hierarchy as nullable. Do not use the same column for different properties in different entities. In EF, map each entity to the same table with the condition PaymentType = (number goes here) and (column name(s) that should not be null) is not null.

    My recommendation is, if you have many narrow types (few properties each) go with TPH, if you have a few wide types go with TPT.

  3. Which design pattern / code technique to use for payment algorithm

    You have more options here:

    a. Use partial classes and put an abstract ProcessPayment() method on the base class and override in the inheriting classes.

    b. Use a base PaymentProcessor class and a specific PaymentProcessor per payment type e.g. PaymentInCashProcessor. In this method you can use reflection to load the correct PaymentProcessor type by either storing a dictionay or better yet, using generics:

abstract class PaymentProcessor
{
}

abstract class PaymentProcessor<TPayment> : PaymentProcessor
    where TPayment : class, Payment
{
}

class PaymentInCashProcessor : PaymentProcessor<PaymentInCash>
{
}

// Use reflection to find types that inherits from PaymentProcessor<PaymentInCash>
// create an instance of the type you found
// then cast the instance to PaymentProcessor<PaymentInCash> to use



回答4:

If you design your EF model you can use the abstract property on a base class called payment. And let inherit all your payment types that class:

Payment will have all common properties, and every specific type can have their own properties.

If you have this kind of model you can just query for payments.

This returns all objects that inherit of the payment type:


var allPayments = objectContext.Payments;