Source of truth (2026)
  • IDataSource contract: DataManagementModelsStandard/IDataSource.cs.
  • Driver metadata model: ConnectionDriversConfig under Models/Config.
  • Engine loading/orchestration: DataManagementEngineStandard/Editor/DM/DMEEditor.cs and assembly handling surfaces.
  • Migration orchestration after registration: DataManagementEngineStandard/Editor/Migration/MigrationManager.cs.

Overview

The Beep Data Management Engine supports extensibility through the IDataSource interface, allowing developers to create custom data source connectors for any type of data store or service. This guide covers the complete process of implementing a custom data source.

Key Benefits
  • Support for any data source type (databases, APIs, files, cloud services)
  • Seamless integration with existing Beep infrastructure
  • Automatic connection management and error handling
  • Consistent interface across all data sources
  • Built-in support for ETL operations and data synchronization
Important Note

When creating a new data source that doesn't exist in the Beep engine's built-in configurations, you must create a corresponding ConnectionDriversConfig to register your data source. This is covered in the next section and is essential for proper integration.

Creating Your ConnectionDriversConfig

Essential First Step

Before implementing your IDataSource, you must understand and create a ConnectionDriversConfig. This configuration tells the Beep Data Management Engine how to identify, load, and instantiate your custom data source. Without this, your data source cannot be discovered or used by the engine.

Understanding ConnectionDriversConfig

The ConnectionDriversConfig class serves as the bridge between your custom data source implementation and the Beep engine. When a user requests a data source that doesn't exist in ConnectionHelper.GetAllConnectionConfigs(), the engine requires this configuration to:

  • Identify: Your custom data source type and category
  • Load: The appropriate driver assembly and class
  • Map: Connection properties to your implementation
  • Format: Connection strings with proper placeholders
  • Display: UI metadata like icons and categories

ConnectionDriversConfig Structure

public class ConnectionDriversConfig : Entity
{
    public string GuidID { get; set; }           // Unique identifier
    public string PackageName { get; set; }      // Package/namespace name
    public string DriverClass { get; set; }      // Driver class name
    public string version { get; set; }          // Version information
    public string dllname { get; set; }          // DLL assembly name
    public string classHandler { get; set; }     // IDataSource implementation class
    public string ConnectionString { get; set; } // Connection string template
    public string iconname { get; set; }         // Icon for UI
    public DataSourceType DatasourceType { get; set; }     // Data source type enum
    public DatasourceCategory DatasourceCategory { get; set; } // Category enum
    public bool ADOType { get; set; }            // Whether it uses ADO.NET
    public bool CreateLocal { get; set; }        // Can create local databases
    public bool InMemory { get; set; }           // In-memory database
    public string extensionstoHandle { get; set; } // File extensions (for file sources)
    
    // Additional ADO.NET specific properties
    public string AdapterType { get; set; }      // DataAdapter type
    public string CommandBuilderType { get; set; } // CommandBuilder type
    public string DbConnectionType { get; set; }  // Connection type
    public string DbTransactionType { get; set; } // Transaction type
}

Creating Configuration Examples

Example 1: Custom REST API Data Source Config

public static ConnectionDriversConfig CreateCustomRestApiConfig()
{
    return new ConnectionDriversConfig
    {
        // Unique identifier - always generate a new GUID
        GuidID = Guid.NewGuid().ToString(),
        
        // Package and assembly information
        PackageName = "MyCustomDataSources",
        DriverClass = "MyCustomDataSources.CustomRestApiDataSource",
        version = "1.0.0.0",
        dllname = "MyCustomDataSources.dll",
        
        // Core handler class (your IDataSource implementation)
        classHandler = "CustomRestApiDataSource",
        
        // Connection string template with placeholders
        ConnectionString = "BaseUrl={Url};ApiKey={Password};Timeout={Port}",
        
        // UI and categorization
        iconname = "api.svg",
        DatasourceCategory = DatasourceCategory.WEBAPI,
        DatasourceType = DataSourceType.WEBAPI, // Use appropriate enum value
        
        // Behavior flags
        ADOType = false,        // Not using ADO.NET
        CreateLocal = false,    // Cannot create local instances
        InMemory = false,       // Not an in-memory database
        Favourite = false,      // Not marked as favorite by default
        IsMissing = false       // Driver is available
    };
}

