我应该如何在类和应用程序层之间传递数据? [英] How should I pass data between classes and application layers?

查看:49
本文介绍了我应该如何在类和应用程序层之间传递数据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

For example, if I am creating a 3 layer application (data / business / UI) and the data layer is grabbing single or multiple records. Do I convert everything from data layer into generic list/collections before sending to the business layer? Is it ok to send data tables? What about sending info back to the data layer?

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

If I use objects/lists, are these members of the Data or Business layers? Can I use the same objects to pass to and from the layers?

这是一些伪代码:

具有电子邮件/密码的对象用户

object user with email / password

在UI层中,用户输入电子邮件/密码.UI层会进行验证,然后我假设创建一个新的对象用户以传递给业务层,该业务层会进行进一步的验证并将同一对象传递给Data层以插入记录.这是正确的吗?

in UI layer, user inputs email / password. UI layer does validation and then I assume creates a new object user to pass to business layer which does further validation and passes same object to Data layer to insert record. Is this correct?

我是.NET的新手(来自8年以上的ASP VBScript背景),并试图加快正确"的工作方式的速度.

I am new to .NET (come from 8+ years of ASP VBScript background) and trying to get up to speed on the 'right' way to do things.

推荐答案

我正在更新此答案,因为Developr留下的评论似乎表明他想要更多细节.

该问题的简短答案是.您将要使用类实例(对象)来介导UI与业务逻辑层之间的接口.BLL和DAL将进行通信,如下所述.您不应该传递SqlDataTables或SqlDataReaders.

The short answer to your question is Yes you'll want to use class instances (objects) to mediate the interface between your UI and your Business Logic Layer. The BLL and DAL will communicate as discussed below. You should not be passing SqlDataTables or SqlDataReaders around.

以下原因的简单原因:对象是类型安全的,提供Intellisense支持,允许您在业务层进行不必要在数据库中找到的添加或更改,并为您提供取消链接应用程序的自由从数据库中访问,以便即使数据库发生更改(当然,在限制范围内),您也可以维护一致的BLL接口.这只是良好的编程习惯.

The simple reasons as to why: objects are type-safe, offer Intellisense support, permit you to make additions or alterations at the Business Layer that aren't necessarily found in the database, and give you some freedom to unlink the application from the database so that you can maintain a consistent BLL interface even as the database changes (within limits, of course). It is simply good programming practice.

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

The big picture is that, for any page in your UI, you'll have one or more "models" that you want to display and interact with. Objects are the way to capture the current state of a model. In terms of process: the UI will request a model (which may be a single object or a list of objects) from the Business Logic Layer (BLL). The BLL then creates and returns this model - usually using the tools from the Data Access Layer (DAL). If changes are made to the model in the UI, then the UI will send the revised object(s) back to the BLL with instructions as to what to do with them (e.g. insert, update, delete).

.NET对于这种类型的关注分离非常有用,因为通用容器类(尤其是List<>类)非常适合此类工作.它们不仅允许您传递数据,而且还可以通过ObjectDataSource类轻松地与复杂的UI控件(例如网格,列表等)集成.您可以使用ObjectDataSource来实现开发UI所需的全部操作:带有参数的"Fill"操作,CRUD操作,排序等.

.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:

Because this is fairly important, let me make a quick diversion to demonstrate how to define an 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 ",该方法使我可以创建对象并传递参数:

Here, MotivationBusinessModel is the namespace for the BLL and ContentPagesLogic is the class implementing the logic for, well, Content Pages. The method for pulling data is "GetArticles" and it takes a Parameter called CurPageCategory. In this particular case, the ObjectDataSource returns a list of objects that is then used by a grid. Note that I need to pass session state information to the BLL class so, in the code behind, I have a method "OnObjectCreating" that lets me create the object and pass in parameters:

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

所以,这就是它的工作方式.但这引出了一个非常大的问题-模型/业务对象从何而来?Linq to SQL和Subsonic之类的ORM提供了代码生成器,可让您为每个数据库表创建一个类.也就是说,这些工具说应该在DAL中定义模型类,然后直接将其映射到数据库表中.Linq to Entities可让您以与数据库布局完全不同的方式定义对象,但相应地更为复杂(这就是为什么Linq to SQL和Linq to Entities之间存在区别的原因).从本质上讲,它是一种BLL解决方案.我和Joel在此线程的各个地方都说过,实际上,通常应该在业务层定义模型(尽管实际上我混合使用了BLL和DAL对象).

So, this is how it works. But that begs one very big question - where do the Models / Business Objects come from? ORMs like Linq to SQL and Subsonic offer code generators that let you create a class for each of your database tables. That is, these tools say that the model classes should be defined in your DAL and the map directly onto database tables. Linq to Entities lets you define your objects in a manner quite distinct from the layout of your database but is correspondingly more complex (that is why there is a distinction between Linq to SQL and Linq to Entities). In essence, it is a BLL solution. Joel and I have said in various places on this thread that, really, the Business Layer is generally where the Models should be defined (although I use a mix of BLL and DAL objects in reality).

