例如,如果我正在创建一个三层应用程序(数据/业务/UI),而数据层正在捕获单个或多个记录.在发送到业务层之前,我是否将数据层的所有内容转换为通用列表/集合?发送数据表可以吗?把信息发送回数据层怎么样?

如果我使用对象/列表,这些成员是数据层还是业务层?我可以使用相同的对象来往于层吗?

下面是一些伪代码:

具有邮箱/密码的对象用户

在UI层,用户输入邮箱/密码.UI层进行验证,然后我假设创建了一个新的对象用户来传递给业务层,业务层进行进一步的验证,并将相同的对象传递给数据层以插入记录.对吗?

我是.NET的新手(来自8年以上的ASP VBScript背景),正在努力熟悉"正确"的做事方式.

推荐答案

I am updating this answer because comments left by Developr seem to indicate that he would like a bit more detail.

对您的问题的简短回答是Yes您将希望使用类实例(对象)来协调UI和业务逻辑层之间的接口.BLL和DAL将按以下讨论进行通信.不应到处传递SqlDataTables或SqlDataReaders.

原因很简单:对象是类型安全的,提供IntelliSense支持,允许您在业务层进行不一定在数据库中找到的添加或更改,并允许您自由地从数据库取消应用程序的链接,以便即使在数据库更改时(当然,在一定范围内)也可以维护一致的BLL接口.简单来说就是good programming practice.美元

最重要的是,对于UI中的任何页面,您都将拥有一个或多个要显示和交互的"模型".对象是捕获模型当前状态的方式.就流程而言:UI将从业务逻辑层(BLL)请求模型(可以是单个对象或对象列表).然后,BLL创建并返回此模型-通常使用数据访问层(DAL)中的工具.如果对UI中的模型进行了更改,则UI会将修订后的对象发送回BLL,并说明如何处理它们(例如,插入、更新、删除).

.NET is great for this kind of Separation of Concerns because the Generic container classes - and in particular the List<> class - are perfect for this kind of work. They not only permit you to pass the data but they are easily integrated with sophisticated UI controls like grids, lists, etc. via the ObjectDataSource class. You can implement the full range of operations that you need to develop the UI using ObjectDataSource: "Fill" operations with parameters, CRUD operations, sorting, etc.).

由于这一点相当重要,让我快速转移注意力,演示如何定义ObjectDataSource:

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
    OldValuesParameterFormatString="original_{0}" 
    SelectMethod="GetArticles" 
    OnObjectCreating="OnObjectCreating"
    TypeName="MotivationBusinessModel.ContentPagesLogic">
    <SelectParameters>
        <asp:SessionParameter DefaultValue="News" Name="category" 
            SessionField="CurPageCategory" Type="String" />
    </SelectParameters>
</asp:ObjectDataSource>

这里,MotivationBusinessModel是BLL的名称空间,ContentPagesLogic是实现内容页逻辑的类.提取数据的方法是"GetArticles",它使用一个名为CurPageCategory的参数.在这种特殊情况下,ObjectDataSource返回一个对象列表,然后由网格使用.请注意,我需要将会话状态信息传递给BLL类,因此在代码隐藏中,我有一个方法"OnObjectCreating",可以创建对象并传入参数:

public void OnObjectCreating(object sender, ObjectDataSourceEventArgs e)
{
    e.ObjectInstance = new ContentPagesLogic(sessionObj);
}

所以,这就是它的工作原理.但这回避了一个非常大的问题-模型/业务对象从何而来?像Linq to SQL和Subsonic这样的ORM提供了代码生成器,允许您 for each 数据库表创建一个类.也就是说,这些工具表示应该在DAL中定义模型类,并将其直接映射到数据库表上.LINQ to Entities允许您以与数据库布局截然不同的方式定义对象,但相应地更加复杂(这就是为什么LINQ to SQL和LINQ to Entities之间有区别的原因).本质上,它是一种BLL解决方案.Joel和我在这个主题的不同地方都说过,实际上,业务层通常是应该定义模型的地方(尽管我实际上混合使用了BLL和DAL对象).

一旦决定这样做,如何实现从模型到数据库的映射?嗯,你在BLL中编写类来提取数据(使用DAL)并填充对象或对象列表.它是Business Logic,因为映射通常伴随着额外的逻辑来充实模型(例如定义派生字段的值).

Joel创建静态工厂类来实现模型到数据库的映射.这是一种很好的方法,因为它使用了一种众所周知的模式,并将映射正确地放置在要返回的对象的构造中.你总是知道go 哪里看 map ,整体方法简单明了.

我采取了不同的方法.在我的BLL中,我定义了Logic个类和Model个类.这些类通常是成对定义的,两个类都在同一个文件中定义,它们的名称因后缀(例如ClassModel和ClassLogic)而不同.逻辑类知道如何使用模型类——执行填充、保存("Upsert")、删除和为模型实例生成反馈等操作.

特别是,为了进行填充,我利用主DAL类(如下所示)中的方法,这些方法允许我获取任何类和任何SQL查询,并找到使用查询返回的数据(作为单个实例或列表)创建/填充类实例的方法.也就是说,逻辑类只是获取一个模型类定义,定义一个SQL查询并将它们发送给DAL.结果是一个对象或对象列表,然后我可以将其传递到UI.请注意,查询可能会从一个表或多个连接在一起的表返回字段.在映射级别,我真的不在乎——我只想填充一些对象.