Example 2: Custom File-Based Data Source Config

public static ConnectionDriversConfig CreateCustomFileConfig()
{
    return new ConnectionDriversConfig
    {
        GuidID = Guid.NewGuid().ToString(),
        PackageName = "MyCustomDataSources",
        DriverClass = "MyCustomDataSources.CustomFileDataSource",
        version = "1.0.0.0",
        dllname = "MyCustomDataSources.dll",
        classHandler = "CustomFileDataSource",
        
        // For file sources, connection string is usually the file path
        ConnectionString = "{File}",
        
        iconname = "file.svg",
        DatasourceCategory = DatasourceCategory.FILE,
        DatasourceType = DataSourceType.Text, // Or create custom enum value
        
        ADOType = false,
        CreateLocal = false,
        InMemory = false,
        Favourite = true,
        IsMissing = false,
        
        // Specify which file extensions this driver handles
        extensionstoHandle = "myformat,dat,custom"
    };
}

Example 3: Custom Database Data Source Config

public static ConnectionDriversConfig CreateCustomDatabaseConfig()
{
    return new ConnectionDriversConfig
    {
        GuidID = Guid.NewGuid().ToString(),
        PackageName = "MyCustomDatabase.Client",
        DriverClass = "MyCustomDatabase.Client",
        version = "2.1.0.0",
        dllname = "MyCustomDatabase.Client.dll",
        classHandler = "CustomDatabaseDataSource",
        
        // ADO.NET style connection string
        ConnectionString = "Server={Host};Port={Port};Database={Database};Username={UserID};Password={Password};",
        
        // ADO.NET types (if using ADO.NET pattern)
        AdapterType = "MyCustomDatabase.Client.CustomDataAdapter",
        CommandBuilderType = "MyCustomDatabase.Client.CustomCommandBuilder", 
        DbConnectionType = "MyCustomDatabase.Client.CustomConnection",
        DbTransactionType = "MyCustomDatabase.Client.CustomTransaction",
        
        iconname = "customdb.svg",
        DatasourceCategory = DatasourceCategory.RDBMS, // or NOSQL
        DatasourceType = DataSourceType.CustomDB, // Define in enum
        
        ADOType = true,         // Uses ADO.NET pattern
        CreateLocal = true,     // Can create local databases
        InMemory = false,
        Favourite = true,
        IsMissing = false
    };
}

Registration Methods

There are several ways to register your ConnectionDriversConfig with the Beep engine:

Method 1: Direct Registration at Startup

// During application initialization
public void RegisterCustomDataSources(IDMEEditor dmeEditor)
{
    // Create your custom config
    var customConfig = CreateCustomRestApiConfig();
    
    // Add to the data drivers classes collection
    dmeEditor.ConfigEditor.DataDriversClasses.Add(customConfig);
    
    // Also register the assembly class definition
    var assemblyClassDef = new AssemblyClassDefinition
    {
        className = "CustomRestApiDataSource",
        type = typeof(CustomRestApiDataSource),
        PackageName = "MyCustomDataSources"
    };
    
    dmeEditor.ConfigEditor.DataSourcesClasses.Add(assemblyClassDef);
}

Method 2: Extend ConnectionHelper

// Create a helper extension class
public static class CustomConnectionHelper
{
    public static List<ConnectionDriversConfig> GetCustomConnectionConfigs()
    {
        var configs = new List<ConnectionDriversConfig>();
        
        // Add all your custom configurations
        configs.Add(CreateCustomRestApiConfig());
        configs.Add(CreateCustomFileConfig());
        configs.Add(CreateCustomDatabaseConfig());
        
        return configs;
    }
    
