Use One Table To Store Data In Multiple Tables

Using Entity Framework Core with Sqlite, I have a table that stores address data called ‘Address’. I wish to use this table in several different tables to store addresses. Currently I have it working for one other table by adding 2 fields directly into the table that reference it:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public int? ClientId { get; set; }
public Client? Client { get; set; }
</code>
<code>public int? ClientId { get; set; } public Client? Client { get; set; } </code>
public int? ClientId { get; set; }
public Client? Client { get; set; }

The problem is this only supports one other table. I was thinking of modifying the Address class to have a list of the BaseModel stored in it as the following:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public List<BaseModel> AddressObjects { get; } = new List<BaseModel>();
</code>
<code>public List<BaseModel> AddressObjects { get; } = new List<BaseModel>(); </code>
public List<BaseModel> AddressObjects { get; } = new List<BaseModel>();

In theory this would save me having to add fields for every other table that I want to support in the Address class.

I am wondering what is the best/proper way to fixing this?

My current setup:

Address

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public partial class Address: BaseModel
{
public int Id { get; set; }
[ObservableProperty]
private string? _unitNum;
[ObservableProperty]
[NotifyDataErrorInfo]
[Required(ErrorMessage = "Street Number is Required")]
private string _streetNum;
[ObservableProperty]
[NotifyDataErrorInfo]
[Required(ErrorMessage = "Street Name is Required")]
private string _streetName;
[ObservableProperty]
[NotifyDataErrorInfo]
[Required(ErrorMessage = "Street Type is Required")]
private string _streetType;
[ObservableProperty]
[NotifyDataErrorInfo]
[Required(ErrorMessage = "Suburb is Required")]
public string _suburb;
[ObservableProperty]
[NotifyDataErrorInfo]
[Required(ErrorMessage = "City is Required")]
private string _city;
public int? ClientId { get; set; }
public Client? Client { get; set; }
}
</code>
<code>public partial class Address: BaseModel { public int Id { get; set; } [ObservableProperty] private string? _unitNum; [ObservableProperty] [NotifyDataErrorInfo] [Required(ErrorMessage = "Street Number is Required")] private string _streetNum; [ObservableProperty] [NotifyDataErrorInfo] [Required(ErrorMessage = "Street Name is Required")] private string _streetName; [ObservableProperty] [NotifyDataErrorInfo] [Required(ErrorMessage = "Street Type is Required")] private string _streetType; [ObservableProperty] [NotifyDataErrorInfo] [Required(ErrorMessage = "Suburb is Required")] public string _suburb; [ObservableProperty] [NotifyDataErrorInfo] [Required(ErrorMessage = "City is Required")] private string _city; public int? ClientId { get; set; } public Client? Client { get; set; } } </code>
public partial class Address: BaseModel
{
    public int Id { get; set; }    

    [ObservableProperty]
    private string? _unitNum;

    [ObservableProperty]
    [NotifyDataErrorInfo]
    [Required(ErrorMessage = "Street Number is Required")]
    private string _streetNum;

    [ObservableProperty]
    [NotifyDataErrorInfo]
    [Required(ErrorMessage = "Street Name is Required")] 
    private string _streetName;

    [ObservableProperty]
    [NotifyDataErrorInfo]
    [Required(ErrorMessage = "Street Type is Required")]
    private string _streetType;

    [ObservableProperty]
    [NotifyDataErrorInfo]
    [Required(ErrorMessage = "Suburb is Required")]
    public string _suburb;

    [ObservableProperty]
    [NotifyDataErrorInfo]
    [Required(ErrorMessage = "City is Required")]
    private string _city;

    public int? ClientId { get; set; }
    public Client? Client { get; set; }
}

Client

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public partial class Client : Person
{
public int Id { get; set; }
public Address BusinessAddress { get; set; } = null;
}
</code>
<code>public partial class Client : Person { public int Id { get; set; } public Address BusinessAddress { get; set; } = null; } </code>
public partial class Client : Person
{
    public int Id {  get; set; }    

    public Address BusinessAddress { get; set; } = null;    
}

Supplier

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public partial class Supplier : Person
{
public int Id { get; set; }
public string SupplierType { get; set; }
public Address ShopAddress { get; set; } = null;
}
</code>
<code>public partial class Supplier : Person { public int Id { get; set; } public string SupplierType { get; set; } public Address ShopAddress { get; set; } = null; } </code>
public partial class Supplier : Person
{
    public int Id {  get; set; }    

