【问题标题】:How to resolve Cross-Dependency如何解决交叉依赖
【发布时间】:2013-05-08 14:12:10
【问题描述】:

我很难在一个解决方案中解决项目之间的交叉依赖关系。 解决方案是一个包含多个项目的 asp.net 应用程序。

Web 项目使用 SLN.Core 项目中的 sessionwrapper 类的服务。 声明如下:

public sealed class SW
{
    public static tblUserRow User
    {
        get
        {
            if (HttpContext.Current == null) return null;
            return HttpContext.Current.Session["dtUser"] == null ? null : (tblUsersRow)(HttpContext.Current.Session["dtUser"] as tblUsers).Rows[0];
        }
    }

    public static void User_Load(string userId)
    {
        tblUsers users = new tblUsers();
        users.LoadByID(userId);
        if (users.Count != 0)
        {
           HttpContext.Current.Session["dtUser"] = users;
        }
    }
}

tblUserRow 是单独模型项目中用户类(强类型数据表)的模型定义的一部分。 SW 类中还有一些其他方法,但不相关。 所以在 SLN.Web 我可以访问像“SW.User.Name”这样的用户数据

模型项目由两个不同命名空间中的结构(模型类)和数据库引擎类组成。

如上所示,SW 类依赖于模型来声明 User。 到这里一切正常。

但事实是模型对象在第一次创建类时需要用户、公司等数据,以便它们可以获得默认值。 F.Ex:如果您创建新发票,最好让用户(客户)分配仓库或付款类型。

根据工作要求,数据库引擎需要公司或用户数据来进行诸如获取数据库(每个公司一个)或保存带有用户信息的日志条目之类的事情。

直到对象在每次调用数据库引擎类甚至方法时都被传递的那一天,但现在我正在重构它,我认为如果可以直接在相应的地方获取该信息会更干净且内存消耗更少来自西南。

但它们之间存在交叉依赖。并且由于 SW 成员被声明为静态的,因此它们可以独立于会话而持续存在,我无法创建接口。

有什么建议吗?

更新:之前解决的用户数据范围存在问题,所以我在这里更正了。 此外,我将添加更多代码以便更好地理解。从这里开始 VB,对不起伙计们,但这是一个很好的多样性模型。

SLN.Models 中的 tblUser 模型示例:

<DesignerCategory("Code"), System.SerializableAttribute()>
Partial Public Class tblUsers
    Inherits TypedTableBase(Of tblUsersRow)

    <DebuggerNonUserCodeAttribute()>
    Public Sub New()
        MyBase.New()
        BeginInit()
        InitClass()
        EndInit()
    End Sub

    <DebuggerNonUserCodeAttribute()>
    Private Sub InitClass()
        TableName = TABLE_NAME

        With Columns
            .Add(New DataColumn(FIELD_ID, GetType(String)))
            .Add(New DataColumn(FIELD_Name, GetType(String)))
            ...
            'Added that last columns as example
            .Add(New DataColumn(FIELD_Company, GetType(String)) With {.DefaultValue=SW.Company.ID})
            .Add(New DataColumn(FIELD_Warehouse, GetType(String)) With {.DefaultValue=SW.Company.Warehouse})
        End With

        Dim keys(1) As DataColumn
        keys(0) = Columns(0)
        PrimaryKey = keys
    End Sub
...

    <DebuggerNonUserCodeAttribute()>
    Public Sub LoadByID(Id As String)
        Rows.Clear()
        Merge(New SLN.DBEngine.Generic(SW.Company.Connection, doLog:=False).ExecuteQuery(COMMAND_LOADBY_ID, Id))
    End Sub
...
End Class
Partial Public Class tblUsersRow
    Inherits DataRow
    <DebuggerNonUserCodeAttribute()>
    Friend Sub New(ByVal builder As DataRowBuilder)
        MyBase.New(builder)
    End Sub

    <DebuggerNonUserCodeAttribute()>
    Public Property ID() As String
        Get
            Return DirectCast(MyBase.Item(0), String)
        End Get
        Set(value As String)
            MyBase.Item(0) = value
        End Set
    End Property

    <DebuggerNonUserCodeAttribute()>
    Public Property Name() As String
        Get
            Return DirectCast(MyBase.Item(1), String)
        End Get
        Set(value As String)
            MyBase.Item(1) = value
        End Set
    End Property
    ...
End Class

**模型类是这样的,而我在 EF 上获得了多个 DB 的解决方案。它们是普通的数据表。

是的,还有另一个名为 Company 的模型类也用于 SW 中,用于在会话中提供公司的数据(多个用户,多个公司登录) 您可以看到在用户构造时,可以从 SW 中检索默认值。例如,发票头等其他模型也是如此。这就是我想要的行为。到目前为止,所有获得默认值的模型,它们都会获得一个完整的对象作为 New() 的参数。一些模型只需要 +25 flds 对象中的一个字段。 他们还使用 DBEngine 来加载/查询/保存/删除数据。