    public static void RegisterCustomConfigs(IDMEEditor dmeEditor)
    {
        var customConfigs = GetCustomConnectionConfigs();
        foreach (var config in customConfigs)
        {
            // Check if not already registered
            if (!dmeEditor.ConfigEditor.DataDriversClasses.Any(d => d.GuidID == config.GuidID))
            {
                dmeEditor.ConfigEditor.DataDriversClasses.Add(config);
            }
        }
    }
}

Method 3: Plugin/Extension Pattern

// Create an extension method for IBeepService (if using DI container)
public static class BeepServiceExtensions
{
    public static void AddCustomDataSources(this IBeepService beepService)
    {
        // Register configurations
        var customConfigs = CustomConnectionHelper.GetCustomConnectionConfigs();
        beepService.DMEEditor.ConfigEditor.DataDriversClasses.AddRange(customConfigs);
        
        // Register assembly definitions
        RegisterCustomAssemblyDefinitions(beepService.DMEEditor);
    }
    
    private static void RegisterCustomAssemblyDefinitions(IDMEEditor dmeEditor)
    {
        var definitions = new[]
        {
            new AssemblyClassDefinition
            {
                className = "CustomRestApiDataSource",
                type = typeof(CustomRestApiDataSource),
                PackageName = "MyCustomDataSources"
            },
            new AssemblyClassDefinition
            {
                className = "CustomFileDataSource", 
                type = typeof(CustomFileDataSource),
                PackageName = "MyCustomDataSources"
            }
            // Add more as needed
        };
        
        foreach (var def in definitions)
        {
            if (!dmeEditor.ConfigEditor.DataSourcesClasses.Any(d => d.className == def.className))
            {
                dmeEditor.ConfigEditor.DataSourcesClasses.Add(def);
            }
        }
    }
}

How Beep Resolves Your Data Source

Understanding how Beep finds and instantiates your data source helps ensure proper registration:

Resolution Process
  1. Connection Request: User requests data source by name
  2. Find Connection Properties: Beep looks up connection in ConfigEditor.DataConnections
  3. Link to Driver: Utilfunction.LinkConnection2Drivers() finds matching ConnectionDriversConfig
  4. Find Assembly Class: Beep looks up AssemblyClassDefinition by classHandler name
  5. Load Type: assemblyHandler.GetType() loads the actual .NET type
  6. Create Instance: Reflection creates instance using constructor
  7. Configure: Sets connection properties and driver config

Driver Resolution Logic (from DMEEditor)

// This is how Beep finds your driver configuration
public IDataSource CreateNewDataSourceConnection(ConnectionProperties cn, string datasourcename)
{
    // 1. Link connection to driver config
    ConnectionDriversConfig driversConfig = Utilfunction.LinkConnection2Drivers(cn);
    
    // 2. Find assembly class definition
    AssemblyClassDefinition ase = ConfigEditor.DataSourcesClasses
        .FirstOrDefault(x => x.className.Equals(driversConfig.classHandler, 
                            StringComparison.InvariantCultureIgnoreCase));
    
    // 3. Load the actual type
    Type adc = assemblyHandler.GetType(ase.type.AssemblyQualifiedName);
    
    // 4. Create instance using reflection
    ConstructorInfo ctor = adc.GetConstructors()
        .FirstOrDefault(c => c.GetParameters().Length == 5);
    ObjectActivator<IDataSource> createdActivator = GetActivator<IDataSource>(ctor);
    IDataSource ds = createdActivator(cn.ConnectionName, Logger, this, cn.DatabaseType, ErrorObject);
    
    // 5. Configure the data source
    ds.Dataconnection.ConnectionProp = cn;
    ds.Dataconnection.DataSourceDriver = driversConfig;
    
    return ds;
}
? Configuration Checklist