    public string SupplierType { get; set; }

    public Address ShopAddress { get; set; } = null;    
}

Shift

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public partial class Shift : BaseModel
{
public int Id { get; set;}
public Address ShiftLocation {get; set;}
}
</code>
<code>public partial class Shift : BaseModel { public int Id { get; set;} public Address ShiftLocation {get; set;} } </code>
public partial class Shift : BaseModel
{
   public int Id { get; set;}
   public Address ShiftLocation {get; set;}
}

Person

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code> public abstract partial class Person: BaseModel
{
public int Id { get; set; }
[ObservableProperty]
[NotifyDataErrorInfo]
[Required(ErrorMessage = "First Name is Required")]
private string _firstName;
[ObservableProperty]
[NotifyDataErrorInfo]
[Required(ErrorMessage = "Last Name is Required")]
private string _lastName;
}
</code>
<code> public abstract partial class Person: BaseModel { public int Id { get; set; } [ObservableProperty] [NotifyDataErrorInfo] [Required(ErrorMessage = "First Name is Required")] private string _firstName; [ObservableProperty] [NotifyDataErrorInfo] [Required(ErrorMessage = "Last Name is Required")] private string _lastName; } </code>
 public abstract partial class Person: BaseModel
 {
     public int Id { get; set; }

     [ObservableProperty]
     [NotifyDataErrorInfo]
     [Required(ErrorMessage = "First Name is Required")]
     private string _firstName;

     [ObservableProperty]
     [NotifyDataErrorInfo]
     [Required(ErrorMessage = "Last Name is Required")]
     private string _lastName;
}

0

You can use a uni-directional many-to-one model where Supplier, Client, and Shift etc. tables each contain an AddressId. Address does not need a inverse navigation property. You can use a shadow property in each of the entities to avoid exposing an AddressId as well:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public class Supplier : Person
{
public virtual Address ShopAddress { get; set; }
}
</code>
<code>public class Supplier : Person { public virtual Address ShopAddress { get; set; } } </code>
public class Supplier : Person
{
    public virtual Address ShopAddress { get; set; }
}

Then in the configuration:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>modelBuilder.Entity<Supplier>()
.HasOne(x => x.ShopAddress)
.WithMany()
.HasForeignKey("AddressId"); // The FK name in the Supplier table.
</code>
<code>modelBuilder.Entity<Supplier>() .HasOne(x => x.ShopAddress) .WithMany() .HasForeignKey("AddressId"); // The FK name in the Supplier table. </code>
modelBuilder.Entity<Supplier>()
   .HasOne(x => x.ShopAddress)
   .WithMany()
   .HasForeignKey("AddressId"); // The FK name in the Supplier table.

The potential issue with this approach is that it is a many-to-1 meaning two or more suppliers can reference the same address, and a supplier and client etc. can reference the same address. Restricting an address to a single row in a given table can be enforced with a unique constraint on the table’s AddressId, but it’s not a simple matter to enforce an address to not be shared between tables. In that case it is better to use specific address tables or owned entities. (same table as the owner, or a separate address table per owner)

If you do intend to share addresses between owner entities, bi-directional links won’t really work unless all entities on the left side are essentially inheriting the same base class using TPH or TPT inheritance in EF. if you want to share address records between suppliers, clients, and such you can, it just requires a little bit of work to query each of the potential owning entities:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>var clients = Context.Clients.Where(x => x.AddressId == addressId).ToList();
var suppliers = Context.Suppliers.Where(x => x.AddressId == addressId).ToList();
// ...
</code>
<code>var clients = Context.Clients.Where(x => x.AddressId == addressId).ToList(); var suppliers = Context.Suppliers.Where(x => x.AddressId == addressId).ToList(); // ... </code>
var clients = Context.Clients.Where(x => x.AddressId == addressId).ToList();
var suppliers = Context.Suppliers.Where(x => x.AddressId == addressId).ToList();
// ...

If you want to neatly put those all into a single list you can project them to a common descriptive class using .Select(x => new AddressOwner { /* populate common descriptive fields */ }).ToList(); for each and Union() the resulting lists together. If AddressId is indexed these will be fairly fast, low impact queries to run.

Edit: With a TablePerConcrete inheritance schema (ClientAddress vs SupplierAddress each containing the address columns for their respective parent, non-shared) The entities can still share a common IAddress inteface to expose the common properties. When you design an EditAddress UI it’s model can be an IAddress, accepting any of the address classes.

