如何从with-in DataContext类中公开DataContext?

Gup*_*R4c 2 c# asp.net-mvc linq-to-sql

在DataContext中扩展类时是否可以公开DataContext?考虑一下:

public partial class SomeClass {
    public object SomeExtraProperty {
        this.DataContext.ExecuteQuery<T>("{SOME_REALLY_COMPLEX_QUERY_THAT_HAS_TO_BE_IN_RAW_SQL_BECAUSE_LINQ_GENERATES_CRAP_IN_THIS INSTANCE}");
    }
}
Run Code Online (Sandbox Code Playgroud)

我该怎么做呢?我现在有一个草率的版本,我将DataContext传递给视图模型,然后我将它传递给我在partial类中设置的方法.我想避免整个DataContext传递,只是有一个我可以引用的属性.

更新@Aaronaught

那么,我将如何编写代码呢?我知道这是一个模糊的问题,但是到目前为止我在网上看到的所有教程都觉得他们认为我知道在哪里放置代码以及如何使用它等等.

假设我有一个非常简单的应用程序结构为(在文件夹中):

  • 控制器
  • 楷模
  • 查看

存储库文件在哪里?在Models文件夹中,还是可以为它们创建"存储库"文件夹?

过去,存储库如何识别DataContext?我是否必须在存储库的每个方法中创建它的新实例(如果这样看起来效率不高......并且不会导致将对象拉出一个实例并在使用它的控制器中使用它的问题另一个例子......)?

例如,我目前有这个设置:

public class BaseController : Controller {
    protected DataContext dc = new DataContext();
}

public class XController : BaseController {
    // stuff
}
Run Code Online (Sandbox Code Playgroud)

这样我就可以为所有继承的控制器提供"全局"DataContext BaseController.我的理解是,这是有效的(我可能是错的......).

在我的Models文件夹中,我有一个"Collections"文件夹,它真正用作ViewModels:

public class BaseCollection {
    // Common properties for the Master page
}

public class XCollection : BaseCollection {
    // X View specific properties
}
Run Code Online (Sandbox Code Playgroud)

那么,把所有这些都放在哪里以及如何将存储库插件?它会是这样的(使用我的应用程序的真实对象):

public interface IJobRepository {
    public Job GetById(int JobId);
}