Before proceeding to implement your IDataSource:

  • ? ConnectionDriversConfig created with unique GUID
  • ? PackageName matches what you'll use in connection properties
  • ? classHandler matches your class name exactly
  • ? ConnectionString template has correct placeholders
  • ? DatasourceType and DatasourceCategory are appropriate
  • ? Config will be registered before creating data source instances

Understanding the IDataSource Interface

The IDataSource interface is located in the DataManagementModels project and defines the contract that all data sources must implement.

Core Interface Properties

public interface IDataSource : IDisposable
{
    // Identity and Configuration
    string GuidID { get; set; }
    string DatasourceName { get; set; }
    string Id { get; set; }
    DataSourceType DatasourceType { get; set; }
    DatasourceCategory Category { get; set; }
    
    // Connection Management
    IDataConnection Dataconnection { get; set; }
    ConnectionState ConnectionStatus { get; set; }
    
    // Data Structure
    List<EntityStructure> Entities { get; set; }
    List<string> EntitiesNames { get; set; }
    
    // Services and Utilities
    IDMEEditor DMEEditor { get; set; }
    IDMLogger Logger { get; set; }
    IErrorsInfo ErrorObject { get; set; }
    
    // Query Configuration
    string ColumnDelimiter { get; set; }
    string ParameterDelimiter { get; set; }
    
    // Events
    event EventHandler<PassedArgs> PassEvent;
}

Essential Methods to Implement

// Connection Management
ConnectionState Openconnection();
ConnectionState Closeconnection();

// Entity Discovery and Management
List<string> GetEntitesList();
EntityStructure GetEntityStructure(string EntityName, bool refresh);
bool CheckEntityExist(string EntityName);
bool CreateEntityAs(EntityStructure entity);

// Data Operations
object GetEntity(string EntityName, List<AppFilter> filter);
Task<object> GetEntityAsync(string EntityName, List<AppFilter> Filter);
object RunQuery(string qrystr);
IErrorsInfo ExecuteSql(string sql);

// CRUD Operations
IErrorsInfo InsertEntity(string EntityName, object InsertedData);
IErrorsInfo UpdateEntity(string EntityName, object UploadDataRow);
IErrorsInfo DeleteEntity(string EntityName, object UploadDataRow);

// Transaction Management
IErrorsInfo BeginTransaction(PassedArgs args);
IErrorsInfo Commit(PassedArgs args);
IErrorsInfo EndTransaction(PassedArgs args);

Implementation Steps

Step 1: Create the Data Source Class

Create a new class that implements the IDataSource interface:

using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using TheTechIdea.Beep;
using TheTechIdea.Beep.DataBase;
using TheTechIdea.Beep.Editor;
using TheTechIdea.Beep.Logger;
using TheTechIdea.Beep.ConfigUtil;
using TheTechIdea.Beep.Addin;

namespace MyCustomDataSources
{
    public class CustomDataSource : IDataSource
    {
        #region Properties
        public string GuidID { get; set; } = Guid.NewGuid().ToString();
        public string DatasourceName { get; set; }
        public string Id { get; set; }
        public DataSourceType DatasourceType { get; set; }
        public DatasourceCategory Category { get; set; }
        public IDataConnection Dataconnection { get; set; }
        public ConnectionState ConnectionStatus { get; set; }
        public List<EntityStructure> Entities { get; set; } = new List<EntityStructure>();
        public List<string> EntitiesNames { get; set; } = new List<string>();
        public IDMEEditor DMEEditor { get; set; }
        public IDMLogger Logger { get; set; }
        public IErrorsInfo ErrorObject { get; set; }
        public string ColumnDelimiter { get; set; } = "'";
        public string ParameterDelimiter { get; set; } = ":";
        
        public event EventHandler<PassedArgs> PassEvent;
        #endregion

