Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.MVC: Repository that reflects IQueryable but not Linq to SQL, DDD How To question

I want to create a DDD repository that returns IQueryable Entities that match the Linq to SQL underlying classes, minus any relations. I can easily return Entities minus the relations with a Linq select new {field, field, ... } projection. How do I code the Repository Entity class? How would I return an object from the repository having the repository class not the Linq to SQL class and still fill it with multiple entities from the Linq selection? How would I reference this returned class in my ViewModel?

I am very new to this, thus the obvious mistakes. Am I missing the boat and should only return complete Entities from the repository, not a projection? I would still need to strip out the Linq to SQL relations before I send them from the repository. Am I totally off base? I really want to keep the IQueryable data type.

For example, my Linq to SQL code in my repository:

public class MiniProduct
{
    public MiniProduct( string ProductNo, string Name, string Description, double Price)
    {    this.ProductNo = ProductNo;
         this.Name = Name;
         this.Description = Description;
         this.Price = Price;
    }
}

public IQueryable<MiniProduct> GetProductsByCategory( string productCategory)
{
    return ( from p in db.Products
             from c in db.Categories
             from pc in db.ProductCategories
             where c.CategoryName == productCategory &&
                   c.CatID == pc.CatID &&
                   pc.ProductID == p.ProductID
             select new { p.ProductNo, p.Name, p.Description, p.Price } );
    // how to return IQueryable<MiniProduct> instead of IQueryable<AnonymousType>???
}

And in the View (trying to strongly type ViewModel) what would be my model data type and how to reference from the view?

<% Page Inherits="System.Web.Mvc.ViewPage<MyStore.Models.MiniProduct>" %>

Edit:

Cottsak empowered the code and made it work, so he earns the checkbox. However, Mark Seemann pointed out that this technique will cause side effects. He was right in that projecting or sub-setting your POCO is bad. After making the code work, I ended up making a ton more one off entity objects, which causes unnecessary complications. Ultimately I changed the code to reflect Mark's suggestions.

To add to Cottsak's suggestions: My repository return value was IQueryable. The page directive model reference type was

Inherits="System.Web.Mvc.ViewPage<IQueryable<MyStore.Models.MiniProduct>>"

The Model fields were accessed by:

Model.SingleOrDefault().ProductNo
Model.SingleOrDefault().Name
...

And this led to a

foreach (MyStore.Models.MiniProduct myproduct in Model) {}

Thank you both for the answers.

like image 827
Zachary Scott Avatar asked Nov 09 '09 07:11

Zachary Scott


People also ask

Is it possible to return an IQueryable<T> from a repository?

Now, you have an IQueryable<T> returned from the repository interface. And some people think this is a bad idea (you can check my specification posts on why). But if you make the repository itself the IQueryable<T>, that's a whole different story. So why not? Basically, you I just had to somehow implement my own LInQ provider.

What is an an MVC application with LINQ to SQL?

An MVC Application With LINQ to SQL. Entity classes are created and stored in LINQ to SQL Classes files (.dbml files). The O/R Designer opens when you open a .dbml file. It is a DataContext class that contains methods and properties for connecting to a database and manipulating the data in the database.

What is IQueryable in SQL Server?

I a nutshell IQueryable<T> allow us to operate and build queries that can be sent to a query provider, e.g LINQ to Entities query provider or Linq2SQL provider which in their turn will translate this to SQL and send it to the connected database.

What is wrong with IQueryable<book> in Entity Framework?

Without a shadow of a doubt we get back an IQueryable<Book> which is an entity from the entity framework, meaning we use the Entity Framework inside our Applications or Services layer. That is a leaky abstraction and a violation of separation of concerns principle.


1 Answers

Assuming that your LINQ to SQL (L2S) classes are auto-generated and reflects your underlying database, the short answer is: Don't expose IQueryable of any of your L2S classes - it would be a Leaky Abstraction.

The slightly longer answer:

The whole point of Repositories is to hide data access behind an abstraction so that you can replace or vary your data access code independently of your Domain Model. That is not going to be possible when you base the Repository interface on types defined within a specific implementation (your L2S-based Data Access Component (DAC)) - even if you could provide a new implementation of your Repository interface, you would need to reference your L2S DAC. That wouldn't play particularly nice if you suddenly decided to switch to LINQ to Entities, or the Azure Table Storage Service.

Domain Objects should be defined in a technology-neutral way. This is best done as Plain Old C# Objects (POCO).

Furthermore, exposing IQueryable gives you the opportunity to perform projections. That may sound attractive, but is actually rather dangerous in a DDD context.

In DDD, we should design Domain Objects so that they encapsulate Domain Logic and ensure all invariants.

As an example, consider the concept of an Entity (not a LINQ to Entitities Entity, but a DDD Entity). Entities are almost always identified by a persistent ID, so one common invariant is that the ID must be defined. We could write a base Entity class like this:

public abstract class Entity
{
    private readonly int id;

    protected Entity(int id)
    {
        if(id <= 0)
        {
            throw new ArgumentOutOfRangeException();
        }
        this.id = id;
    }

    public int Id
    {
        get { return this.id; }
    }
}

Such a class nicely enforces its invariants (in this case that the ID must be a positive number). There may be a lot of other, more Domain-specific invariants implemented in particular classes, but many of these are very likely to be at odds with the concept of projection: You might be able to define a projection that omits certain properties (such as the ID), but it will crash at run-time because the default values for the types (such as 0 for int) is going to throw exceptions.

In other words, even if you need only certain properties of a Domain Object, it will only make sense to hydrate it in its entirety, because that is the only way you can satisfy its invariants.

In any case, if you frequently find that you need to select only certain parts of your Domain Objects, it may be because they violate the Single Responsibility Principle. It might be a better idea to spilt the Domain Class into two or more classes.

In conclusion, exposing IQueryable sounds like a very attractive strategy, but with the current implementation of L2S it will lead to Leaky Abstractions and Anemic Domain Models.

With the next version of the Entity Framework, we should get POCO support, so it may move us closer to that goal. However, as far as I'm concerned, the jury is still out on that account.


For a more thorough discussion of a very similar topic, see IQueryable is Tight Coupling.

like image 85
Mark Seemann Avatar answered Sep 18 '22 08:09

Mark Seemann