Interface to enumerate over files

I’m trying to design a generic interface for enumerating over a list files. Each iteration will open a file, allow the consuming code to access the file to perform some task, and then close the file.

At first this seemed like like a simple enumeration pattern, but the consuming code actually needs to know the status of each file both after it is opened and after it is closed. So rather then having a single MoveNext() method my interface has OpenNextFile() and CloseCurrentFile() methods, each of which may return a failure status. In addition, CloseCurrentFile() needs a way to report whether each file was saved or not when it was closed.

Here’s my current interface.

public interface IFileEnumerator
{
    //The following properties reflect the current state of the IFileEnumerator
    FileEnumeratorStatus Status { get; }
    string StatusMessage { get; }
    string FileName { get; }
    byte[] FileData { get; }

    //Attempts to open the next file
    FileEnumeratorStatus OpenNextFile();

    //Closes the current file
    FileEnumeratorStatus CloseCurrentFile();
}


public enum FileEnumeratorStatus
{ 
    AtStart, //Files have not been opened
    Opened, //File currently opened
    Opened_Failed, //Failed to open file
    Close_Saved, //File closed and saved
    Closed_NotSaved, //File closed and not saved
    Close_SaveFailed, //File closed saving failed
    AtEnd //No more files
}

Here’s an example of the consuming code:

//FileProcessor is a class which run a ProcesssJob on a set of files returned by a     
//IFileEnumerator and returns a ProcesssingJobResult with results of all the files 
public class FileProcessor
{
    public ProcesssingJobResult RunJob(IFileEnumerator fileEnumerator, ProcesssJob job)
    {
        List<FileProcessingResult> resultList = new List<FileProcessingResult>();

        while (fileEnumerator.OpenNextFile() != FileEnumeratorStatus.AtEnd)
        {
            FileProcessingResult currentFileResult = new FileProcessingResult();
            currentFileResult.FileName = fileEnumerator.FileName;

            if (fileEnumerator.Status == FileEnumeratorStatus.Opened)
            {
                currentFileResult.JobCompleted = job.RunJob(fileEnumerator.FileData);

                var closureStatus = fileEnumerator.CloseCurrentFile();

                if (closureStatus == FileEnumeratorStatus.Close_Saved)
                    currentFileResult.Saved = true;
                else if (closureStatus == FileEnumeratorStatus.Closed_NotSaved)
                    currentFileResult.Saved = false;
                else if (closureStatus == FileEnumeratorStatus.Close_SaveFailed)
                {
                    currentFileResult.Saved = false;
                    currentFileResult.Message = fileEnumerator.StatusMessage;
                }
            }
            else if (fileEnumerator.Status == FileEnumeratorStatus.Opened_Failed)
            {
                currentFileResult.JobCompleted = false;
                currentFileResult.Message = fileEnumerator.StatusMessage;
                currentFileResult.Saved = false;
            }

            resultList.Add(currentFileResult);
        }

        ProcesssingJobResult jobresult = new ProcesssingJobResult();
        jobresult.TimeStamp = DateTime.Now.ToUniversalTime();
        jobresult.Machine = System.Environment.MachineName;
        jobresult.User = System.Environment.UserName;
        jobresult.Results = resultList;

        return jobresult;
    }
}

//A generic task that can be run on a file
public interface ProcesssJob
{
    bool RunJob(byte[] data);
}

//Result of processing one file
public class FileProcessingResult
{
    public bool JobCompleted { get; set; }
    public bool Saved { get; set; }
    public string FileName { get; set; }
    public string Message { get; set; }

}

//The overall result of processing a set of files
public class ProcesssingJobResult
{
    public DateTime TimeStamp { get; set; }
    public String Machine { get; set; }
    public String User { get; set; }
    public List<FileProcessingResult> Results { get; set; }
}

Maybe this is just a question of careful documentation, but I’m having trouble dealing with all these different possible states. Drawing a state diagram doesn’t help as it’s possible to go from every state to just about every other state. Should the consuming code call CloseCurrentFile() if OpenNextFile() return Open_Failed? What if it does? Would a Closed status be returned even if a file was never opened? If CloseCurrentFile() was not called after OpenNextFile() should it be closed automatically by the next call to OpenNextFile() or should an exception be thrown? I’d rather someone implementing this interface not have to worry about getting all this right.

The problem is much simpler if I have a single OpenNextFile() method which is responsible for both closing the last opened file, and opening the next file. Yet then this single method needs to report both the result of opening the next file, as well as closing the last file. This also becomes tricky for consuming code as the final state of each file is not determined until the following iteration.

public interface IFileEnumerator_Alternate
{
    string FileName { get; }
    byte[] FileData { get; }
    string StatusMessage {get;}

    //Closes the previous file, and opens the next file
    Result OpenNextFile();
}

public class Result
{
    public bool CurrentFileOpened {get; set;}
    public string CurrentFileName {get; set;}
    public string CurrentFileStatusMessage {get; set;}

    public string LastFileName {get; set;}
    public bool LastFileClosedOk {get; set;}
    public string LastFileClosedStatusMessage {get; set;}
    public bool LastFileSaved {get; set;}
}

Is there another way to look at this, or some other common design pattern that is more suited to what I’m trying to do? Here’s an activity diagram showing the overall process.

Activity Diagram

11

What about approaching the problem from the other side. You have a class, that represents that nice flow diagram of yours and this flow diagram creates “events” based on transitions. Those events then can be represented as either an interface or events:

public interface IFileProcessor
{
    void HandleFile(string fileName, Stream data);
    void OpenFailed(string fileName);
    void CloseFailed(string fileName);
    void Closed(string fileName, bool saved);
    void AllFilesProcessed(); // maybe?
}

Then your “enumerator” will accept instance of class that implements this interface and executes the relevant methods.

string[] files = ...; // what files?
IFileProcessor processor = ...; // how the files should be handled?
var enumerator = new FileEnumerator(files, processor);
enumerator.Run(); // process all files

This solutions seems much simpler, but slightly harder to understand due to inverted control.

12

What if instead of using an interface I create an abstract class with abstract methods of each of these steps which concrete classes have to implement

Yes. An interface does not tell how the methods interact or how to process. That is just one technique for dependency injection.

Part of the problem is acquiring the list of files can be a long process. Each file may need to be downloaded, and opened to find nested contents etc..etc. So I want to do the processing as the files are being iterated over rather then opening them twice.

Document Facade

A general meta-data class & and associated “framework” with injectable custom equality and comparison functionality.

public class DocumentFacade : IEquatable<DocumentFacade>, IComparable<DocumentFacade> {
    // this class is more like document metadata, not necessarily a facade design
    // pattern wrapping the Document object itself
    protected EqualityComparer<DocFacade> equalizer;  // customized Equals, CompareTo

    public string Name { get; set; }
    // superset of any and all properties relevant to processing. Covers all
    // document sub classes. 

    public DocStatus OpenStatus { get; set; }  // an enum
    public DocStatus CloseStatus { get; set; }
    public DocClass DocumentClass; // an enum, alternatively Type.Name, or even the Type object itself.

    public BaseDocClass theDoc;  //reference to the document this DocumentFacade represents
                                 // maybe you don't even need this.

   public DocumentFacade (IEqualityComparer<DocFacade> equalizer) {
       if(equalizer == null) throw new NullArgumentException();
       this.equalizer = equalizer;
   }

    public override bool Equals(object that) {
       if(that == null) return false;
       if(that.DocumentType != this.DocumentType) return false;
        return equalizer.Equals(this, other);
    }

    // the IEquatable implementation
    public bool Equals(this, y) {
        return Equals(y);
    }
} 