        #region Constructor
        public CustomDataSource(string datasourcename, IDMLogger logger, 
                               IDMEEditor dmeEditor, DataSourceType databasetype, 
                               IErrorsInfo errorObject)
        {
            DatasourceName = datasourcename;
            Logger = logger;
            DMEEditor = dmeEditor;
            DatasourceType = databasetype;
            ErrorObject = errorObject;
            Category = DatasourceCategory.NOSQL; // Set appropriate category
        }
        #endregion

        #region Connection Management
        public ConnectionState Openconnection()
        {
            try
            {
                ErrorObject.Flag = Errors.Ok;
                
                // Initialize your connection here
                // Example: Connect to your custom data source
                if (Dataconnection?.ConnectionProp?.ConnectionString != null)
                {
                    // Perform actual connection logic
                    // var connectionString = Dataconnection.ConnectionProp.ConnectionString;
                    // _customClient = new CustomClient(connectionString);
                    // _customClient.Connect();
                    
                    ConnectionStatus = ConnectionState.Open;
                    Logger?.WriteLog($"Successfully opened connection to {DatasourceName}");
                    
                    // Load entities after successful connection
                    LoadEntities();
                }
                else
                {
                    ConnectionStatus = ConnectionState.Broken;
                    ErrorObject.Flag = Errors.Failed;
                    ErrorObject.Message = "Connection string is null or empty";
                }
            }
            catch (Exception ex)
            {
                ConnectionStatus = ConnectionState.Broken;
                ErrorObject.Flag = Errors.Failed;
                ErrorObject.Ex = ex;
                ErrorObject.Message = ex.Message;
                Logger?.WriteLog($"Failed to open connection to {DatasourceName}: {ex.Message}");
            }
            
            return ConnectionStatus;
        }

        public ConnectionState Closeconnection()
        {
            try
            {
                if (ConnectionStatus == ConnectionState.Open)
                {
                    // Close your custom connection
                    // _customClient?.Disconnect();
                    
                    ConnectionStatus = ConnectionState.Closed;
                    Logger?.WriteLog($"Successfully closed connection to {DatasourceName}");
                }
            }
            catch (Exception ex)
            {
                Logger?.WriteLog($"Error closing connection to {DatasourceName}: {ex.Message}");
            }
            
            return ConnectionStatus;
        }
        #endregion

        #region Entity Discovery
        public List<string> GetEntitesList()
        {
            try
            {
                if (ConnectionStatus != ConnectionState.Open)
                    return new List<string>();
                    
                // Discover entities from your data source
                var entityNames = new List<string>();
                
                // Example: Query your data source for available entities
                // entityNames = _customClient.GetTableNames();
                
                EntitiesNames = entityNames;
                return entityNames;
            }
            catch (Exception ex)
            {
                Logger?.WriteLog($"Error getting entities list: {ex.Message}");
                return new List<string>();
            }
        }

        public EntityStructure GetEntityStructure(string EntityName, bool refresh)
        {
            try
            {
                if (refresh || !Entities.Any(e => e.EntityName.Equals(EntityName, StringComparison.OrdinalIgnoreCase)))
                {
                    // Build entity structure from your data source
                    var entity = new EntityStructure
                    {
                        EntityName = EntityName,
                        DataSourceID = DatasourceName,
                        DatabaseType = DatasourceType,
                        Fields = new List<EntityField>()
                    };
                    
                    // Example: Get column information from your data source
                    // var columns = _customClient.GetColumns(EntityName);
                    // foreach (var column in columns)
                    // {
                    //     entity.Fields.Add(new EntityField
                    //     {
                    //        FieldName = column.Name,
                    //         Fieldtype = column.DataType,
                    //         Size1 = column.Size,
                    //         AllowDBNull = column.IsNullable
                    //     });
                    // }
                    
                    // Update entities list
                    var existingEntity = Entities.FirstOrDefault(e => 
                        e.EntityName.Equals(EntityName, StringComparison.OrdinalIgnoreCase));
                    if (existingEntity != null)
                        Entities.Remove(existingEntity);
                    Entities.Add(entity);
                    
                    return entity;
                }
                
                return Entities.FirstOrDefault(e => 
                    e.EntityName.Equals(EntityName, StringComparison.OrdinalIgnoreCase));
            }
            catch (Exception ex)
            {
                Logger?.WriteLog($"Error getting entity structure for {EntityName}: {ex.Message}");
                return null;
            }
        }

        public bool CheckEntityExist(string EntityName)
        {
            try
            {
                return GetEntitesList().Any(e => 
                    e.Equals(EntityName, StringComparison.OrdinalIgnoreCase));
            }
            catch (Exception ex)
            {
                Logger?.WriteLog($"Error checking entity existence for {EntityName}: {ex.Message}");
                return false;
            }
        }
        #endregion

        #region Data Retrieval
        public object GetEntity(string EntityName, List<AppFilter> filter)
        {
            try
            {
                if (ConnectionStatus != ConnectionState.Open)
                {
                    ErrorObject.Flag = Errors.Failed;
                    ErrorObject.Message = "Connection is not open";
                    return null;
                }
                
                // Convert filters to your data source's query format
                var queryFilters = ConvertFilters(filter);
                
                // Execute query against your data source
                // var result = _customClient.Query(EntityName, queryFilters);
                
                // Convert result to DataTable or List<T>
                // return ConvertToDataTable(result);
                
                return new DataTable(); // Placeholder
            }
            catch (Exception ex)
            {
                ErrorObject.Flag = Errors.Failed;
                ErrorObject.Ex = ex;
                ErrorObject.Message = ex.Message;
                Logger?.WriteLog($"Error getting entity {EntityName}: {ex.Message}");
                return null;
            }
        }

        public async Task<object> GetEntityAsync(string EntityName, List<AppFilter> Filter)
        {
            return await Task.Run(() => GetEntity(EntityName, Filter));
        }

        public object RunQuery(string qrystr)
        {
            try
            {
                if (ConnectionStatus != ConnectionState.Open)
                {
                    ErrorObject.Flag = Errors.Failed;
                    ErrorObject.Message = "Connection is not open";
                    return null;
                }
                
                // Execute custom query
                // var result = _customClient.ExecuteQuery(qrystr);
                // return ConvertToDataTable(result);
                
                return new DataTable(); // Placeholder
            }
            catch (Exception ex)
            {
                ErrorObject.Flag = Errors.Failed;
                ErrorObject.Ex = ex;
                ErrorObject.Message = ex.Message;
                Logger?.WriteLog($"Error executing query: {ex.Message}");
                return null;
            }
        }
        #endregion

        #region CRUD Operations
        public IErrorsInfo InsertEntity(string EntityName, object InsertedData)
        {
            try
            {
                // Simple insert - in real implementation, you'd want to 
                // persist changes back to file
                ErrorObject.Flag = Errors.Ok;
                ErrorObject.Message = "Insert operation completed";
                return ErrorObject;
            }
            catch (Exception ex)
            {
                ErrorObject.Flag = Errors.Failed;
                ErrorObject.Ex = ex;
                ErrorObject.Message = ex.Message;
                return ErrorObject;
            }
        }

        public IErrorsInfo UpdateEntity(string EntityName, object UploadDataRow)
        {
            try
            {
                ErrorObject.Flag = Errors.Ok;
                ErrorObject.Message = "Update operation completed";
                return ErrorObject;
            }
            catch (Exception ex)
            {
                ErrorObject.Flag = Errors.Failed;
                ErrorObject.Ex = ex;
                ErrorObject.Message = ex.Message;
                return ErrorObject;
            }
        }

