Проблема производительности в отношении EF 4.1, сначала кода, и TPH - PullRequest
1 голос
/ 25 октября 2011

Я пытаюсь создать модель данных, используя EF 4.1 и код в первую очередь. Мои классы Poco выглядят так:

public abstract class AttributeBase
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string AttributeDescription { get; set; }
    public int Length { get; set; }

    public bool AllowNull { get; set; }
    public bool DisplayInWeb { get; set; }

}

public class StringListAttribute : AttributeBase
{
    public ICollection<StringValue> Values { get; set; }        

    public StringListAttribute()
    {
        Values = new List<StringValue>();
    }
}

public class StringValue
{
    public int StringValueId { get; set; }        
    public string Value { get; set; }

}

Основная идея заключается в том, что StringListAttribute наследуется от класса AttributeBase (другие классы также наследуются от класса AttributeBase, но я не включил их в этот пост)

Класс StringListAttribute имеет коллекцию StringValues. (отношение ноль-ко-многим). В моем классе MyContextExtention я пытаюсь добиться того, чтобы всякий раз, когда мне нужен список объектов AttributeBase из базы данных, я хотел получить все объекты StringListAttribute и заполнить их коллекцию StringValue за один раз. Я не хочу ленивой загрузки. Я хочу получить как можно больше данных в минимально возможном количестве запросов.

Мой класс Context наследуется от DbContext, и я создал метод расширения для моего класса BraArkivContext

public class MyContext : DbContext 
{        
    public DbSet<AttributeBase> Attributes { get; set; }        

    public MyContext()
    {
        Configuration.LazyLoadingEnabled = false;
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<StringListAttribute>()
                         .HasMany(t=>t.Values).WithOptional().WillCascadeOnDelete();            

        base.OnModelCreating(modelBuilder);   
    }
}

public static class MyContextExtension
{
    private const string Values = "Values";

    public static List<AttributeBase> AttributesWithData1(this MyContext context)
    {
        var attributeBases = new List<AttributeBase>();

        var stringListAttributes = context.Attributes.Include(Values).OfType<StringListAttribute>().ToList();

        attributeBases.AddRange(stringListAttributes);

        return attributeBases;
    }

}

Мой тестовый проект (я использую nUnit)) ничего особенного, и он содержит следующие методы:

public class TestHelper
{
     public static StringListAttribute CreateStringListAttribute(string name, int listSize)
    {
        var listAttribute = new StringListAttribute();
        listAttribute.Name = name;

        for (int i = 0; i < listSize; i++)
        {
            var attr1 = new StringValue();
            attr1.Value = String.Format("StringValue_{0}", i);
            listAttribute.Values.Add(attr1);
        }

        return listAttribute;
    }
}

[Test]
public void AttributeBase_GetAllAttributesWithData1()
{            

         //First add some data to the database
        var numberOfObjects = 100;
        var name = "StringListAttribute_ReadMultipleStringsListsFromDb_" + Guid.NewGuid().ToString();
        var listAttributes = TestHelper.CreateStringLists(name, numberOfObjects);

        using (var context = new MyContext())
        {
            foreach (var stringListAttribute in listAttributes)
            {
                context.Attributes.Add(stringListAttribute);    
            }

            context.SaveChanges();
        }

        Stopwatch sw = new Stopwatch()

        var attributes = new List<AttributeBase>();
        using (var context = new MyContext())
        {
            sw.Start();
            attributes = context.AttributesWithData1().ToList();
            sw.Stop();
        }


        Debug.WriteLine(String.Format("Total time for {0} attributes is: {1}.", attributes.Count, sw.Elapsed));
        Assert.IsNotNull(attributes);
}

При запуске метода теста "AttributeBase_GetAllAttributesWithData1" выполняемый оператор sql выглядит следующим образом:

SELECT 
[Project1].[Id] AS [Id], 
[Project1].[C1] AS [C1], 
[Project1].[Name] AS [Name], 
[Project1].[AttributeDescription] AS [AttributeDescription], 
[Project1].[Length] AS [Length], 
[Project1].[AllowNull] AS [AllowNull], 
[Project1].[DisplayInWeb] AS [DisplayInWeb], 
[Project1].[Document_Id] AS [Document_Id], 
[Project1].[C2] AS [C2], 
[Project1].[StringValueId] AS [StringValueId], 
[Project1].[Value] AS [Value], 
[Project1].[StringListAttribute_Id] AS [StringListAttribute_Id]
FROM ( SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
[Extent1].[AttributeDescription] AS [AttributeDescription], 
[Extent1].[Length] AS [Length], 
[Extent1].[AllowNull] AS [AllowNull], 
[Extent1].[DisplayInWeb] AS [DisplayInWeb], 
[Extent1].[Document_Id] AS [Document_Id], 
'0X0X' AS [C1], 
[Extent2].[StringValueId] AS [StringValueId], 
[Extent2].[Value] AS [Value], 
[Extent2].[StringListAttribute_Id] AS [StringListAttribute_Id], 
CASE WHEN ([Extent2].[StringValueId] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS     [C2]
FROM  [dbo].[AttributeBases] AS [Extent1]
LEFT OUTER JOIN [dbo].[StringValues] AS [Extent2] ON [Extent1].[Id] = [Extent2].[StringListAttribute_Id]
WHERE [Extent1].[Discriminator] = 'StringListAttribute'
)  AS [Project1]
ORDER BY [Project1].[Id] ASC, [Project1].[C2] ASC

Когда я запускаю этот SQL-оператор в SQL Server Management Studio, он довольно быстрый, но Entity Framework очень долго обрабатывает результат и возвращает классы poco, заполненные данными.

Есть ли другой способ моделирования классов AttributeBase, StringListAttribute и StringValue и отношения между ними для уменьшения времени обработки в EF?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...