Объектно-ориентированный n-уровневый дизайн. Я слишком много абстрагируюсь? Или недостаточно? - PullRequest
7 голосов
/ 08 июня 2010

Я создаю свое первое решение корпоративного уровня (по крайней мере, я пытаюсь сделать его корпоративным). Я пытаюсь следовать шаблонам проектирования наилучшей практики, но начинаю беспокоиться, что, возможно, захожу слишком далеко с абстракцией.

Я пытаюсь создать приложение asp.net webforms (в C #) как приложение n-уровня. Я создал уровень доступа к данным, используя строго типизированный набор данных XSD, который взаимодействует с серверной частью SQL. Я обращаюсь к DAL через некоторые объекты бизнес-уровня, которые я создал на основе 1: 1, для таблиц данных в наборе данных (например, класс UsersBLL для данных пользователей в наборе данных). Я выполняю проверки внутри BLL, чтобы убедиться, что данные, передаваемые в DAL, соответствуют бизнес-правилам приложения. Это все хорошо. Однако я застреваю там, где я подключаю BLL к уровню представления. Например, мой класс UsersBLL имеет дело главным образом с целыми таблицами данных, поскольку он взаимодействует с DAL. Должен ли я сейчас создать отдельный класс «Пользователь» (Singular), который отображает свойства одного пользователя, а не нескольких пользователей? Таким образом, мне не нужно выполнять поиск в таблицах данных на уровне представления, так как я мог бы использовать свойства, созданные в классе User. Или было бы лучше как-то попытаться справиться с этим внутри UsersBLL?

Извините, если это звучит немного сложно ... Ниже приведен код от UsersBLL:

using System;
using System.Data;
using PedChallenge.DAL.PedDataSetTableAdapters;

[System.ComponentModel.DataObject]
public class UsersBLL
{
    private UsersTableAdapter _UsersAdapter = null;
    protected UsersTableAdapter Adapter
    {
        get
        {
            if (_UsersAdapter == null)
                _UsersAdapter = new UsersTableAdapter();

            return _UsersAdapter;
        }
    }


    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, true)]
    public PedChallenge.DAL.PedDataSet.UsersDataTable GetUsers()
    {
        return Adapter.GetUsers();
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, false)]
    public PedChallenge.DAL.PedDataSet.UsersDataTable GetUserByUserID(int userID)
    {
        return Adapter.GetUserByUserID(userID);
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, false)]
    public PedChallenge.DAL.PedDataSet.UsersDataTable GetUsersByTeamID(int teamID)
    {
        return Adapter.GetUsersByTeamID(teamID);
    }


    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, false)]
    public PedChallenge.DAL.PedDataSet.UsersDataTable GetUsersByEmail(string Email)
    {
        return Adapter.GetUserByEmail(Email);
    }


    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Insert, true)]
    public bool AddUser(int? teamID, string FirstName, string LastName, 
        string Email, string Role, int LocationID)
    {
        // Create a new UsersRow instance
        PedChallenge.DAL.PedDataSet.UsersDataTable Users = new PedChallenge.DAL.PedDataSet.UsersDataTable();
        PedChallenge.DAL.PedDataSet.UsersRow user = Users.NewUsersRow();

        if (UserExists(Users, Email) == true)
            return false;


        if (teamID == null) user.SetTeamIDNull();
        else user.TeamID = teamID.Value;
        user.FirstName = FirstName;
        user.LastName = LastName;
        user.Email = Email;
        user.Role = Role;
        user.LocationID = LocationID;

        // Add the new user
        Users.AddUsersRow(user);
        int rowsAffected = Adapter.Update(Users);

        // Return true if precisely one row was inserted,
        // otherwise false
        return rowsAffected == 1;
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Update, true)]
    public bool UpdateUser(int userID, int? teamID, string FirstName, string LastName,
        string Email, string Role, int LocationID)
    {
        PedChallenge.DAL.PedDataSet.UsersDataTable Users = Adapter.GetUserByUserID(userID);
        if (Users.Count == 0)
            // no matching record found, return false
            return false;

        PedChallenge.DAL.PedDataSet.UsersRow user = Users[0];

        if (teamID == null) user.SetTeamIDNull();
        else user.TeamID = teamID.Value;
        user.FirstName = FirstName;
        user.LastName = LastName;
        user.Email = Email;
        user.Role = Role;
        user.LocationID = LocationID;

        // Update the product record
        int rowsAffected = Adapter.Update(user);

        // Return true if precisely one row was updated,
        // otherwise false
        return rowsAffected == 1;
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Delete, true)]
    public bool DeleteUser(int userID)
    {
        int rowsAffected = Adapter.Delete(userID);

        // Return true if precisely one row was deleted,
        // otherwise false
        return rowsAffected == 1;
    }

    private bool UserExists(PedChallenge.DAL.PedDataSet.UsersDataTable users, string email)
    {
        // Check if user email already exists
        foreach (PedChallenge.DAL.PedDataSet.UsersRow userRow in users)
        {
            if (userRow.Email == email)
                return true;
        }
        return false;
    }
}

