C#架构3层解决方案

时间:2010-11-25 22:26:32

标签: c# .net wpf winforms model-view-controller

我在C#中创建了一个win form解决方案。该解决方案有三个项目,前层(FL)业务层(BL)和数据层(DL)。

前层有各种形式。所有表单都实现了IForms接口 业务层具有所有业务逻辑。所有业务逻辑类都实现了IController接口 数据层具有与sql数据库通信的所有数据逻辑。所有数据逻辑类都继承IModel接口

我正在尝试使用MVC设计模式,但我有点挣扎 目标:当我按下表单中的按钮时,调用BL类中的方法。

如果我将BL dll添加到FL,那么我可以调用BL的方法。

BL.Class classObject = new BL.Class(this);
classObject.CallMethod();

在BL中,我希望得到调用该方法的对象的引用,所以我尝试添加FL dll的引用,但是它表示循环dll引用。

我想在BL类中有一个构造函数:

公共BL.Class(IView视图) { 的MessageBox(view.Name); }

如何构建我的解决方案来实现这一目标?请注意,解决方案很大,所以我不能将所有类合并到一个DLL中。

保重, FM

7 个答案:

答案 0 :(得分:4)

您正尝试将演示逻辑注入业务层。你的BL不应该创建消息框 - 这是表示层的可重用性。

答案 1 :(得分:2)

我知道你的编程胜利,但你可能想看看WebFormsMVP或MVVM模式以获得一些想法。

“在BL中,我希望得到调用该方法的对象的引用,所以我尝试添加FL dll的引用,但是它表示循环dll引用。”

特别是在WebFormsMVP项目中,参数在层之间根据它们在.Net框架中的方式传递,例如(object sender,mySpecialEventArgs e) - 参数“mySpecialEventArgs”中的第二个参数具有通过的所有信息来回。

答案 2 :(得分:2)

您可以在BL中为FL定义事件以进行注册。每当模型发生变化时,BL都应该触发一个事件。这是视图处理它。

您的BL创建了一个消息框。如果您的观点是WebForm怎么办? BL不应该知道视图的细节。

此外,您的BL项目不应该有FL的dll引用。你需要在BL中声明IView,而不是FL。

编辑: 这是我之前使用的结构。你的BL定义了界面。如果想要使用BL,您的视图必须实现该接口。 BL使用该界面与视图进行交互。这样,您可以构建使用相同BL的不同视图。

BL.dll

public interface IView
{
    /// <summary>Update the view</summary>
    void UpdateView(object myBusinessObject);

    /// <summary>Display message</summary>
    void ShowMessage(string msg);
}

public class BL
{
    // Model and View
    private IView _view;

    /// <summary>Constructor</summary>
    public BL (IView view)
    {
        _view = view;
    }

    public void foo()
    {
        // Do something

        // Show message
        _view.ShowMessage("Hello World");
    }
}

FL.dll

public class FL
{
    private BL _myBL;

    /// <summary>Constructor</summary>
    public FL ()
    {
        _myBL = new BL(this);
    }

    /// <summary>Handles user event</summary>
    public void handleEvent()
    {
        // Call BL to do something

        _myBL.foo();
    }

    public void UpdateView(object myBusinessObject)
    {
        // Update your view
    }

    public void ShowMessage(string msg)
    {
        // Display message to user
    }
}

答案 3 :(得分:2)

分层架构背后的基本思想是第n层应该与第n + 1层对话但是从不跨越层之间,因此第n层不能跳转并直接与第n + 2层对话。

这是一个架构原则,现在归结为你的情况。

<1>首先,您正在讨论3层体系结构,它不是3层体系结构,因为您没有在sepreate服务器中托管DAL OR BL,而是在解决方案中创建单独的层(通过创建单独的项目)

2-由于您有三个DLLS(FL,BL,DAL),所以现在您的分层必须是FL - &gt; BL ----&GT; DAL在哪里---&gt;代表与之交谈。

您将如何在应用程序中获得上述路径(FL - &gt; BL ----&gt; DAL)?

为此,您可以在BL项目中添加DAL的引用,并在FL项目中添加BL引用,这样就可以遵循上述架构原则。

如何在您的情况下实施MVC?

1-'C'代表MVC中的Controller,Controller是一个负责更新FL和你的模型的类。因此,您需要为应用程序中的各个独特功能创建一个独特的控制器(请注意,您不需要为应用程序中的每个表单创建一个控制器),因此如果您有订单模块而不是创建OrderBaseController并将此控制器作为引用您的观点(Winform)

2-在步骤1中创建的控制器可以公开模型(记住,我们在FL中有BL的引用),OrderView可以使用它。

3- View可以通过控制器更改模型(注意模型在BL中但我们在FL中有参考)但实际的数据库更改仍未提交。

您将如何执行上述第3步?

    interface IOrderController
{
    bool SaveOrder(order order);
    bool ValidateOrder(order order);
    order GetOrder();
}

public class OrderBaseController : IOrderController
{
    private OrderServiceFacade Orderhelper { get; set; }

    public OrderBaseController()
    {
        Orderhelper = new OrderServiceFacade();
    }

    public bool ValidateOrder(order objOrder)
    {
    }

    #region IOrderController Members

    public bool SaveOrder(order order)
    {
        bool success = false;
        if (ValidateOrder(order))
        {
           success = Orderhelper.SaveOrder(order);

        }

        return success;
    }

    #endregion

    #region IOrderController Members