在 SLN.Web 中可能会看到类似这样的内容(登录):

SW.Company_Load(ddlCompany.Text)
sDescription = New Generic(SW.Company.Connection,False).ExecuteQuery("sp_Warehouse_LoadBy_Id",SW.User.Warehouse).Rows(0)("rDescription").ToString

这是一个粗略的例子。

所以 SLN.Core.SW 需要 SLN.Models.tblUsersRow 和 SLN.DBEngine.Generic

而 SLN.Models.tblUsers 需要 SLN.DBEngine 但也想获得 SLN.Core.SW ...

SLN.DBEngine... 需要 SLN.Core.SW 所以他知道数据库指向(一些其他的东西)

而 SLN.Web 需要所有这些。 (叹气!)

清楚吗? (呃……)

【问题讨论】:

  • 如果 _currentUser 是静态的,它将在所有线程/请求之间共享,并且整个系统中只有一个当前用户。这就是你真正想做的事情吗?
  • 是的,这是该对象的测试/初始版本。已更正为专门使用会话。谢谢。

标签: c# asp.net dependencies


【解决方案1】:

很难清楚地理解你的故事。但据我所知,您的模型需要 current_user 来创建对象(好吧,我不明白为什么需要这样做的业务逻辑),而您的网络 sln 需要 current_user 的模型。

基本上,流程应该是这样的:

  • 用户类已定义
  • current_user 已启动
  • 使用 current_user,模型将启动其他对象

有很多方法可以做到这一点,但我提出了 2 个解决方案:

  1. 假设您进行依赖注入,就可以完成此实现。干净,可测试。此设计用于模型解决方案。

    public interface IUserProvider
    {
        User CurrentUser { get; }
    }
    
    public class ModelCreator
    {
        public ModelCreator(IUserProvider provider)
        {
            this.provider = provider;
        }
        IUserProvider provider;
    
        public Invoice Get(){
            User currentUser = provider.CurrentUser;
            // do other
        }
    }
    
  2. 这个实现是在没有依赖注入的情况下完成的。不干净,但可模拟且易于设计。此设计适用于模型或实体解决方案。

    public static class UserProvider
    {
        private static Func<User> currentUserDelegate = new Func<User>(NullUser);
        public static Func<User> CurrentUserDelegate
        {
            set
            {
                currentUserDelegate = value;
            }
        }
    
        private static User NullUser()
        {
            return null;
        }
        public static User CurrentUser
        {
            get
            {
                return currentUserDelegate();
            }
        }
    }
    

    用法:

    public sealed class SW
    {
        private static User _currentUser;
        public static User GetCurrentUser()
        {
            if (_currentUser == null)
            {
                tblUsers users = new tblUsers();
                users.LoadByID(userId);
                HttpContext.Current.Session["dtUser"] = users;
                _currentUser = users[0];
            }
            return _currentUser;
        }
    
        public static void User_Load(string userId)
        {
            UserProvider.CurrentUserDelegate = new Func<User>(GetCurrentUser);
        }
    }
    

【讨论】:

  • 感谢您的贡献。我很抱歉我没有更清楚。在研究您的示例时,我必须说静态类或方法不允许使用接口。 (这就是问题所在)我可能会用更高版本更正代码,以便您可以看到最新的声明。
  • 如果你能提供基于代码的一般结构比故事中描述的更好。比如你说these are the classes in Model, and these are in UI
  • 对该答案投了赞成票,因为在尝试了这种方法并发现它完全有效之后,我意识到这不适用于我的场景。但是,正如我的问题所建议的那样,当无法进行重组时,这将是一个很好的解决方案。
【解决方案2】:

我终于解决了交叉依赖,将与SW类相关的数据实体类移动到同一个CORE项目中。 还将 DB 访问引擎类移至 CORE,因此在模型项目中只有实体,它可以依赖 CORE 进行 DB 访问。 CORE 项目不依赖任何东西,因为它的所有实体都在它上面,并且还具有数据库访问引擎。

所以最后又是一个糟糕的结构设计问题。

我也想感谢 Fendy,因为在其他情况下答案很好。 @Fendy 如果您知道一种给予您信任的方法,请告诉我。 谢谢。

【讨论】:

  • 点赞就够了。如果我的回答没有具体回答您的问题,那么投票就足够了。并请评论我的答案以获得解释,以免其他用户感到困惑。
猜你喜欢
  • 2015-11-29
  • 2019-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-18
  • 1970-01-01
  • 2012-03-15
  • 2014-04-14
相关资源
最近更新 更多