Некоторые указания в правильном направлении будут с благодарностью !!

Спасибо всем!

Макс

Ответы [ 2 ]

4 голосов
/ 08 июня 2010

Тип слоя, который вы пытаетесь использовать, обычно включает отход от подхода DataTable к чему-то, что использует экземпляр (примерно) для каждой строки в базе данных. Другими словами, DAL будет возвращать либо одного пользователя, либо набор пользователей, в зависимости от того, какой статический метод Load вы вызываете. Это означает, что все методы, которые принимают группу параметров для представления пользователя, вместо этого принимают DTO пользователя.

DAL для пользователей будет выглядеть примерно так:

public static class UserDal
{
    public static User Load(int id) { }

    public static User Save(User user) } { }

    public static IEnumerable<User> LoadByDiv(int divId) { }
}
  1. Это статично, потому что у него нет состояния. (Возможно, это может иметь базу данных соединение как его состояние, но это не очень хорошая идея в большинстве случаев, и пул соединений удаляет любые выгода. Другие могут поспорить за шаблон синглтона.)

  2. Работает на уровне Пользователя. Класс DTO, а не DataTable или любой другой другая специфичная для базы данных абстракция. Возможно, реализация использует база данных, возможно, он использует LINQ: звонящий не должен знать ни того, ни другого. Обратите внимание, как он возвращает IEnumerable вместо того, чтобы совершать какие-либо особый вид коллекции.

  3. Это касается только доступа к данным, не бизнес-правила. Поэтому следует быть вызываемым только изнутри бизнеса логический класс, который имеет дело с пользователями. такие класс может решить, какой уровень доступа звонящему разрешено иметь, если таковой имеется.

  4. DTO обозначает Объект Передачи Данных, который обычно составляет класс, содержащий только общественная собственность. Вполне может быть грязный флаг, который автоматически устанавливается, когда свойства изменены Там может быть способ явно установить грязный флаг, но нет публичного способа очистки Это. Кроме того, идентификатор обычно доступен только для чтения (поэтому что он может быть заполнен только из десериализации).

  5. DTO намеренно не содержит бизнес логика, которая пытается обеспечить правильность; вместо, соответствующий класс бизнес-логики контекстуально обеспечивает соблюдение правил. Бизнес логика изменения, так что если DTO или DAL были обременены это, нарушение единой ответственности принцип приведет к таким бедствиям, как не возможность десериализации объекта, потому что его значения больше не считаются допустимыми.

  6. Уровень представления может создавать экземпляр пользователя объект, заполните его и спросите слой бизнес-логики пожалуйста, вызовите метод Save в DAL. Если BLL решит сделать это, он заполнит ID и очистить грязный флаг. Используя этот идентификатор, BLL может затем получить сохраненные экземпляры, вызвав Метод загрузки по идентификатору DAL.

  7. DAL всегда имеет метод Save и Load-by-ID метод, но он вполне может иметь методы загрузки на основе запросов, такой как пример LoadByDiv выше. Это должно предложить любые методы, которые BLL требует для эффективного работа.

  8. Реализация DAL является секретом, поскольку BLL и выше обеспокоены. Если поддержка базы данных, как правило, будут хранимые процедуры соответствует различным методам DAL, но это деталь реализации. Точно так же, как и любой вроде кеширование.

3 голосов
/ 08 июня 2010

Чтобы облегчить свой дизайн, вам определенно не нужно извлекать целые таблицы данных и искать их на уровне презентации. Прелесть базы данных в том, что она индексируется, чтобы упростить быстрый запрос данных на уровне строк (т.е. получить строку по индексируемому идентификатору).

Ваш DAL должен предоставлять такой метод, как GetUserByUserID (int userID). Затем вы должны предоставить этот метод через BLL, применяя любую необходимую бизнес-логику.

Кроме того, я бы не стал использовать наборы данных типа и рассмотрел бы инструмент ORM, такой как Entity Framework.

...