    public order GetOrder()
    {
        throw new NotImplementedException();
    }

    #endregion
}

我们刚刚实现了一个ordercontroller。现在是时候将这个控制器附加到视图中了。

 public partial class Form1 : Form
{
    IOrderController Controller;

    public order OrderToBeSaved { get; set; }
    public Form1()
    {
        InitializeComponent();
        Controller = new OrderBaseController(); // you have the controller , 
        // controller creation can also be delegated to some other component but that's totally different issue.

        OrderToBeSaved = Controller.GetOrder(); // You got the order object here , once you get the order object you can bind its properties to the different control.



    }
}

你有完全加载的订单对象,你可以使用它的属性来绑定表单上的不同控件。

现在是保存订单的时候了

 private void btnSave_Click(object sender, EventArgs e)
    {
        Controller.SaveOrder(OrderToBeSaved);
    }

这里我们已经将保存逻辑删除到控制器,现在让我们看看如何从控制器中保存。 Befor Controller保存顺序,它必须通过链,即它必须有一些逻辑与BL通信(控制器在FL中)

由于我们在FL中引用了BL,我们可以直接与BL交谈,但是我们再经历了一个我们称之为ServiceFacade的设计概念,一个ServiceFacade将用于将任务从一个层委托给另一个层层

 class OrderServiceFacade
{
    public bool SaveOrder(order order)
    {
       return  OrderDAO.SaveOrder(order);
    }
}

最后我们需要在数据库中保存订单,因此我们需要在DAL中定义一个类,我们称之为OrderDAO。

static class OrderDAO
{
    static public  bool SaveOrder(order order)
     {
            // put logic to access database and save the data.
     }

    static DataTable GetOrder(int OrderID);
}

它不完整,但它让你了解MVC如何在分层的情节中运作。

答案 4 :(得分:1)

实际上有人建议设计这种类型的东西。大多数关于此的文档都专门针对WPF,如果你查找MVVM(MVC的另一个名字),你会发现它。

基本上,你需要一个第四个dll,它是一种框架层。这就是你需要的:

ServiceProvider类 - 实现IServiceProvider的东西。

为什么我们需要这个?因为您的FL将提供UI服务。在你的情况下一个MessageBox服务。但是,您可能会发现这非常有用,您将需要创建其他服务。喜欢浏览文件或文件夹的东西。还有更多,但我们现在就坚持这一点。

您的ServiceProvider类还应保留其知道的服务列表,并提供添加到此列表的方法。完整服务提供商可能包括以下内容。

private readonly Dictionary<Type,object> mServices = new Dictionary<Type,object>();
public void Add(Type type,object value)  { ...}
public object GetService(Type serviceType) { ... }
public void Remove(Type type)  { ... }
public T Resolve<T>()  { return (T)this.GetService(typeof(T)); } 

如果你把它变成一个单独的类,那么你可以在任何地方使用这个服务。

接下来的工作是创建服务。在您的新框架中,DLL将创建一个类似IMessageBoxService的接口,其中包含一些您可能希望调用以向用户触发消息的方法。您可以通过以下功能开始简单,稍后再添加。

void ShowError(string message);

然后,您需要在FL dll中实现此接口。此实现将对MessageBox进行实际调用。

下一步是向服务提供商注册新的服务实施。因此,在GUI打开之前的某个地方调用ServiceProvider.add(typeof(IMessageBoxService), new MyMessageBoxService());

现在,在您的业务逻辑中,每次需要弹出消息时,您都可以向服务提供商询问消息框服务,并在IMessageBoxService中调用所需的方法。像这样......

IMessageBoxService wMess = ServiceProvider.GetInstance().Resolve<IMessageBoxService>();
wMess.ShowError("My Error Message");

现在你有一个干净漂亮的MVC兼容设计,用于弹出错误消息而不会产生任何令人讨厌的循环依赖。

如果您想了解有关MVVM的更多信息以及它的全部内容,可以查看Microsoft链接。或者有一个完整的MVVM框架,有人为WPF创建了具有许多有趣功能的麻烦。如果你有时间,你可能也想看一下。

Microsoft Introduces MVVM

MVVM Framework

答案 5 :(得分:0)

如果将两个图层实现为单独的项目(导致循环依赖),则无法让两个图层彼此了解。

一种方法是使用观察者模式。在业务类中设置事件,并让表示层订阅这些事件。当业务层完成其操作时,它可以触发事件并将结果传递回视图。业务类不需要知道表示层,它只知道有订阅它的对象,并避免创建循环依赖。

更新:这是BL使用事件调用FL的一种方法。这是一种循环逻辑,但如果你已经围绕两层了解彼此的代码,那么你可以考虑这个。

class BL
{
    public void CallFromFL (int parameter)
    {
        int result = DoSomeWork (parameter);
        if (OnComplete != null)
            OnComplete (result);
    }
    public event Action <int> OnComplete;
}

class FL
{
    void Foo ()
    {
        BL bl = new BL ();
        bl.OnComplete += this.getResult;
        bl.CallFromFL (5);
    }

    void GetResult (int result) {...}
}

答案 6 :(得分:0)

为什么BL需要了解FL?真正的BL与FL无关。目前使用WindowsForms实现FL,如果稍后需要将FL更改为WPF,则BL也需要更改。那么如果一层中的变化也影响另一层,那么分层是什么? BL的想法是它独立于表示层,但可以由不同的FL使用。 BL不应做出任何假设或需要了解FL。