        public IErrorsInfo DeleteEntity(string EntityName, object UploadDataRow)
        {
            try
            {
                ErrorObject.Flag = Errors.Ok;
                ErrorObject.Message = "Delete operation completed";
                return ErrorObject;
            }
            catch (Exception ex)
            {
                ErrorObject.Flag = Errors.Failed;
                ErrorObject.Ex = ex;
                ErrorObject.Message = ex.Message;
                return ErrorObject;
            }
        }

        public IErrorsInfo UpdateEntities(string EntityName, object UploadData, IProgress<PassedArgs> progress)
        {
            return UpdateEntity(EntityName, UploadData);
        }
        #endregion

        #region Not Implemented Methods
        public bool CreateEntityAs(EntityStructure entity) => false;
        public List<ChildRelation> GetChildTablesList(string tablename, string SchemaName, string Filterparamters) => new List<ChildRelation>();
        public List<RelationShipKeys> GetEntityforeignkeys(string entityname, string SchemaName) => new List<RelationShipKeys>();
        public List<ETLScriptDet> GetCreateEntityScript(List<EntityStructure> entities = null) => new List<ETLScriptDet>();
        public IErrorsInfo CreateEntities(List<EntityStructure> entities) => new ErrorsInfo { Flag = Errors.Ok, Message = "Not implemented" };
        public IErrorsInfo RunScript(ETLScriptDet dDLScripts) => new ErrorsInfo { Flag = Errors.Ok, Message = "Not implemented" };
        public IErrorsInfo ExecuteSql(string sql) => new ErrorsInfo { Flag = Errors.Ok, Message = "Not implemented" };
        public IErrorsInfo BeginTransaction(PassedArgs args) => new ErrorsInfo { Flag = Errors.Ok, Message = "Not implemented" };
        public IErrorsInfo Commit(PassedArgs args) => new ErrorsInfo { Flag = Errors.Ok, Message = "Not implemented" };
        public IErrorsInfo EndTransaction(PassedArgs args) => new ErrorsInfo { Flag = Errors.Ok, Message = "Not implemented" };
        #endregion

        #region Dispose
        public void Dispose()
        {
            _data?.Clear();
            _data = null;
        }
        #endregion
    }
}

Using the Simple Data Source

Here's how to register and use the simple text file data source:

// 1. Register the data source
var assemblyClassDef = new AssemblyClassDefinition
{
    className = "SimpleTextFileDataSource",
    type = typeof(SimpleTextFileDataSource),
    PackageName = "SimpleDataSources"
};
dmeEditor.ConfigEditor.DataSourcesClasses.Add(assemblyClassDef);

// 2. Create connection properties
var connectionProps = new ConnectionProperties
{
    ConnectionName = "MyTextFile",
    ConnectionString = @"C:\Data\sample.txt", // Path to your text file
    DatabaseType = DataSourceType.Text,
    Category = DatasourceCategory.FILE,
    DriverName = "SimpleTextFile"
};
dmeEditor.ConfigEditor.DataConnections.Add(connectionProps);

// 3. Use the data source
var dataSource = dmeEditor.GetDataSource("MyTextFile");
var state = dmeEditor.OpenDataSource("MyTextFile");

if (state == ConnectionState.Open)
{
    var entities = dataSource.GetEntitesList();
    Console.WriteLine($"Found entities: {string.Join(", ", entities)}");
    
    foreach (var entityName in entities)
    {
        var data = dataSource.GetEntity(entityName, null);
        Console.WriteLine($"Retrieved data for {entityName}");
        
        if (data is DataTable dt)
        {
            Console.WriteLine($"Rows: {dt.Rows.Count}, Columns: {dt.Columns.Count}");
        }
    }
}

Sample Text File Format

Create a text file with pipe-separated values:

Name|Age|Email|Department
John Doe|30|john.doe@company.com|Engineering
Jane Smith|25|jane.smith@company.com|Marketing
Bob Johnson|35|bob.johnson@company.com|Sales
Alice Brown|28|alice.brown@company.com|Engineering