To share addresses between client/supplier/etc. then a TablePerType schema would be more applicable. This has a ClientAddress/SupplierAddress, but they each link to an Address table row.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public class Supplier : Person
{
public virtual SupplierAddress ShopAddress { get; set; }
}
public class Address
{
[Key]
public int AddressId { get; set; }
// Address columns
}
public class SupplierAddress : IAddress
{
[Key]
public int ShopAddressId { get; set; }
public virtual ICollection<Supplier> Suppliers { get; } = []; // Optional
public virtual Address Address { get; set; }
int IAddress.Id => ShopAddressId;
string IAddress.AddressType => typeof(SupplierAddress).Name;
string IAddress.Street
{
get => Address.Street;
set => Address.Street = value;
}
}
</code>
<code>public class Supplier : Person { public virtual SupplierAddress ShopAddress { get; set; } } public class Address { [Key] public int AddressId { get; set; } // Address columns } public class SupplierAddress : IAddress { [Key] public int ShopAddressId { get; set; } public virtual ICollection<Supplier> Suppliers { get; } = []; // Optional public virtual Address Address { get; set; } int IAddress.Id => ShopAddressId; string IAddress.AddressType => typeof(SupplierAddress).Name; string IAddress.Street { get => Address.Street; set => Address.Street = value; } } </code>
public class Supplier : Person
{
    public virtual SupplierAddress ShopAddress { get; set; }
}

public class Address
{
    [Key]
    public int AddressId { get; set; }
    // Address columns
}

public class SupplierAddress : IAddress
{
    [Key]
    public int ShopAddressId { get; set; }
    public virtual ICollection<Supplier> Suppliers { get; } = []; // Optional
    public virtual Address Address { get; set; }

    int IAddress.Id => ShopAddressId;
    string IAddress.AddressType => typeof(SupplierAddress).Name;

    string IAddress.Street
    {
        get => Address.Street;
        set => Address.Street = value;
    }
}

Here EF can be configured to either treat the relationship between the Supplier and SupplierAddress as a many-to-one with, or without a Suppliers collection in Supplier address, or it can alternatively be configured as a 1-to-1 if only one supplier should link to the address. (but other parent types can share the address)

With the TPT inheritance you could have a DbSet of Address, or alternatively just use a common IAddress interface which your UI expects as a model. To get that common interface to work you would need the PK and a type identifier (enum or string, etc.) for each type of address available for binding so you know what to hand off to to update the appropriate row.

The main consideration with a shared address between rows or parents is what should happen when an address is edited as part of one parent. Using a shared reference, editing the address fields would affect any and all parent rows referencing that address. If this isn’t the desired behavior then you have to take care to insert a new Address record or associate the parent to a different existing address when editing a parent to reflect a new location.

Edit: The IAddress interface would represent the common address view, so it would look pretty much like the base Address table:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public interface IAddress
{
int Id { get; }
string AddressType { get; } // or could be an Enumeration for Supplier vs. Client, vs. etc.
string StreetNumber { get; }
string Street { get; }
string State { get; }
string Postcode { get; }
string Country { get; }
}
</code>
<code>public interface IAddress { int Id { get; } string AddressType { get; } // or could be an Enumeration for Supplier vs. Client, vs. etc. string StreetNumber { get; } string Street { get; } string State { get; } string Postcode { get; } string Country { get; } } </code>
public interface IAddress
{
    int Id { get; } 
    string AddressType { get; } // or could be an Enumeration for Supplier vs. Client, vs. etc.
    string StreetNumber { get; }
    string Street { get; }
    string State { get; }
    string Postcode { get; }
    string Country { get; }
}

Ideally I would recommend using ViewModels to implement the IAddress interface that are populated by the entity structure, but in case you are passing entities to the view and back: Each typed address would extend this interface. If you are using a TPC inheritance where SupplierAddress/ClientAddress etc. each have the address fields as part of them then for the most part the class looks the same:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public class SupplierAddress : IAddress
{
public int SupplierAddressId { get; set; }
public string StreetNumber { get; }
public string Street { get; }
public string State { get; }
public string Postcode { get; }
public string Country { get; }
int IAddress.Id { get => SupplierAddressId }
string IAddress.AddressType { get => typeof(SupplierAddress).Name; }
}
</code>
<code>public class SupplierAddress : IAddress { public int SupplierAddressId { get; set; } public string StreetNumber { get; } public string Street { get; } public string State { get; } public string Postcode { get; } public string Country { get; } int IAddress.Id { get => SupplierAddressId } string IAddress.AddressType { get => typeof(SupplierAddress).Name; } } </code>
public class SupplierAddress : IAddress
{
    public int SupplierAddressId { get; set; }
    public string StreetNumber { get; }
    public string Street { get; }
    public string State { get; }
    public string Postcode { get; }
    public string Country { get; }

    int IAddress.Id { get => SupplierAddressId } 
    string IAddress.AddressType { get => typeof(SupplierAddress).Name; } 
}

I use explicit interface implementation for the Id and AddressType since these are only relevant to the view etc. using the IAddress wrapper. Your view’s “model” would be of type IAddress so it can be given any of the concrete address objects read “as an” IAddress so the view won’t know of the specific SupplierAddressId, it will see “Id” and “AddressType” in addition to the common StreetNumber, Street, etc. Id points to the SupplierAddressId, while AddressType can be sent back to the controller as a parameter or used to tell which controller to call to save a SupplierAddress vs. a ClientAddress.

If using a TPT inheritance model where there is a common Address table as well as a SupplierAddress/ClientAddress linking table then we need to add one property to the IAddress interface:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>int AddressId { get; }
</code>
<code>int AddressId { get; } </code>
int AddressId { get; }

This is in addition to the “Id” as we will need to be able to relay the linked Address record ID if we want to support re-associating an SupplierAddress to another existing physical location.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public class SupplierAddress : IAddress
{
public int SupplierAddressId { get; set; }
public virtual Address Address { get; set; } = new();
int IAddress.Id { get => SupplierAddressId }
string IAddress.AddressType { get => typeof(SupplierAddress).Name; }
public int AddressId { get; set; }
public string StreetNumber
{
get => Address.StreetNumber;
set => Address.StreetNumber = value;
}
public string Street
{
get => Address.Street;
set => Address.Street = value;
}
public string State
{
get => Address.State;
set => Address.State = value;
}
public string Postcode
{
get => Address.Postcode;
set => Address.Postcode = value;
}
public string Country
{
get => Address.Country;
set => Address.Country = value;
}
}
</code>
<code>public class SupplierAddress : IAddress { public int SupplierAddressId { get; set; } public virtual Address Address { get; set; } = new(); int IAddress.Id { get => SupplierAddressId } string IAddress.AddressType { get => typeof(SupplierAddress).Name; } public int AddressId { get; set; } public string StreetNumber { get => Address.StreetNumber; set => Address.StreetNumber = value; } public string Street { get => Address.Street; set => Address.Street = value; } public string State { get => Address.State; set => Address.State = value; } public string Postcode { get => Address.Postcode; set => Address.Postcode = value; } public string Country { get => Address.Country; set => Address.Country = value; } } </code>
public class SupplierAddress : IAddress
{
    public int SupplierAddressId { get; set; }
    public virtual Address Address { get; set; } = new(); 

    int IAddress.Id { get => SupplierAddressId } 
    string IAddress.AddressType { get => typeof(SupplierAddress).Name; } 

    public int AddressId { get; set; }

    public string StreetNumber 
    { 
        get => Address.StreetNumber; 
        set => Address.StreetNumber = value;
    }
    public string Street 
    { 
        get => Address.Street; 
        set => Address.Street = value;
    }
    public string State 
    { 
        get => Address.State; 
        set => Address.State = value;
    }
    public string Postcode 
    { 
        get => Address.Postcode; 
        set => Address.Postcode = value;
    }
    public string Country 
    { 
        get => Address.Country; 
        set => Address.Country = value;
    }
}

Normally I don’t recommend initializing a reference to another entity, but it will be necessary if you pass entities. You will need to be careful when updating a SupplierAddress etc. this way, you cannot simply call DbContext.Update(). Since we could point the SupplierAddress at a new address vs. update the existing address details you will need to handle both of those scenarios. In the case of an insert if you get an AddressId you ignore the address street etc. fields and create a SupplierAddress linked to the AddressId provided. If you get a “0” then create a new SupplierAddress and Address with the provided data. When updating you should always fetch the existing SupplierAddress & Address then base your action on the updated AddressId. For instance:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>var existingSupplierAddress = Context.SupplierAddresses
.Include(x => x.Address)
.Single(x => x.SupplierAddressId == address.Id);
if (address.AddressId != existingSupplierAddress.Address.AddressId)
{
var updatedAddress = Context.Addresses
.Single(x => x.AddressId == address.AddressId);
existingSupplierAddress.Address = updatedAddress;
}
else
{
existingSupplierAddress.Address.StreetNumber = address.StreetNumber;
existingSupplierAddress.Address.Street = address.Street;
// ...
}
Context.SaveChanges();
</code>
<code>var existingSupplierAddress = Context.SupplierAddresses .Include(x => x.Address) .Single(x => x.SupplierAddressId == address.Id); if (address.AddressId != existingSupplierAddress.Address.AddressId) { var updatedAddress = Context.Addresses .Single(x => x.AddressId == address.AddressId); existingSupplierAddress.Address = updatedAddress; } else { existingSupplierAddress.Address.StreetNumber = address.StreetNumber; existingSupplierAddress.Address.Street = address.Street; // ... } Context.SaveChanges(); </code>
var existingSupplierAddress = Context.SupplierAddresses
    .Include(x => x.Address)
    .Single(x => x.SupplierAddressId == address.Id);

if (address.AddressId != existingSupplierAddress.Address.AddressId)
{
    var updatedAddress = Context.Addresses
        .Single(x => x.AddressId == address.AddressId);
    existingSupplierAddress.Address = updatedAddress;
}
else
{
    existingSupplierAddress.Address.StreetNumber = address.StreetNumber;
    existingSupplierAddress.Address.Street = address.Street;
    // ...
}
Context.SaveChanges();

A table-per-concrete inheritance will be much simpler & safer to work with since the table-per-type model above means potentially updating address information for more than one parent record.

Edit: I had a look at your code sample and it’s close, but I though I’d expand on the different examples of inheritance and make a correction to the answer around TPT. While the database structure for TPT would allow sharing, EF will not allow sharing a base address across different types of Addresses, it expects the base Address to be the PK and FK as far as relations from other entities. The database would allow a ClientAddress and SupplierAddress table’s AddressId PK to both point at a single Address row, but EF isn’t happy with this in an inheritance model. (which does make sense because that would be two extended Addresses with the same PK) So I expanded a model called “MtO” which is a Many-to-One example which replaces the inheritance with a regular Client-ClientAddress-Address relationship. This can support multiple clients sharing an address, or a Client and Supplier sharing an address.

The code snippets are all available on PasteBin along with the test SQL Schema I used for SQL Server Express. (just create a database called “Inheritance” to create the 4 schemas and adjust the DbContext connection strings for your machine name / auth)

  • Schema: https://pastebin.com/Di2t71Rm
  • TPC Example: https://pastebin.com/rGYELzcs
  • TPH Example: https://pastebin.com/Xcfv0408
  • TPT Example: https://pastebin.com/byVJKmbK
  • MtO Example: https://pastebin.com/tFv6UQez

With the TPT if we want to link a supplier to the same address as a client we cannot really do this as we cannot tell EF to create a new SupplierAddress but link to the same existing Address. With the MtO approach we can with having the same shared Address table. With the MtO association between the ClientAddress-Address I am instantiating a new Address by default. This is generally not advised when working with reference types but the default behaviour for a new ClientAddress is it will be treated as a new Address. If you want to associate to an existing Address row, that is fine, just set the Address. The catch is to remember to eager load .ClientAddress and .ClientAddress.Address when reading MtO. With TPT there is no .Address to worry about thanks to the Inheritance.

The use of the IAddress interface would be mainly to enable a single editing page for address details. The view bindings and controller action can use this as the Model, but if passing it back to the controller you should be inspecting the AddressType to either talk to the correct type controller for the address type, or handing off to update the correct concrete address. You won’t be able to (and shouldn’t) simply try and cast the passed data to a concrete address subclass and call DbContext.Update((ClientAddress)address); Instead, depending slightly on the inheritance mode, once you know you are dealing with a ClientAddress you would use the IAddress.Id (ClientAddressId) to fetch the ClientAddress then copy the values across from the passed in IAddress. For the TPT/MtO you need to differentiate between “I want to associate this client to a new/different address” vs. “I want to update the client’s existing address details” For MtO that means also changing the address details of anyone else pointing at that address.

Hopefully that gives you some ideas and insights into options to get what you want from the relationships.

12

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật