Skip to main content
All docs
V22.1

Attach Files to Objects (.NET 6)

  • 6 minutes to read

This lesson describes how to attach file collections to business objects. In this tutorial, we will add the File Attachment Module to an ASP.NET Core Blazor application, and implement new business classes: Resume to store a Contact’s resume information and PortfolioFileData to save file data collection items.

Note

Before you proceed, take a moment to review the following lessons:

  • Implement Custom Business Classes and Reference Properties (XPO/EF)
  • Inherit from the Business Class Library Class (XPO/EF)
  • Set a One-to-Many Relationship (XPO/EF)
  • Implement Property Value Validation in Code (XPO/EF)

Step-by-Step Instructions

  1. Add the DevExpress.ExpressApp.FileAttachment.Blazor NuGet package to the MySolution.Blazor.Server project. See the following topic for more information on how to install DevExpress NuGet packages: Install DevExpress Controls Using NuGet Packages.

  2. In the Startup.cs file, call the AddFileAttachments(IModuleBuilder<IBlazorApplicationBuilder>, Action<FileAttachmentsOptions>) method to add the File Attachment Module to your application:

    public class Startup {
    // ...
        public void ConfigureServices(IServiceCollection services) {
            // ...
            services.AddXaf(Configuration, builder => {
                builder.UseApplication<MySolutionBlazorApplication>();
                builder.Modules
                    .AddConditionalAppearance()
                    // ...
                    .AddFileAttachments()
                // ...
            });
            // ...
        }
    }
    

    If you add the File Attachment Module when you create an XAF application, the Solution Wizard generates the code used to add the File Attachment Module automatically.

  3. Right-click the Business Objects folder in the MySolution.Module project and choose Add Item | Class… Specify Resume.cs as the new file name and click Add.

  4. Replace the generated class declaration with the following code:

    using DevExpress.Persistent.Base;
    using DevExpress.Persistent.BaseImpl;
    using DevExpress.Xpo;
    using MySolution.Module.BusinessObjects;
    
    namespace MySolution.Module.BusinessObjects {
        [DefaultClassOptions]
        [ImageName("BO_Resume")]
        public class Resume : BaseObject {
            public Resume(Session session) : base(session) {}
            private Contact contact;
            [Association("Contact-Resumes")]
            public Contact Contact {
                get { return contact; }
                set { SetPropertyValue(nameof(Contact), ref contact, value); }
            }
            [Aggregated, Association("Resume-PortfolioFileData")]
            public XPCollection<PortfolioFileData> Portfolio {
                get { return GetCollection<PortfolioFileData>(nameof(Portfolio)); }
            }
        }
    }   
    
  5. Add the Resume property to the Contact class:

    namespace MySolution.Module.BusinessObjects {
        [DefaultClassOptions]
        public class Contact : Person {
            // ...
            [Association("Contact-Resumes")]
            public XPCollection<Resume> Resumes {
                get { return GetCollection<Resume>(nameof(Resumes)); }
            }
        }
    } 
    
  6. Add another class to the MySolution.Module project and name it PortfolioFileData. Replace the generated class declaration with the following code:

    using DevExpress.Persistent.BaseImpl;
    using DevExpress.Xpo;
    
    namespace MySolution.Module.BusinessObjects {
        public class PortfolioFileData : FileAttachmentBase {
            public PortfolioFileData(Session session) : base(session) { }
            private Resume resume;
            [Association("Resume-PortfolioFileData")]
            public Resume Resume {
                get { return resume; }
                set { SetPropertyValue(nameof(Resume), ref resume, value); }
            }
            public override void AfterConstruction() {
                base.AfterConstruction();
                documentType = DocumentType.Unknown;
            }
            private DocumentType documentType;
            public DocumentType DocumentType {
                get { return documentType; }
                set { SetPropertyValue(nameof(DocumentType), ref documentType, value); }
            }
        }
        public enum DocumentType {
            SourceCode = 1, Tests = 2, Documentation = 3,
            Diagrams = 4, ScreenShots = 5, Unknown = 6
        };
    }    
    

    In this lesson, we derive PortfolioFileData from the FileAttachmentBase class in the XPO-based application and from the FileAttachment class in the EF Core-based application. FileAttachmentBase and FileAttachment are Business Class Library classes. These classes can store files in the database. For more information about file attachment properties, see the following topics: File Attachment Properties in XPO/EF Core.

    In the XPO-based application, we initialize the PortfolioFileData.DocumentType property in the AfterConstruction() method that is called after the corresponding object’s creation. See the following topic for details: How to: Initialize Business Objects with Default Property Values in XPO.

  7. In EF Core-based applications, add the Required attribute to the Resume property in the PortfolioFileData class.

    using DevExpress.Persistent.BaseImpl.EF;
    
    namespace MySolution.Module.BusinessObjects {
        [ImageName("BO_FileAttachment")]
        public class PortfolioFileData : FileAttachment {
            public PortfolioFileData()
                : base() {
                DocumentType = DocumentType.Unknown;
            }
            //..           
            [Required]
            public virtual Resume Resume { get; set; }
            //...
            }
        }
        //...
    }
    

    The Resume and PortfolioFileData classes are connected with a One-to-Many relationship. For more information on how to create a one-to-many relationship between business objects, see the following topics: Set a One-to-Many Relationship (XPO/EF Core).

    In the EF Core-based application, a deletion of a master object does not delete the related objects. In this lesson, we use the Required attribute to configure the associations between classes. This way you can delete the referenced objects with the master object and avoid integrity violation.

    Alternatively, you can use the Fluent API and specify the OnDelete method for the Portfolio-Resume relationship as described in the following topic: The Fluent API OnDelete Method.

  8. Add the RuleRequiredFieldAttribute attribute to the Resume property to implement property value validation:

    using DevExpress.Persistent.BaseImpl;
    using DevExpress.Xpo;
    
    namespace MySolution.Module.BusinessObjects {
        public class PortfolioFileData : FileAttachmentBase {
            public PortfolioFileData(Session session) : base(session) { }
            private Resume resume;
            [Association("Resume-PortfolioFileData")]
            [RuleRequiredField(DefaultContexts.Save)]
            public Resume Resume {
                get { return resume; }
                set { SetPropertyValue(nameof(Resume), ref resume, value); }
            }
            //...
        }
        //..
    }    
    

    For additional information on validation, refer to the following article: Validation (Prevent Data Errors).

  9. In EF Core-based applications, open the MySolution.Module.BusinessObjects\MySolutionDbContext.cs file and add the properties of Resume, PortfolioFileData, and FileAttachments types to the DbContext:

    public class MySolutionDbContext : DbContext {
        //...
        public DbSet<Resume> Resumes { get; set; }
        public DbSet<PortfolioFileData> PortfolioFileData { get; set; }
        public DbSet<FileAttachment> FileAttachments { get; set; }
    }
    
  10. Add a migration and update the database. See the following section for details: Use a DBMS: Setup Migrations.

  11. Run the application. Open the Resume List View and create a new Resume object. Fill the Contact field and add a new Portfolio File Data object. In the Portfolio File Data window, specify the Document Type and select the file that you wish to attach.

    XAF Add a Resume object

    Users can click the file link to download the resume file.

    To get a file stored within a PortfolioFileData object in code, use the IFileData.SaveToStream method of its File property.

Next Lesson

Create Multiple View Variants