public class JobRepository : IJobRepository {
    public Job GetById(int JobId) {
        using (DataContext dc = new DataContext()) {
            return dc.Jobs.Single(j => (j.JobId == JobId));
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

另外,界面有什么意义?是这样的其他服务可以连接到我的应用程序?如果我不计划拥有任何此类功能怎么办?

继续,有一个抽象对象收集真实对象的所有信息会更好吗?例如,一个IJob对象将具有Job我可能想要添加的+附加属性的所有属性,例如Name?那么存储库将更改为:

public interface IJobRepository {
    public IJob GetById(int JobId);
}

public class JobRepository : IJobRepository {
    public IJob GetById(int JobId) {
        using (DataContext dc = new DataContext()) {
            return dc.Jobs.Single(j => new IJob {
                Name = dc.SP(JobId)  // of course, the project here is wrong,
                                     // but you get the point...
            });
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

我的脑袋现在很困惑.我希望从头到尾看到一个教程,即"文件 - >新建 - >做这个 - >做那个".

无论如何,@ Aaronaught,抱歉向你猛烈抨击如此巨大的问题,但你显然对此有了比我更多的知识,所以我想尽可能多地挑选你的大脑.

Aar*_*ght 5

老实说,这不是Linq to SQL的设计方案.Linq to SQL本质上是数据库的薄外观; 你的实体模型应该密切反映您的数据模型,常常你的LINQ to SQL"实体模型"根本不适合作为您的域模型使用(这是"模式",MVC).

您的控制器应该使用某种存储库或服务.应该是对象负责加载特定实体以及视图模型所需的任何其他数据.如果你没有一个库/服务,您可以直接嵌入这种逻辑到控制器,但如果你这样做了很多,然后你会用易碎的设计,很难维持到结束-最好先从从一开始就有很好的设计.

不要尝试设计你的实体类来引用DataContext.这正是Linq to SQL等ORM试图避免的那种情况.如果您的实体实际上知道DataContext它们,则它们违反了Linq to SQL提供的封装并将实现泄露给公共呼叫者.

你需要有一个负责组装视图模型类,而班要么是知道的DataContext本身,或者引用的各种其他类DataContext.通常,如上所述,所讨论的类是某种类型的域存储库,它抽象出所有数据库访问.

PS有些人会坚持认为存储库应该专门处理域对象而不是表示(视图)对象,并将后者称为服务构建器 ; 称之为你喜欢的,原则基本相同,一个包装你的数据访问类的类,负责加载一个特定类型的对象(视图模型).


比方说,你正在建设一个汽车交易网站,需要显示有关领域模型(实际的汽车/上市),以及具有单独获得一些相关的但不联信息(假设价格区间对于那个特定的模型).所以你有一个像这样的视图模型:

public class CarViewModel
{
    public Car Car { get; set; }
    public decimal LowestModelPrice { get; set; }
    public decimal HighestModelPrice { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

您的视图模型构建器可以像这样简单:

public class CarViewModelService
{
    private readonly CarRepository carRepository;
    private readonly PriceService priceService;

    public CarViewModelService(CarRepository cr, PriceService ps) { ... }

    public CarViewModel GetCarData(int carID)
    {
        var car = carRepository.GetCar(carID);
        decimal lowestPrice = priceService.GetLowestPrice(car.ModelNumber);
        decimal highestPrice = priceService.GetHighestPrice(car.ModelNumber);
        return new CarViewModel { Car = car, LowestPrice = lowestPrice,
            HighestPrice = highestPrice };
    }
}
Run Code Online (Sandbox Code Playgroud)

而已. CarRepository是包装你的信息库DataContext,并加载/保存Cars,并且PriceService基本上包了一堆在同一设立的存储过程DataContext.

它可能看起来像一个很大的努力,创造所有这些类,但一旦你进入它的摆动,它真的不是那么费时,你最终会发现它的方式更容易维护.


更新:新问题的答案

存储库文件在哪里?在Models文件夹中,还是可以为它们创建"存储库"文件夹?

如果存储库负责持久化模型类,则存储库是模型的一部分.如果他们处理视图模型(AKA,他们是"服务"或"视图模型构建器"),那么它们是您的表示逻辑的一部分; 从技术上讲,它们介于Controller和Model之间,这就是为什么在我的MVC应用程序中我通常都有一个Model名称空间(包含实际的域类)和一个ViewModel名称空间(包含表示类).

存储库如何识别DataContext?

在大多数情况下,您将要通过构造函数传递它.这允许您DataContext跨多个存储库共享同一实例,这在您需要回包含多个域对象的视图模型时变得非常重要.

此外,如果您以后决定开始使用依赖注入(DI)框架,那么它可以自动处理所有依赖项解析(通过绑定DataContextHTTP-request-scoped).通常你的控制器不应该创建DataContext实例,它们应该实际注入(再次通过构造函数)与预先存在的单个存储库,但是如果没有DI框架,这可能会有点复杂,所以如果你不这样做有一个,你的控制器实际去创建这些对象是好的(不是很好).

在我的Models文件夹中,我有一个"Collections"文件夹,它真正用作ViewModels

这是错的.您的视图模型不是您的模型.视图模型属于视图,它与您的域模型("M"或"模型"所指的)分开.如上所述,我建议实际创建一个ViewModel命名空间以避免膨胀Views命名空间.

那么,把所有这些都放在哪里以及如何将存储库插件?

请参阅上面的几个段落 - 应该注入存储库,DataContext并且应该向控制器注入存储库.如果你没有使用DI框架,你可以让你的控制器创建DataContext和存储库,但尽量不要过多地使用后者设计,你会想要稍后清理它.

另外,界面有什么意义?

首先,如果需要,您可以更改持久性模型.也许您认为Linq to SQL过于面向数据,并且您希望切换到更灵活的东西,如Entity Framework或NHibernate.也许您需要实现对Oracle,mysql或其他非Microsoft数据库的支持.或者,您可能完全打算继续使用Linq to SQL,但希望能够为您的控制器编写单元测试; 唯一的方法是将模拟/伪造的存储库注入控制器,为了工作,它们需要是抽象类型.

继续,有一个抽象对象收集真实对象的所有信息会更好吗?例如,一个IJob对象将具有Job我可能想要添加的+附加属性的所有属性,例如Name

这或多或少是我首先推荐的,尽管你已经完成了一个更难以调试的投影.最好只在单独的代码行上调用SP,然后将结果合并.

此外,您不能使用域或视图模型的接口类型.它不仅是错误的比喻(模型代表了应用程序的不变定律,除非现实世界的要求发生变化,否则它们不应该改变),但它实际上是不可能的; 接口不能是数据绑定,因为发布时没有任何实例化.

所以,是的,你在这里得到了正确的想法,除了(a)而不是IJob它应该是你的JobViewModel,(b)而不是IJobRepository它应该是a JobViewModelService,而(c)而不是直接实例化DataContext它应该接受一个通过构造函数.

请记住,所有这一切的目的是保持干净,可维护的设计.如果您有24小时的截止日期,那么您仍然可以通过将所有这些逻辑直接推入控制器来使其工作.只是不要长时间离开,否则你的控制器将(d)演变为上帝对象的可憎之物.