不扩展导航属性

时间:2014-12-28 13:29:25

标签: breeze

我只使用Entity Framework进行元数据生成,我遇到的问题是breeze查询没有扩展导航属性。在这个多对多关系示例中,Student的StudentCourses属性在微风查询结果中返回null。可能是因为我没有使用EF进行数据访问?谢谢!

public class Student
{
    public int StudentID { get; set; }
    public string Name { get; set; }

    [InverseProperty("Student")]
    public virtual ICollection<StudentCourse> StudentsCourses { get; set; }
}

public class Course
{
    public int CourseID { get; set; }
    public string Name { get; set; }

    [InverseProperty("Course")]
    public virtual ICollection<StudentCourse> StudentsCourses { get; set; }
}

public class StudentCourse
{
    //[Key, Column(Order = 0)]
    public int StudentID { get; set; }

    //[Key, Column(Order = 1)]
    public int CourseID { get; set; }

    [ForeignKey("CourseID")]
    [InverseProperty("StudentsCourses")]
    public virtual Course Course { get; set; }

    [ForeignKey("StudentID")]
    [InverseProperty("StudentsCourses")]
    public virtual Student Student { get; set; }
}

class StudentMap : EntityTypeConfiguration<Student>
{
    public StudentMap()
    {
        HasRequired(p => p.StudentsCourses).WithMany().HasForeignKey(p => p.StudentID);
    }
}

class StudentCourseMap : EntityTypeConfiguration<StudentCourse>
{
    public StudentCourseMap()
    {
        HasKey(pc => new { pc.CourseID, pc.StudentID });
        Property(pc => pc.CourseID)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
        Property(pc => pc.StudentID)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
    }
}

控制器:

[BreezeController]
public class SchoolController : ApiController
{
    private readonly SchoolRepository _repository;

    public SchoolController() : this(null) { }

    public SchoolController(SchoolRepository repository)
    {
        _repository = repository ?? new SchoolRepository();
    }

    [HttpPost]
    public SaveResult SaveChanges(JObject saveBundle)
    {
        return _repository.SaveChanges(saveBundle);
    }

    [HttpGet]
    public IQueryable<Student> Students()
    {
        return _repository.Students;
    }

    [HttpGet]
    public IQueryable<StudentCourse> StudentsCourses()
    {
        return _repository.StudentsCourses;
    }
}

SchoolRepository.Students的实现:

public IQueryable<Student> Students
{
    get
    {
        return SchoolContext.Students().AsQueryable();
    }
}

微风查询:

var query = breeze.EntityQuery.from("Students")
        .where("Name", "==", "H")
        .expand("StudentsCourses");

1 个答案:

答案 0 :(得分:0)

我认为您的第一个挑战在于将查询信息从客户端传递到服务器,以便您的特定数据访问技术可以做正确的事情。

您没有使用EF。您不清楚是否可以使用LINQ来访问所需的数据。我通过查看您的Students控制器方法来收集您未在数据层上应用LINQ表达式:

public IQueryable<Student> Students
{
    get
    {
        return SchoolContext.Students().AsQueryable();
    }
}

您正在将数据库中的每个学生拉入服务器内存,然后让Web API应用LINQ查询,以便在通过网络发送学生之前将其缩小。

如果没有很多学生,那没关系。如果你这样做那太糟糕了。

如果你试图将所有课程同时拉入服务器内存,那将会更加糟糕。

从您的评论中可以看出,您在服务器上尝试来连接学生及其课程地图之间以及这些地图和课程之间的导航属性时会遇到其他困难。显然,您的数据访问层不会为您执行此操作,或者您还没有提到其他一些障碍。

如果我对您的实际数据访问选项有更多了解,我可能会为您提供更合适的解决方案。

了解您实际使用此API的方式也很有帮助。如果你不需要LINQ的全部功能(或者不能真正使用它),也许你应该简单地创建你需要的端点。

Web API控制器

让我们假设 - 我现在在猜测 - 您只使用一个或两个过滤条件查询“学生及其课程”,而不是订购或分页。

你的例子 - “让所有名字以'H'开头的学生获得他们的课程” - 这对我来说听起来很人性化。但我会咬人。我们也支持StudentID

过滤

您可以编写一个Web API控制器方法,将这两个条件作为参数。您的存储库不必将所有内容连接在一起。它只需要知道如何应用选择标准。这是一种的可能性。

[HttpGet]
public object StudentsAndCourses(int? studentId, string? studentName)
{
    var students = _repository.GetStudents(studentId, studentName);

    // once we know the students, we can get the course maps
    var courseMaps = _repository.GetCourseMaps(students);

    // now get the courses for those maps
    var courses = _repository.GetCourses(courseMaps);

    return new { students, courseMaps, courses};
}

正如您所看到的,我只是将您需要的实体放入包中并将包送到客户端。此方法不返回IQueryable。因此,有效地忽略OData查询字符串(如果有的话)。 Web API不会尝试从OData查询字符串中组合或应用LINQ表达式。

  

为了使这个例子相对简单,我不是在谈论Breeze能够将查询表示为JSON对象

     

如果确实需要在服务器上使用丰富的查询语义但无法使用LINQ,那将是理想的选择。解析查询的Breeze JSON表示要比拆除OData URL查询字符串或插入.NET LINQ表达式树容易得多。

     

如果你不需要那种丰富感,那就太过分了......而且我会假设你没有这种情况。

Breeze Client

让我们按学生姓名查询

return EntityQuery.from('StudentsAndCourses')
    .withParameters({studentName: 'H'})
    .using(manager).execute()
    .then(success).catch(handleFail);

现在按学生ID查询

return EntityQuery.from('StudentsAndCourses')
    .withParameters({studentId: 42})
    .using(manager).execute()
    .then(success).catch(handleFail);

两者的success回调相同。它的工作是将选定的学生返回给来电者:

function success(data) {
    return data.results.students
}

CourseMapCourse实体也位于data.results包中。你不在乎。您满意的是Breeze在处理行李时发现了这些并连接了相关实体。

假设您的客户端dataservice中有一个名为getStudentInfoById的方法。您可以在ViewModel中调用它,如下所示:

vm.student;
...
dataservice.getStudentInfoById().then(function(student) {
    vm.student = student;
});

查询成功后,将填充以下(角度模型)属性

vm.student;
vm.student.StudentCourses;
vm.student.StudentCourses[0].Course;

您还可以查看使用此查询或类似查询检索到的所有课程:

manager.getEntities(['Course']);

如果你没有太多的课程,你可能会在你的应用程序启动时将它们全部提前。然后,您可以简化StudentsAndCourses控制器方法,使其仅返回Students及其StudentCourse地图。

我把它作为练习留给你。

您真的需要EF元数据生成吗?

我敢打赌你的模型比你问题中显示的模型复杂得多。

如果您的模型实际上相当小,您可以考虑在客户端上编写metadata by hand。您可能会发现它既简单又简单。