c# - 如何在DataContext类中公开DataContext?

标签 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}");
    }
}


我该怎么做呢?我现在有一个草率的版本,将DataContext传递给视图模型,然后从那里传递给我在部分类中设置的方法。我想避免整个DataContext传递出去,而只具有一个我可以引用的属性。

@Aaronaught的更新

那么,我将如何编写代码?我知道这是一个模糊的问题,但是从到目前为止我在网上看到的内容来看,所有教程都觉得他们假设我知道代码的放置位置和使用方式等。

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


控制器
楷模
观看次数


存储库文件在哪里?在Models文件夹中,还是可以为它们创建一个“ Repositories”文件夹?

过去,存储库如何知道DataContext?我是否必须在存储库的每种方法中创建它的新实例(如果这样看来效率低下……并且这不会导致从一个实例中拉出对象并将其用于正在使用的控制器中的问题)一个不同的实例...)?

例如,我目前有此设置:

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

public class XController : BaseController {
    // stuff
}


这样,我就有一个“全局” DataContext可用于从BaseController继承的所有控制器。我的理解是有效的(我可能错了...)。

在我的Models文件夹中,有一个“ Collections”文件夹,它实际上充当ViewModels:

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

public class XCollection : BaseCollection {
    // X View specific properties
}


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

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));
        };
    }
}


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

继续前进,最好有一个抽象对象来收集真实对象的所有信息吗?例如,一个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...
            });
        };
    }
}


我的头现在好困惑。我希望从头到尾看到一个教程,即“文件->新建->执行此操作->执行该操作”。

无论如何,@ Aaronaught,很抱歉对您提出了一个如此大的问题,但是您显然比我拥有更多的知识,所以我想尽我所能。

最佳答案

老实说,这不是Linq to SQL设计的那种情况。 Linq to SQL本质上是数据库上的薄薄单板。您的实体模型应该紧密地反映您的数据模型,并且通常您的Linq to SQL“实体模型”根本不适合用作域模型(在MVC中是“模型”)。

您的控制器应使用某种存储库或服务。对象应该负责加载特定实体以及视图模型所需的任何其他数据。如果您没有存储库/服务,则可以将此逻辑直接嵌入到控制器中,但是如果您这样做很多,则最终将得到难以维护的脆弱设计-最好从一开始就有好的设计。

不要尝试将您的实体类设计为引用DataContext。这正是Linq to SQL之类的ORM试图避免的情况。如果您的实体实际上知道DataContext,则它们违反了Linq向SQL提供的封装,并将实现泄漏给公共调用者。

您需要有一个负责组装视图模型的类,并且该类应了解DataContext本身,或其他引用DataContext的其他类。如上所述,通常,所讨论的类是某种类型的域存储库,可以抽象出所有数据库访问权限。

附言某些人会坚持认为,存储库应专门处理域对象,而不是表示(视图)对象,并将后者称为服务或构建器。称它为您喜欢的东西,原理本质上是相同的,一个包装您的数据访问类并负责加载一种特定类型的对象(视图模型)的类。



假设您正在建立一个汽车交易网站,并且需要显示有关域模型的信息(实际的汽车/列表)以及一些必须单独获得的相关但未链接的信息(假设价格范围为针对该特定模型)。因此,您将拥有一个这样的视图模型:

public class CarViewModel
{
    public Car Car { get; set; }
    public decimal LowestModelPrice { get; set; }
    public decimal HighestModelPrice { get; set; }
}


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

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 };
    }
}


而已。 CarRepository是用于包装您的DataContext并加载/保存Cars的存储库,而PriceService本质上是包装在同一DataContext中设置的一堆存储过程。

创建所有这些类看起来似乎很费劲,但是一旦您熟悉了它,实际上并没有那么费时,最终您会发现它的维护方式更容易。



更新:新问题的答案


存储库文件在哪里?在Models文件夹中,还是可以为它们创建一个“ Repositories”文件夹?


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


存储库如何知道DataContext?


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

另外,如果您以后决定开始使用依赖项注入(DI)框架,则它可以自动处理所有依赖项解析(通过将DataContext绑定为HTTP请求范围)。通常,您的控制器不应该创建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应该是JobViewModelService,并且(c)与其直接实例化DataContext,不如通过构造函数接受一个。

请记住,所有这些目的是保持干净,可维护的设计。如果您有24小时的最后期限要满足,那么只需将所有这些逻辑直接推入控制器中,您仍然可以使它正常工作。只是不要长时间保持这种状态,否则您的控制器将(d)演变为“上帝客体”可憎之物。

关于c# - 如何在DataContext类中公开DataContext?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4140325/

相关文章:

c# - 如何通过 Interop (C#) 向 Word 文档添加结构化内容?

asp.net-mvc - 从 MVC 区域的 _Layout.cshtml 添加数据到公共(public) _Layout.cshtml

asp.net-mvc - 在 ASP.NET MVC 3 中使用 WebGrid 显示原始 HTML

c# - Javascript 和必填字段验证器

c# - 如何使用 c# 和 asp.net 在我的页面粉丝墙上的 facebook 中发帖

c# - 系统编程语言和应用程序编程语言之间的区别

c# - ASP.NET 5.0 MVC6 的 Omnisharp sublime 文本错误

sql - 性能瓶颈 - Linq to SQL 或数据库 - 我怎么知道?

c# - LINQ:何时使用编译查询?

c# - 跨表LinqToSQL查询WP7 C#