// A class for each way we want to compare Documents; each sub-class I suppose.
// MSDN says inheriting is the way to go, vice interface implementation.
public class DocFacadeEqualizerTypeA : IEqualityComparer<DocumentFacade> {
    public override bool Equals (DocFacade x, DocFacade y) {
        // customized implementation
    }
    // ditto CompareTo override
}

public class DocumentFacadeCollection : List<DocFacade> {

    public bool Equals (DocFacade x, DocFacade y) {
        if(x == null) return false;
        return x.Equals(y);
    }

    // lots of inherited List stuff makes this class really handy.
    public DocFacadeCollection GetOpenStatus(DocStatus thisStatus) {
        Find( x => x.OpenStatus == thisStatus);
    }

    public DocFacadeCollection FindAllDuplicates (DocFacadeCollection otherList) {
         DocFacadeCollection duplicates = new DocFacadeCollection();
         duplicates.AddRange( this.FindAll( x => otherList.Contains( x )).AsEnumerable());
         return duplicates;
    }
}

public static class DocumentDNAAnalyzer {
    // this may be a factory, or perhaps more likely, the front of
    // of a Builder pattern implimentation
    // Besides a customized DocFacade object, we may also be able to
    // provide stuff for abstract template.

    public static DocFacadeFactory = new DocFacadeFactory();

    public static DocFacade Create(object TheDocument) { 
        return DocFacadeFactory.Create(TheDocument));
    }
}

See this thread for a great GetHashCode() Implementation

I think that you should separate enumerating over files and handing each particular file.

There already is an interface in .Net for enumerating: IEnumerable<T>, so I would use that.

That leaves working with a single file: that would look pretty much like your IFileEnumerator, except with the enumerator parts removed.

I also don’t think that opening and closing should use the same status enum.

public interface IFile
{
    string FileName { get; }
    byte[] FileData { get; }

    bool Open();

    FileCloseStatus Close();
}

public enum FileCloseStatus 
{ 
    Saved, //File closed and saved
    NotSaved, //File closed and not saved
    SaveFailed, //File closed saving failed
}

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

Interface to enumerate over files

I’m trying to design a generic interface for enumerating over a list files. Each iteration will open a file, allow the consuming code to access the file to perform some task, and then close the file.

At first this seemed like like a simple enumeration pattern, but the consuming code actually needs to know the status of each file both after it is opened and after it is closed. So rather then having a single MoveNext() method my interface has OpenNextFile() and CloseCurrentFile() methods, each of which may return a failure status. In addition, CloseCurrentFile() needs a way to report whether each file was saved or not when it was closed.

Here’s my current interface.

public interface IFileEnumerator
{
    //The following properties reflect the current state of the IFileEnumerator
    FileEnumeratorStatus Status { get; }
    string StatusMessage { get; }
    string FileName { get; }
    byte[] FileData { get; }

    //Attempts to open the next file
    FileEnumeratorStatus OpenNextFile();

    //Closes the current file
    FileEnumeratorStatus CloseCurrentFile();
}


public enum FileEnumeratorStatus
{ 
    AtStart, //Files have not been opened
    Opened, //File currently opened
    Opened_Failed, //Failed to open file
    Close_Saved, //File closed and saved
    Closed_NotSaved, //File closed and not saved
    Close_SaveFailed, //File closed saving failed
    AtEnd //No more files
}

Here’s an example of the consuming code:

//FileProcessor is a class which run a ProcesssJob on a set of files returned by a     
//IFileEnumerator and returns a ProcesssingJobResult with results of all the files 
public class FileProcessor
{
    public ProcesssingJobResult RunJob(IFileEnumerator fileEnumerator, ProcesssJob job)
    {
        List<FileProcessingResult> resultList = new List<FileProcessingResult>();

        while (fileEnumerator.OpenNextFile() != FileEnumeratorStatus.AtEnd)
        {
            FileProcessingResult currentFileResult = new FileProcessingResult();
            currentFileResult.FileName = fileEnumerator.FileName;

            if (fileEnumerator.Status == FileEnumeratorStatus.Opened)
            {
                currentFileResult.JobCompleted = job.RunJob(fileEnumerator.FileData);

                var closureStatus = fileEnumerator.CloseCurrentFile();

                if (closureStatus == FileEnumeratorStatus.Close_Saved)
                    currentFileResult.Saved = true;
                else if (closureStatus == FileEnumeratorStatus.Closed_NotSaved)
                    currentFileResult.Saved = false;
                else if (closureStatus == FileEnumeratorStatus.Close_SaveFailed)
                {
                    currentFileResult.Saved = false;
                    currentFileResult.Message = fileEnumerator.StatusMessage;
                }
            }
            else if (fileEnumerator.Status == FileEnumeratorStatus.Opened_Failed)
            {
                currentFileResult.JobCompleted = false;
                currentFileResult.Message = fileEnumerator.StatusMessage;
                currentFileResult.Saved = false;
            }

            resultList.Add(currentFileResult);
        }

        ProcesssingJobResult jobresult = new ProcesssingJobResult();
        jobresult.TimeStamp = DateTime.Now.ToUniversalTime();
        jobresult.Machine = System.Environment.MachineName;
        jobresult.User = System.Environment.UserName;
        jobresult.Results = resultList;

        return jobresult;
    }
}

//A generic task that can be run on a file
public interface ProcesssJob
{
    bool RunJob(byte[] data);
}

//Result of processing one file
public class FileProcessingResult
{
    public bool JobCompleted { get; set; }
    public bool Saved { get; set; }
    public string FileName { get; set; }
    public string Message { get; set; }

}

//The overall result of processing a set of files
public class ProcesssingJobResult
{
    public DateTime TimeStamp { get; set; }
    public String Machine { get; set; }
    public String User { get; set; }
    public List<FileProcessingResult> Results { get; set; }
}

Maybe this is just a question of careful documentation, but I’m having trouble dealing with all these different possible states. Drawing a state diagram doesn’t help as it’s possible to go from every state to just about every other state. Should the consuming code call CloseCurrentFile() if OpenNextFile() return Open_Failed? What if it does? Would a Closed status be returned even if a file was never opened? If CloseCurrentFile() was not called after OpenNextFile() should it be closed automatically by the next call to OpenNextFile() or should an exception be thrown? I’d rather someone implementing this interface not have to worry about getting all this right.

The problem is much simpler if I have a single OpenNextFile() method which is responsible for both closing the last opened file, and opening the next file. Yet then this single method needs to report both the result of opening the next file, as well as closing the last file. This also becomes tricky for consuming code as the final state of each file is not determined until the following iteration.

public interface IFileEnumerator_Alternate
{
    string FileName { get; }
    byte[] FileData { get; }
    string StatusMessage {get;}

    //Closes the previous file, and opens the next file
    Result OpenNextFile();
}

public class Result
{
    public bool CurrentFileOpened {get; set;}
    public string CurrentFileName {get; set;}
    public string CurrentFileStatusMessage {get; set;}

    public string LastFileName {get; set;}
    public bool LastFileClosedOk {get; set;}
    public string LastFileClosedStatusMessage {get; set;}
    public bool LastFileSaved {get; set;}
}

Is there another way to look at this, or some other common design pattern that is more suited to what I’m trying to do? Here’s an activity diagram showing the overall process.

Activity Diagram

11

What about approaching the problem from the other side. You have a class, that represents that nice flow diagram of yours and this flow diagram creates “events” based on transitions. Those events then can be represented as either an interface or events:

public interface IFileProcessor
{
    void HandleFile(string fileName, Stream data);
    void OpenFailed(string fileName);
    void CloseFailed(string fileName);
    void Closed(string fileName, bool saved);
    void AllFilesProcessed(); // maybe?
}

Then your “enumerator” will accept instance of class that implements this interface and executes the relevant methods.