以下是第一个函数.它将接受任意类,并将其自动映射到从查询中提取的所有匹配字段.匹配是通过查找其名称与类中的属性相匹配的字段来执行的.如果有额外的类字段(例如,您将使用业务逻辑填充的字段)或额外的查询字段,它们将被忽略.

    public List<T> ReturnList<T>() where T : new()
    {
        try
        {
            List<T> fdList = new List<T>();
            myCommand.CommandText = QueryString;
            SqlDataReader nwReader = myCommand.ExecuteReader();
            Type objectType = typeof (T);
            PropertyInfo[] typeFields = objectType.GetProperties();
            if (nwReader != null)
            {
                while (nwReader.Read())
                {
                    T obj = new T();
                    for (int i = 0; i < nwReader.FieldCount; i++)
                    {
                        foreach (PropertyInfo info in typeFields)
                        {
                            // Because the class may have fields that are *not* being filled, I don't use nwReader[info.Name] in this function.
                            if (info.Name == nwReader.GetName(i))
                            {
                                if (!nwReader[i].Equals(DBNull.Value)) 
                                    info.SetValue(obj, nwReader[i], null);
                                break;
                            }
                        }
                    }
                    fdList.Add(obj);
                }
                nwReader.Close();
            }
            return fdList;
        }
        catch
        {
            conn.Close();
            throw;
        }
    }

这是在我的DAL上下文中使用的,但是DAL类中唯一需要的是QueryString的持有者,一个具有开放连接和任何参数的SqlCommand对象.关键是确保ExecuteReader在调用时能够工作.因此,我的BLL对该函数的典型用法如下所示:

return qry.Command("Select AttendDate, Count(*) as ClassAttendCount From ClassAttend")
          .Where("ClassID", classID)
          .ReturnList<AttendListDateModel>();

您还可以实现对匿名类的支持,如下所示:

    public List<T> ReturnList<T>(T sample)
    {
        try
        {
            List<T> fdList = new List<T>();
            myCommand.CommandText = QueryString;
            SqlDataReader nwReader = myCommand.ExecuteReader();
            var properties = TypeDescriptor.GetProperties(sample);
            if (nwReader != null)
            {
                while (nwReader.Read())
                {
                    int objIdx = 0;
                    object[] objArray = new object[properties.Count];
                    for (int i = 0; i < nwReader.FieldCount; i++)
                    {
                        foreach (PropertyDescriptor info in properties) // FieldInfo info in typeFields)
                        {
                            if (info.Name == nwReader.GetName(i))
                            {
                                objArray[objIdx++] = nwReader[info.Name];
                                break;
                            }
                        }
                    }
                    fdList.Add((T)Activator.CreateInstance(sample.GetType(), objArray));
                }
                nwReader.Close();
            }
            return fdList;
        }
        catch
        {
            conn.Close();
            throw;
        }
    }

对此的调用如下所示:

var qList = qry.Command("Select QueryDesc, UID, StaffID From Query")
               .Where("SiteID", sessionObj.siteID)
               .ReturnList(new { QueryDesc = "", UID = 0, StaffID=0 });

现在,qList是动态定义的动态创建的类实例的通用列表.

假设BLL中有一个函数,它将下拉列表作为参数,并请求用数据填充列表.下面是如何用上面检索到的结果填充下拉列表:

foreach (var queryObj in qList)
{
    pullDownList.Add(new ListItem(queryObj.QueryDesc, queryObj.UID.ToString()));
}

简而言之,我们可以动态定义匿名业务模型类,然后通过向DAL传递一些(动态)SQL来填充它们.因此,BLL很容易根据UI中不断变化的需求进行更新.


最后一个注意事项:如果您担心定义和传递对象会浪费内存,那么就不应该这样做:如果您使用SqlDataReader来提取数据并将其放入构成列表的对象中,那么当读卡器以只读、仅向前的方式迭代时,您将只有一个内存副本(列表).当然,如果您在数据访问层使用DataAdapter和Table类(等等),那么将产生不必要的开销(这就是为什么您不应该这样做).

Asp.net相关问答推荐

Azure DevOps 构建管道正在发布旧的/缓存的构建工件

JWT 和 Google 在 Razor 页面中进行身份验证和授权

属性包含破折号的 ServiceStack 请求对象?

如何在asp.net中单击按钮的新选项卡中打开页面?

带有模型的 mvc 上传文件 - 第二个参数发布的文件为空

无法访问,内部,资源文件?

如何确定 web.config 中的编译 debug="true"

在不知道键名的情况下访问 JSON 对象的元素

Asp.net Identity 密码哈希

使用 jQuery 调用 ASP.NET PageMethod/WebMethod - 返回整个页面

在 ApiController 中添加自定义响应头

如何获取当前登录用户的角色列表

如何验证 WebClient 请求?

无法在 IIS 中启动网站 - W3SVC 正在运行

Web API 必填参数

ASP.NET 按钮重定向到另一个页面

如何告诉 RadioButtonList 不生成表格

好的 ASP.NET 类似 excel 的网格控件?

LinkBut​​ton 向 OnClick 后面的代码发送值

如何检索 X509Store 中的所有证书