2

I am quite new to Entity Framework, and while it has many advantages over NHibernate, I am disappointed to discover it does not support lazy loading of properties.

Take this class:

public class Product
{ 
    public virtual Guid Id {get;set;}
    public virtual string Name {get;set;}
    public virtual string Details {get;set;}
}

My plan was to use Entity Splitting to map it to two tables:

CREATE TABLE [dbo].[Product](
[Id] [uniqueidentifier] NOT NULL PRIMARY KEY,
[Name] [nvarchar](50) NULL
) 

CREATE TABLE [dbo].[ProductDetails](
[Id] [uniqueidentifier] NOT NULL PRIMARY KEY,
[Details] [nvarchar](max) NULL
)

And here is my fluent mapping:

modelBuilder.Entity<Product>()
            .Map(m =>
            {
                m.Properties(t => new { t.Id, t.Name });
                m.ToTable("Product");
            })
            .Map(m =>
            {
                m.Properties(t => new { t.Id, t.Details});
                m.ToTable("ProductDetails");
            });

I want to be able to show a list of products without loading the details field. However, whenever I load a product it always does an INNER JOIN. I want it to only read from Product, but then read from ProductDetails when I read the Details property.

How can this be achieved?

If it is not possible, how else can I implement lazy loading of properties?

Table Splitting is not acceptable as this means the persistence mechanism is dictating the design of the domain.

Edit after CodeCaster's answer:

The domain is fixed - I do not want a solution that introduces a ProductDetails entity. This question is about persisting an existing domain model. Altering the domain does not answer the question.

Paul T Davies
  • 2,527
  • 2
  • 22
  • 39

3 Answers3

3

How can [lazy-loading scalar properties from split tables] be achieved?

If it is not possible, how else can I implement lazy loading of properties?

You can't, lazy-loading only works for navigation properties.

If you change your model like this:

public class Product
{ 
    public virtual Guid Id {get;set;}
    public virtual string Name {get;set;}

    public virtual ProductDetails Details {get;set;}
}

public class ProductDetails
{ 
    public virtual Guid Product_Id {get;set;}
    public virtual string Details {get;set;}
}

You can utilize lazy loading, where ProductDetails only will be queried when you get the Product.Details property.

[Would] this mean the persistence mechanism is dictating the design of the domain?

Entity models don't have to be domain models.

Community
  • 1
  • 1
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • Maybe I wasn't clear, but this is precisely what I was trying to avoid. You say Entity models don't have to be domain models. I'm starting to think that this means EF is not a good fit for a persistence ignorant domain model? – Paul T Davies Dec 06 '13 at 13:05
  • _"EF is not a good fit for a persistence ignorant domain model?"_ - EF is an ORM that's tied pretty much to SQL databases, that can hardly be considered persistence ignorant. :) – CodeCaster Dec 06 '13 at 13:07
  • A well designed domain is ignorant even of the O/RM, or even the existence of an O/RM. I can generally swap out NHibernate or EF in my systems, or maybe even a document database (although I haven't tried that yet). – Paul T Davies Dec 06 '13 at 13:12
  • 1
    The `Product` you show can very well be a **domain model**, but if you want to use lazy loading as you described with Entity Framework you'll have to use the structure I showed above; that is your **entity model**. Of course your domain model should not know about your entity model, or even suffer from it, but in order to let your `Product` domain model be saved in my `Product` and `ProductDetails` entity models, you'll need a data access / mapping layer in between. "Lazy loading" definitely isn't a property of or task for a domain model. – CodeCaster Dec 06 '13 at 13:15
  • Thanks for this. I have upvoted this answer because it is helpful, but the correct answer would just be 'No'. If you quickly get in there with another answer just saying that I will accept it! – Paul T Davies Dec 06 '13 at 13:21
  • I can't find an absolute answer or documentation reference that says _"No, you can't do this"_, although everything I do find on "ef split table lazy loading" does the same as I do: introduce a navigation property (i.e. a new 'subclass'), as opposed to the scalar (string) property you want to use. Feel free to leave the question open until someone else can give a more satisfying answer. :) – CodeCaster Dec 06 '13 at 13:22
  • Ok, but I really think it is a no. I've been looking a while now. – Paul T Davies Dec 06 '13 at 13:23
0

You may use the following class I use in my project:

/// <summary>
/// Wrapper class for an unlimited size string property 
/// to allow for lazy loading with Entity Framework.
/// </summary>
public class Text
{
    [MaxLength]
    public string Value { get; set; }

    public static implicit operator string(Text val)
    {
        return val.Value;
    }

    public static implicit operator Text(string val)
    {
        return new Text { Value = val };
    }

    public override string ToString()
    {
        return Value;
    }
}

Since the class overrides the implicit operator, you can treat an object of type Text just as a normal string:

Text myText = "Hello Text";

In your entity class you can then simply create a virtual (Lazy Loading) property:

public virtual Text Comment { get; set; }
Sam
  • 1,301
  • 1
  • 17
  • 26
0

You can load just a portion of an entity using an anonymous type, you just can't load the partial entity into the same entity class. (Or, well, you can "manually".)

var products =
   context
   .Products
   .Select(p => new {
      p.ID,
      p.Name
   }).AsEnumerable() // come out of EF
   .Select(anon => new Product { // manually load into product objects
      ID = anon.ID,
      Name = anon.Name
   }).ToList();

Could also just use the anonymous type, or some other type.

You will also have to load the Details property "manually" later, though.

Dave Cousineau
  • 12,154
  • 8
  • 64
  • 80