0

我正在使用 ASP.net 中的依赖注入开发我的第一个数据驱动域。

如果在我的数据访问层中创建了一些域数据模型,例如:

public class Company {
   public Guid CompanyId { get; set; }
   public string Name { get; set; }
}

public class Employee {
   public Guid EmployeeId { get; set; }
   public Guid CompanyId { get; set; }
   public string Name { get; set; }
}

然后我开发了一个界面,例如:

public interface ICompanyService {
   IEnumerable<Model.Company> GetCompanies();
   IEnumerable<Model.Employee> GetEmployees();
   IEnumerable<Model.Employee> GetEmployees(Guid companyId);
}

在一个单独的模块中,我使用 Linq to Sql 实现了这个接口:

public class CompanyService : ICompanyService {

    public IEnumerable<Model.Employee> GetEmployees();
    {
        return EmployeeDb
            .OrderBy(e => e.Name)
            .Select(e => e.ToDomainEntity())
            .AsEnumerable();
    }
}

其中 ToDomainEntity() 在员工存储库类中实现为基实体类的扩展方法:

public Model.EmployeeToDomainEntity()
{
     return new  Model.Employee {
         EmployeeId = this.EmployeeId,
         CompanyId = this.CompanyId,
         Name = this.Name
     };
}

到目前为止,我或多或少地遵循了 Mark Seeman 的优秀著作“.NET 中的依赖注入”中描述的模式——并且一切都很好。

但是,我想扩展我的基本模型以包括关键参考模型,因此域 Employee 类将变为:

public class Employee {
   public Guid EmployeeId { get; set; }
   public Guid CompanyId { get; set; }
   public Company { get; set; }
   public string Name { get; set; }
}

并且 ToDomainEntity() 函数将扩展为:

public Model.Employee ToDomainEntity()
{
     return new  Model.Employee {
         EmployeeId = this.EmployeeId,
         CompanyId = this.CompanyId,
         Company = (this.Company == null) ? null : this.Company.ToDomainEntity()
         Name = this.Name
     };
}

我怀疑从领域建模的角度来看这可能是“不好的做法”,但我认为,如果我要开发一个特定的视图模型来实现相同的目的,我遇到的问题也会成立。

从本质上讲,我遇到的问题是填充数据模型的速度/效率。如果我使用上述 ToDomainEntity() 方法,Linq to Sql 会创建一个单独的 SQL 调用来检索每个员工的公司记录的数据。正如您所料,这会显着增加评估 SQL 表达式所需的时间(在我们的测试数据库上从大约 100 毫秒到 7 秒),特别是在数据树很复杂的情况下(因为单独的 SQL 调用来填充每个节点/树的子节点)。

如果我创建数据模型'内联......

public IEnumerable<Model.Employee> GetEmployees();
{
    return EmployeeDb
        .OrderBy(e => e.Name)
        .Select(e => new Model.Employee {
            EmployeeId = e.EmployeeId,
            /* Other field mappings */
            Company = new Model.Company {
               CompanyId = e.Company.CompanyId,
               /* Other field mappings */
            }
        }).AsEnumerable();
}

Linq to SQL 产生了一个很好的、紧凑的 SQL 语句,它本机使用“内部连接”方法将公司与员工关联起来。

我有两个问题:

1)从域类对象中引用关联的数据类是否被认为是“不好的做法”?

2)如果是这种情况,并且为此目的创建了一个特定的视图模型,那么在不必求助于创建内联赋值块来构建表达式树的情况下,填充模型的正确方法是什么?

任何帮助/建议将不胜感激。

4

2 回答 2

1

该问题是由于同时具有数据层实体和域层实体并且需要在两者之间进行映射而引起的。虽然你可以让它发挥作用,但这会使一切变得非常复杂,正如你已经经历的那样。由于性能原因以及其他业务逻辑和表示逻辑将需要不同的数据,您正在数据和域之间进行映射,并且很快将为这些相同的实体添加更多映射。

唯一真正的解决方案是放弃您的数据实体并创建可以直接序列化到您的后端存储(SQL 服务器)的 POCO 模型对象。

POCO 实体从一开始就在 LINQ to SQL 中得到支持,但我认为迁移到Entity Framework Code First会更好。

这样做时,您可以从存储库中公开IQueryable<T>接口(您当前称为存储库ICompanyService,但更好的名称应该是ICompanyRepository)。这使您可以执行高效的 LINQ 查询。直接通过查询提供程序查询时,您可以阻止加载完整的实体。例如:

from employee in this.repository.GetEmployees()
where employee.Company.Name.StartWith(searchString)
select new 
{
    employee.Name, 
    employee.Company.Location 
};

使用 时IQueryable<T>,LINQ to SQL 和 Entity Framework 会将其转换为非常有效的 SQL 查询,该查询仅从数据库中返回员工姓名和公司位置,并在数据库内部进行过滤(与GetEmployees()返回.NET 应用程序时在您的 .NET 应用程序中进行过滤相比IEnumerable<T>) .

于 2012-07-13T08:14:25.967 回答
0

您可以使用方法让 Linq2Sql 预加载某些实体(而不是延迟加载它们) :http DataLoadOptions.LoadWith: //msdn.microsoft.com/en-us/library/bb534268.aspx。如果您对Company实体执行此操作,那么我认为 Linq2Sql 不必访问数据库来再次获取它。

于 2012-07-12T22:03:37.303 回答