string[] files = ...; // what files?
IFileProcessor processor = ...; // how the files should be handled?
var enumerator = new FileEnumerator(files, processor);
enumerator.Run(); // process all files

This solutions seems much simpler, but slightly harder to understand due to inverted control.

12

What if instead of using an interface I create an abstract class with abstract methods of each of these steps which concrete classes have to implement

Yes. An interface does not tell how the methods interact or how to process. That is just one technique for dependency injection.

Part of the problem is acquiring the list of files can be a long process. Each file may need to be downloaded, and opened to find nested contents etc..etc. So I want to do the processing as the files are being iterated over rather then opening them twice.

Document Facade

A general meta-data class & and associated “framework” with injectable custom equality and comparison functionality.

public class DocumentFacade : IEquatable<DocumentFacade>, IComparable<DocumentFacade> {
    // this class is more like document metadata, not necessarily a facade design
    // pattern wrapping the Document object itself
    protected EqualityComparer<DocFacade> equalizer;  // customized Equals, CompareTo

    public string Name { get; set; }
    // superset of any and all properties relevant to processing. Covers all
    // document sub classes. 

    public DocStatus OpenStatus { get; set; }  // an enum
    public DocStatus CloseStatus { get; set; }
    public DocClass DocumentClass; // an enum, alternatively Type.Name, or even the Type object itself.

    public BaseDocClass theDoc;  //reference to the document this DocumentFacade represents
                                 // maybe you don't even need this.

   public DocumentFacade (IEqualityComparer<DocFacade> equalizer) {
       if(equalizer == null) throw new NullArgumentException();
       this.equalizer = equalizer;
   }

    public override bool Equals(object that) {
       if(that == null) return false;
       if(that.DocumentType != this.DocumentType) return false;
        return equalizer.Equals(this, other);
    }

    // the IEquatable implementation
    public bool Equals(this, y) {
        return Equals(y);
    }
} 


// A class for each way we want to compare Documents; each sub-class I suppose.
// MSDN says inheriting is the way to go, vice interface implementation.
public class DocFacadeEqualizerTypeA : IEqualityComparer<DocumentFacade> {
    public override bool Equals (DocFacade x, DocFacade y) {
        // customized implementation
    }
    // ditto CompareTo override
}

public class DocumentFacadeCollection : List<DocFacade> {

    public bool Equals (DocFacade x, DocFacade y) {
        if(x == null) return false;
        return x.Equals(y);
    }

    // lots of inherited List stuff makes this class really handy.
    public DocFacadeCollection GetOpenStatus(DocStatus thisStatus) {
        Find( x => x.OpenStatus == thisStatus);
    }

    public DocFacadeCollection FindAllDuplicates (DocFacadeCollection otherList) {
         DocFacadeCollection duplicates = new DocFacadeCollection();
         duplicates.AddRange( this.FindAll( x => otherList.Contains( x )).AsEnumerable());
         return duplicates;
    }
}

public static class DocumentDNAAnalyzer {
    // this may be a factory, or perhaps more likely, the front of
    // of a Builder pattern implimentation
    // Besides a customized DocFacade object, we may also be able to
    // provide stuff for abstract template.

    public static DocFacadeFactory = new DocFacadeFactory();

    public static DocFacade Create(object TheDocument) { 
        return DocFacadeFactory.Create(TheDocument));
    }
}

See this thread for a great GetHashCode() Implementation

I think that you should separate enumerating over files and handing each particular file.

There already is an interface in .Net for enumerating: IEnumerable<T>, so I would use that.

That leaves working with a single file: that would look pretty much like your IFileEnumerator, except with the enumerator parts removed.

I also don’t think that opening and closing should use the same status enum.

public interface IFile
{
    string FileName { get; }
    byte[] FileData { get; }

    bool Open();

    FileCloseStatus Close();
}

public enum FileCloseStatus 
{ 
    Saved, //File closed and saved
    NotSaved, //File closed and not saved
    SaveFailed, //File closed saving failed
}

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