LINQ to SQL разделение и повторное использование структур данных - PullRequest
1 голос
/ 04 ноября 2011

Вот упрощенный SQL моих таблиц, который конвертируется в модель LINQ to SQL.

CREATE TABLE Campaign (
    Id int PRIMARY KEY,
    Name varchar NOT NULL
);
CREATE TABLE Contract (
    Id int PRIMARY KEY,
    CampaignId int NULL REFERENCES Campaign(Id)
);

Теперь у меня есть такие классы ( они находятся в другом пространстве имен, а не в классах сущностей из datamodel ).

public class CampaignInfo {
    public static CampaignModel Get(DataModel.CampaignInfo campaign) {
         return new CampaignInfo {
              Id = campaign.Id,
              Name = campaign.Name,
              Status = CampaignStatus.Get( c )
         };
    }
    public int Id {get; set;}
    public int Name {get; set;}
    public CampaignStatus { get; set;}
}

public class CampaignStatus {
    public static CampaignStatus Get(DataModel.Campaign campaign) {
         return new CampaignStatus {
              Campaign = campaign.Id, // this is just for lookup on client side
              ContractCount = campaign.Contracts.Count()
              // There is much more fields concerning status of campaign
         };
    }
    public int Campaign { get; set; }
    public int ContractCount {get; set;}
}

И чем я выполняю запрос:

dataContext.Campaigns.Select( c => CampaignInfo.Get( c ) );

Другой фрагмент кода может сделать что-то вроде этого:

dataContext.Campaigns.Where( c => c.Name == "DoIt" ).Select( c => CampaignInfo.Get( c ) );

Или я хочу получить список статусов для кампаний:

dataContext.Campaigns.Select( c => CampaignStatus.Get( c ) );

Примечание. Результаты этих вызовов преобразуются в JSON, поэтому нет необходимости отслеживать исходные сущности БД.

Как видите, есть две цели. Иметь контроль над тем, какие данные берутся из базы данных, и повторно использовать эти структуры в других местах. Однако такой подход является огромной ошибкой, поскольку классы возвращают объект и используют его в дереве выражений.

Теперь я понимаю, что это не может волшебным образом создать выражение, чтобы создать целое с помощью одного запроса. Вместо этого он учитывается для каждой кампании отдельно. В довольно сложных сценариях это ужасное замедление.

Есть ли какой-нибудь простой способ, как этого добиться? Я думаю, эти классы должны возвращать некоторые выражения, но я совершенно новичок в этой области, и я не уверен, что делать.

Ответы [ 2 ]

3 голосов
/ 04 ноября 2011

Общая проблема, если я правильно понимаю, состоит в том, что у вас есть бизнес-логика, которую вы не хотите повторять в своем коде (DRY). Вы хотите иметь возможность использовать эту логику в своих методах LINQ.

Общее решение с LINQ (через деревья выражений) состоит в том, чтобы создать функцию фильтра или преобразования, которая возвращает IQueryable, чтобы вы могли выполнить дальнейшую обработку этого запроса до его отправки в базу данных.

Вот способ сделать это:

// Reusable method that returns a query of CampaignStatus objects
public static IQueryable<CampaignStatus> 
    GetCampaignStatusses(this IQueryable<Compaign> campaigns)
{
    return
        from campaign in campaigns
        new CampaignStatus
        {
            Campaign = campaign.Id,
            ContractCount = compaign.Contracts.Count()
        };
}

С этим вы можете написать следующий код:

using (var db = new DataModel.ModelDataContext() ) 
{
    return
        from campaign in db.Campaigns.WithContractCount()
        from status in db.Campaigns.GetCampaignStatusses()
        where campaign.Id == status.Campaign
        select new
        {
            Id = campaign.Id,
            Name = campaign.Name,
            Status = status
        };
}

Наличие метода, который возвращает IQueryable, позволяет вам выполнять всевозможные дополнительные операции, но этот метод не знает об этом. Например, вы можете добавить фильтрацию:

        from campaign in db.Campaigns.WithContractCount()
        from status in db.Campaigns.GetCampaignStatusses()
        where campaign.Id == status.Campaign
        where campaign .Name == "DoIt"
        where status .ContractsCount < 10
        select new
        {
            Id = campaign.Id,
            Name = campaign.Name,
            Status = status
        };

или добавить дополнительные свойства к выводу:

        from campaign in db.Campaigns.WithContractCount()
        from status in db.Campaigns.GetCampaignStatusses()
        where campaign.Id == status.Campaign
        select new
        {
            OtherProp = campaign.OtherProp,
            Id = status.Campaign,

            Name = campaign.Name,
            Status = status
        };

Это будет преобразовано в эффективный запрос SQL. Запрос не будет получать больше записей или столбцов из базы данных, чем это строго необходимо.

0 голосов
/ 04 ноября 2011

Вы можете использовать что-то вроде:

using( var dataContext = new DataModel.ModelDataContext() )  
{  
    dataContext.Campaigns.Select( c => new {  
            Id = c.Id,  
            Name = c.Name,  
            Status = new CampaignStatus()  
            {  
                ContractCount = c.Contracts.Count()  
            }  
        })  
}

в вашем запросе.

...