一旦决定这样做,如何实现从模型到数据库的映射?好吧,您可以在BLL中编写类以提取数据(使用DAL)并填充对象或对象列表.之所以称为 Business Logic (业务逻辑),是因为映射通常伴随着其他逻辑,以充实模型(例如,定义派生字段的值).

Once you decide to do this, how do you implement the mapping from models to the database? Well, you write classes in the BLL to pull the data (using your DAL) and fill the object or list of objects. It is Business Logic because the mapping is often accompanied by additional logic to flesh out the Model (e.g. defining the value of derived fields).

Joel创建静态Factory类以实现模型到数据库的映射.这是一个很好的方法,因为它使用了众所周知的模式并将映射权放置在要返回的对象的构造中.您总是知道去哪里可以看到映射,并且整个方法非常简单明了.

Joel creates static Factory classes to implement the model-to-database mapping. This is a good approach as it uses a well-known pattern and places the mapping right in the construction of the object(s) to be returned. You always know where to go to see the mapping and the overall approach is simple and straightforward.

我采取了另一种方法.在整个BLL中,我定义了 Logic 类和 Model 类.这些通常是在匹配对中定义的,其中两个类都在同一个文件中定义,并且它们的名称因其后缀而不同(例如,ClassModel和ClassLogic).逻辑类知道如何与模型类一起使用-完成诸如Fill,Save("Upsert"),Delete之类的操作,并为模型实例生成反馈.

I've taken a different approach. Throughout my BLL, I define Logic classes and Model classes. These are generally defined in matching pairs where both classes are defined in the same file and whose names differ by their suffix (e.g. ClassModel and ClassLogic). The Logic classes know how to work with the Model classes - doing things like Fill, Save ("Upsert"), Delete, and generate feedback for a Model Instance.

特别是要执行Fill,我利用在主DAL类(如下所示)中找到的方法,使我可以采用任何类和任何SQL查询,并找到一种使用返回的数据创建/填充该类实例的方法.通过查询(作为单个实例还是作为列表).也就是说,Logic类仅获取Model类定义,定义SQL查询并将其发送到DAL.结果是单个对象或对象列表,然后我可以将其传递给UI.请注意查询可能返回一个表或联接在一起的多个表中的字段.在映射级别,我真的不在乎-我只想填充一些对象.

In particular, to do the Fill, I leverage methods found in my primary DAL class (shown below) that let me take any class and any SQL query and find a way to create/fill instances of the class using the data returned by the query (either as a single instance or as a list). That is, the Logic class just grabs a Model class definition, defines a SQL Query and sends them to the DAL. The result is a single object or list of objects that I can then pass on to the UI. Note that the query may return fields from one table or multiple tables joined together. At the mapping level, I really don't care - I just want some objects filled.

这是第一个功能.它将采用任意类并将其自动映射到从查询中提取的所有匹配字段.通过查找名称与类中的属性匹配的字段来执行匹配.如果有多余的类字段(例如,您将使用业务逻辑填充的字段)或多余的查询字段,则将其忽略.

Here is the first function. It will take an arbitrary class and map it automatically to all matching fields extracted from a query. The matching is performed by finding fields whose name matches a property in the class. If there are extra class fields (e.g. ones that you'll fill using business logic) or extra query fields, they are ignored.

    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的持有者,带有打开的Connection和任何参数的SqlCommand对象.关键只是确保在调用ExecuteReader时可以正常工作.因此,我的BLL通常使用此功能,如下所示:

This is used in the context of my DAL but the only thing that you have to have in the DAL class is a holder for the QueryString, a SqlCommand object with an open Connection and any parameters. The key is just to make sure the ExecuteReader will work when this is called. A typical use of this function by my BLL thus looks like:

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

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

You can also implement support for anonymous classes like so:

    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是动态定义的动态创建的类实例的常规列表.

Now qList is a generic list of dynamically-created class instances defined on the fly.

比方说,您的BLL中有一个函数,该函数以下拉列表作为参数,并请求以数据填充列表.这是您可以使用上面检索到的结果填充下拉列表的方法:

Let's say you have a function in your BLL that takes a pull-down list as an argument and a request to fill the list with data. Here is how you could fill the pull down with the results retrieved above:

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

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

In short, we can define anonymous Business Model classes on the fly and then fill them just by passing some (on the fly) SQL to the DAL. Thus, the BLL is very easy to update in response to evolving needs in the UI.

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

One last note: If you are concerned that defining and passing around objects wastes memory, you shouldn't be: if you use a SqlDataReader to pull the data and place it into the objects that make up your list, you'll only have one in-memory copy (the list) as the reader iterates through in a read-only, forward-only fashion. Of course, if you use DataAdapter and Table classes (etc.) at your data access layer then you would be incurring needless overhead (which is why you shouldn't do it).

这篇关于我应该如何在类和应用程序层之间传递数据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