Best Practice Using C# XSD typed-datasets In Enter

2020-06-18 02:14发布

问题:

I'm trying to understand what is the best practice using XSD tables generated from the Database scheme that I currently use.

1) Do you think the XSD information should be located as part of the Model?

2) Does it mean that Data Access Layer returns Datasets and other generated objects?

3) Does it goes through all the system layers all the way to the UI?

4) If the XSD is part of the Data Access Layer, should I convert the results to objects from the Model? What is best convert methodology?

Thanks, Ronny

回答1:

You have restricted the purpose and applications of XSDs by making XSDs specific to Datasets in your question.

XSD is acronym for Extensible Scehma Defination. XSD standards are defined by W3C for the sake of standardizing XML files that you may use in your applications.

As an example lets say, you are heavily using XML files in your application which you may exchange with different types of remote sources. These sources may send you XML files in various formats. In your application you need to be sure to receive the XML file in proper format so that you can further perform your business operations on the XML file. So you need enforce standardization on to those XML file. You will need to validate the XML file against the acceptable standards at your end. You will need to compare the schema of XML with the standards. These standards are written in XSD form. And you will validate the schema of your XML file against the schema standards as defined in the XSD file. This is the actual purpose of the XSD files.

Now answering your questions..

1.) Do you think the XSD information should be located as part of the Model?

As I just sais XSD file stores the schema not the data. Same way in any application when you use Datasets which actually hold data in memory at runtime - will also have its own schema, the form in which it will hold the data. These varies based on the underlying Datatables and their relations. So MS guys introduced the concept of TypedDataSets. TypedDataSets - as the name suggests are qualified schema of the Dataset which you are going to use at run-time to play with the data. So TypedDataSets are actually defined in form of XSD file which defines the schema of DataTables and the relations inbetween. So when you create a TypedDataSet file in Visual studio, it basically creates an XSD file, All tables that you add from the database source to TypedDataSet surface, will be analyzed and metadata schema of each table will be created in the XSD file. At runtime when you select records into your dataset, you already know what kind of data is coming into them and if the data is not in the form as defined in the XSD you will get a runtime exception.

Still XSDs are not instrumental at runtime becuase Visual studio generates tpyed-dataset codebase from the XSD file by using XSD.exe tool.

2) Does it mean that Data Access Layer returns Datasets and other generated objects?

If Your data layer is using TypedDataset, It will return DataTables or DataRow[], or DataRow as you need.

3) Does it goes through all the system layers all the way to the UI?

You can generate custom business objects on top of it which is a recommended practice rather than throwing Dataset objects here and there in your application.

4) If the XSD is part of the Data Access Layer, should I convert the results to objects from the Model? What is best convert methodology?

Write a mapping mechanism using Reflection. We map our DataRow to Business object instances and DataTables to Business object Collections.

You can start re-designing to upscale your project with more maintainable architecture. And of course this will take time and effort but eventually you'll have great results.

This is what I have in my project.

1.) Application.Infrastructure

  • Base classes for all businessobjects, busines object collection, data-access classes and my custom attributes and utilities as extension methods, Generic validation framework. This determines overall behavior organization of my final .net application.

2.) Application.DataModel

  • XSD Typed Dataset for the Database.
  • TableAdapters extended to incorporate Transactions and other features I may need.

3.) Application.DataAccess

  • Data access classes.
  • Actual place where Database actions are queried using underlying Typed Dataset.

4.) Application.DomainObjects

  • Business objects and Business object collections.
  • Enums.

5.) Application.BusinessLayer

  • Provides manager classes accessible from Presentation layer.
  • HttpHandlers.
  • My own Page base class.
  • More things go here..

6.) Application.WebClient or Application.WindowsClient

  • My presentation layer
  • Takes references from Application.BusinessLayer and Application.BusinessObjects.

Application.BusinessObjects are used across the application and they travel across all layers whenever neeeded [except Application.DataModel and Application.Infrastructure]

All my queries are defined only Application.DataModel.

Application.DataAccess returns or takes Business objects as part of any data-access operation. Business objects are created with the help of reflection attributes. Each business object is marked with an attribute mapping to target table in database and properties within the business object are marked with attributes mapping to target coloumn in respective data-base table.

My validation framework lets me validate each field with the help of designated ValidationAttribute.

My framrwork heavily uses Attributes to automate most of the tedious tasks like mapping and validation. I can also new feature as new aspect in the framework.

A sample business object would look like this in my application.

User.cs

[TableMapping("Users")]
public class User : EntityBase
{
    #region Constructor(s)
    public AppUser()
    {
        BookCollection = new BookCollection();
    }
    #endregion

    #region Properties

    #region Default Properties - Direct Field Mapping using DataFieldMappingAttribute

    private System.Int32 _UserId;

    private System.String _FirstName;
    private System.String _LastName;
    private System.String _UserName;
    private System.Boolean _IsActive;

    [DataFieldMapping("UserID")]
    [DataObjectFieldAttribute(true, true, false)]
    [NotNullOrEmpty(Message = "UserID From Users Table Is Required.")]
    public override int Id
    {
        get
        {
            return _UserId;
        }
        set
        {
            _UserId = value;
        }
    }

    [DataFieldMapping("UserName")]
    [Searchable]
    [NotNullOrEmpty(Message = "Username Is Required.")]
    public string UserName
    {
        get
        {
            return _UserName;
        }
        set
        {
            _UserName = value;
        }
    }

    [DataFieldMapping("FirstName")]
    [Searchable]
    public string FirstName
    {
        get
        {
            return _FirstName;
        }
        set
        {
            _FirstName = value;
        }
    }

    [DataFieldMapping("LastName")]
    [Searchable]
    public string LastName
    {
        get
        {
            return _LastName;
        }
        set
        {
            _LastName = value;
        }
    }

    [DataFieldMapping("IsActive")]
    public bool IsActive
    {
        get
        {
            return _IsActive;
        }
        set
        {
            _IsActive = value;
        }
    }

    #region One-To-Many Mappings
    public BookCollection Books { get; set; }

    #endregion

    #region Derived Properties
    public string FullName { get { return this.FirstName + " " + this.LastName; } }

    #endregion

    #endregion

    public override bool Validate()
    {
        bool baseValid = base.Validate();
        bool localValid = Books.Validate();
        return baseValid && localValid;
    }
}

BookCollection.cs

/// <summary>
/// The BookCollection class is designed to work with lists of instances of Book.
/// </summary>
public class BookCollection : EntityCollectionBase<Book>
{
    /// <summary>
    /// Initializes a new instance of the BookCollection class.
    /// </summary>
    public BookCollection()
    {
    }

    /// <summary>
    /// Initializes a new instance of the BookCollection class.
    /// </summary>
    public BookCollection (IList<Book> initialList)
        : base(initialList)
    {
    }
}