我目前正在阅读Pro Asp.Net MVC框架书。在书中,作者建议使用类似于以下内容的存储库模式。

[Table(Name = "Products")]
public class Product
{
    [Column(IsPrimaryKey = true,
            IsDbGenerated = true,
            AutoSync = AutoSync.OnInsert)]
    public int ProductId { get; set; }
    [Column] public string Name { get; set; }
    [Column] public string Description { get; set; }
    [Column] public decimal Price { get; set; }
    [Column] public string Category { get; set; }
}

public interface IProductsRepository
{
    IQueryable<Product> Products { get; }
}

public class SqlProductsRepository : IProductsRepository
{
    private Table<Product> productsTable;

    public SqlProductsRepository(string connectionString)
    {
        productsTable = new DataContext(connectionString).GetTable<Product>();
    }

    public IQueryable<Product> Products
    {
        get { return productsTable; }
    }
}


然后以以下方式访问数据:

public ViewResult List(string category)
{
    var productsInCategory =  (category == null) ? productsRepository.Products : productsRepository.Products.Where(p => p.Category == category);

    return View(productsInCategory);
}


这是访问数据的有效方法吗?是要从数据库中检索整个表并在内存中对其进行过滤,还是链接的Where()方法将导致一些LINQ魔术基于lambda创建优化的查询?

最后,当通过LINQ-to-SQL连接时,C#中的Repository模式的其他哪些实现可能会提供更好的性能?

最佳答案

我可以理解Johannes'希望更严格地控​​制SQL的执行,并且通过实现我有时称为“惰性锚点”的方法,我已经能够在我的应用程序中做到这一点。

我使用定制的LazyList<T>LazyItem<T>类的组合来封装延迟初始化:


LazyList<T>包装了IQueryable集合的IList功能,但最大化了LinqToSql的Deferred Execution函数,以及
LazyItem<T>将使用LinqToSql IQueryable或用于执行其他延迟代码的通用Func<T>方法包装单个项目的惰性调用。


这是一个示例-我有这个模型对象Announcement,可能带有附件的图像或pdf文档:

public class Announcement : //..
{
    public int ID { get; set; }
    public string Title { get; set; }
    public AnnouncementCategory Category { get; set; }
    public string Body { get; set; }
    public LazyItem<Image> Image { get; set; }
    public LazyItem<PdfDoc> PdfDoc { get; set; }
}


ImagePdfDoc类继承形成的类型File,该类型包含包含二进制数据的byte[]。这个二进制数据很重,我可能不需要总是每次都需要Announcement从数据库返回它。所以我想保持对象图“锚定”而不是“填充”(如果您愿意)。

所以,如果我做这样的事情:

Console.WriteLine(anAnnouncement.Title);


..i可以知道我只是从db加载了直接Announcement对象的数据。但是,如果在以下行中,我需要这样做:

Console.WriteLine(anAnnouncement.Image.Inner.Width);


..i可以确保LazyItem<T>知道如何获取其余数据。

另一个很大的好处是,这些“惰性”类可以隐藏基础存储库的特定实现,因此我不必一定要使用LinqToSql。对于正在从中剪切示例的应用程序,我正在(使用LinqToSql),但是插入另一个数据源(甚至可能完全不使用存储库模式的完全不同的数据层)将很容易。

LINQ但不是LinqToSql

您会发现,有时您需要执行一些执行到LinqToSql提供程序的,恰好是barf的精美LINQ查询。这是因为LinqToSql通过将有效的LINQ查询逻辑转换为T-SQL代码来工作,有时并不总是可能的。

例如,我有此功能,我希望从中获得IQueryable结果:

    private IQueryable<Event> GetLatestSortedEvents()
    {
        // TODO: WARNING: HEAVY SQL QUERY! fix
        return this.GetSortedEvents().ToList()
            .Where(ModelExtensions.Event.IsUpcomingEvent())
            .AsQueryable();
    }


为什么该代码不转换为SQL并不重要,但请相信我,该IsUpcomingEvent()谓词中的条件涉及许多DateTime比较,对于LinqToSql而言,要转换为T-SQL太简单了。

通过使用.ToList()然后使用条件(.Where(..)然后使用.AsQueryable(),我实际上是在告诉LinqToSql我需要所有.GetSortedEvents()项目,即使我要过滤它们。这是我的过滤器表达式无法正确呈现给SQL的实例,因此我需要在内存中对其进行过滤。就延迟执行和延迟加载而言,这可能就是我所说的LinqToSql性能的局限性-但我的应用程序中只有少数这些WARNING: HEAVY SQL QUERY!块,我认为进一步的智能重构可以完全消除它们。

最后,如果您愿意,LinqToSql可以在大型应用程序中提供出色的数据访问提供程序。我发现,要获得所需的结果并进行抽象和隔离,我需要在此处和此处添加代码的某些事项。在希望从LinqToSql更好地控制实际SQL性能的地方,我添加了智能功能以获得所需的结果。因此,恕我直言,LinqToSql非常适合需要数据库查询优化的繁重应用,只要您了解LinqToSql的工作原理即可。我的设计最初是基于Rob的Storefront tutorial,因此,如果您需要以上有关我的言论的更多解释,您可能会发现它很有用。

而且,如果您想使用上面的那些惰性类,则可以将它们获取为herehere

09-06 23:49