亚音速一对多关系子对象

时间:2009-12-27 18:09:14

标签: c# subsonic

我不知道我做错了什么“我想我是”或者我已经达到亚音速限制。 我有3个表条,ArticleCategories和ArticleComments与其他表之间的一对多关系。 我创建了以下类

  public partial class Article
{
    private string _CategoryName;
    public string CategoryName
    {
        get { return _CategoryName; }
        set
        {
            _CategoryName = value;
        }

    }
    public ArticleCategory Category { get;set;}
    private List<System.Linq.IQueryable<ArticleComment>> _Comments;
    public List<System.Linq.IQueryable<ArticleComment>> Comments
    {
        get
        {
            if (_Comments == null)
                _Comments = new List<IQueryable<ArticleComment>>();
            return _Comments;
        }
        set
        {
            _Comments = value;
        }
    }

}

我使用此代码段获得了一系列文章

var list = new IMBDB().Select.From<Article>()
             .InnerJoin<ArticleCategory>(ArticlesTable.CategoryIDColumn, ArticleCategoriesTable.IDColumn)
             .InnerJoin<ArticleComment>(ArticlesTable.ArticleIDColumn,ArticleCommentsTable.ArticleIDColumn)
             .Where(ArticleCategoriesTable.DescriptionColumn).IsEqualTo(category).ExecuteTypedList<Article>();

            list.ForEach(x=>x.CategoryName=category);

            list.ForEach(y => y.Comments.AddRange(list.Select(z => z.ArticleComments)));

我收集了Ok,但是当我尝试使用

的评论集时
  foreach (IMB.Data.Article item in Model)

{

       %>
     
  <%
      foreach (IMB.Data.ArticleComment comment in item.Comments)
      {

          %>
         ***<%=comment.Comment %>*** 
      <%}
   } %>

comment.Comment行抛出此异常 无法将类型为'SubSonic.Linq.Structure.Query`1 [IMB.Data.ArticleComment]'的对象强制转换为'IMB.Data.ArticleComment'。

我基本上是在每次需要评论时都试图避免访问数据库。 是否有另一个实现这一目标? 感谢

2 个答案:

答案 0 :(得分:2)

好的 - 我并没有完全按照你的意思行事,但希望这个例子可以为这种情况带来一些清晰度。这更像是我如何使用SubSonic进行连接,如果我绝对必须这样做的话。我考虑这种方法的唯一方法是,如果我受到对象和/或数据库模式的某些第三方实现的限制......

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using SubSonic.Repository;

namespace SubsonicOneToManyRelationshipChildObjects
{
    public static class Program
    {
        private static readonly SimpleRepository Repository;

        static Program()
        {
            try
            {
                Repository = new SimpleRepository("SubsonicOneToManyRelationshipChildObjects.Properties.Settings.StackOverflow", SimpleRepositoryOptions.RunMigrations);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                Console.ReadLine();
            }
        }

        public class Article
        {

            public int Id { get; set; }
            public string Name { get; set; }

            private ArticleCategory category;
            public ArticleCategory Category
            {
                get { return category ?? (category = Repository.Single<ArticleCategory>(single => single.Id == ArticleCategoryId)); }
            }

            public int ArticleCategoryId { get; set; }

            private List<ArticleComment> comments;
            public List<ArticleComment> Comments
            {
                get { return comments ?? (comments = Repository.Find<ArticleComment>(comment => comment.ArticleId == Id).ToList()); }
            }
        }

        public class ArticleCategory
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }

        public class ArticleComment
        {
            public int Id { get; set; }
            public string Name { get; set; }

            public string Body { get; set; }

            public int ArticleId { get; set; }
        }

        public static void Main(string[] args)
        {
            try
            {
                // generate database schema
                Repository.Single<ArticleCategory>(entity => entity.Name == "Schema Update");
                Repository.Single<ArticleComment>(entity => entity.Name == "Schema Update");
                Repository.Single<Article>(entity => entity.Name == "Schema Update");

                var category1 = new ArticleCategory { Name = "ArticleCategory 1"};
                var category2 = new ArticleCategory { Name = "ArticleCategory 2"};
                var category3 = new ArticleCategory { Name = "ArticleCategory 3"};

                // clear/populate the database
                Repository.DeleteMany((ArticleCategory entity) => true);
                var cat1Id = Convert.ToInt32(Repository.Add(category1));
                var cat2Id = Convert.ToInt32(Repository.Add(category2));
                var cat3Id = Convert.ToInt32(Repository.Add(category3));

                Repository.DeleteMany((Article entity) => true);
                var article1 = new Article { Name = "Article 1", ArticleCategoryId = cat1Id };
                var article2 = new Article { Name = "Article 2", ArticleCategoryId = cat2Id };
                var article3 = new Article { Name = "Article 3", ArticleCategoryId = cat3Id };

                var art1Id = Convert.ToInt32(Repository.Add(article1));
                var art2Id = Convert.ToInt32(Repository.Add(article2));
                var art3Id = Convert.ToInt32(Repository.Add(article3));

                Repository.DeleteMany((ArticleComment entity) => true);
                var comment1 = new ArticleComment { Body = "This is comment 1", Name = "Comment1", ArticleId = art1Id };
                var comment2 = new ArticleComment { Body = "This is comment 2", Name = "Comment2", ArticleId = art1Id };
                var comment3 = new ArticleComment { Body = "This is comment 3", Name = "Comment3", ArticleId = art1Id };
                var comment4 = new ArticleComment { Body = "This is comment 4", Name = "Comment4", ArticleId = art2Id };
                var comment5 = new ArticleComment { Body = "This is comment 5", Name = "Comment5", ArticleId = art2Id };
                var comment6 = new ArticleComment { Body = "This is comment 6", Name = "Comment6", ArticleId = art2Id };
                var comment7 = new ArticleComment { Body = "This is comment 7", Name = "Comment7", ArticleId = art3Id };
                var comment8 = new ArticleComment { Body = "This is comment 8", Name = "Comment8", ArticleId = art3Id };
                var comment9 = new ArticleComment { Body = "This is comment 9", Name = "Comment9", ArticleId = art3Id };
                Repository.Add(comment1);
                Repository.Add(comment2);
                Repository.Add(comment3);
                Repository.Add(comment4);
                Repository.Add(comment5);
                Repository.Add(comment6);
                Repository.Add(comment7);
                Repository.Add(comment8);
                Repository.Add(comment9);

                // verify the database generation
                Debug.Assert(Repository.All<Article>().Count() == 3);
                Debug.Assert(Repository.All<ArticleCategory>().Count() == 3);
                Debug.Assert(Repository.All<ArticleComment>().Count() == 9);

                // fetch a master list of articles from the database
                var articles = 
                    (from article in Repository.All<Article>() 
                    join category in Repository.All<ArticleCategory>() 
                        on article.ArticleCategoryId equals category.Id 
                    join comment in Repository.All<ArticleComment>() 
                        on article.Id equals comment.ArticleId 
                    select article)
                    .Distinct()
                    .ToList();

                foreach (var article in articles)
                {
                    Console.WriteLine(article.Name  + " ID " + article.Id);
                    Console.WriteLine("\t" + article.Category.Name + " ID " + article.Category.Id);

                    foreach (var articleComment in article.Comments)
                    {
                        Console.WriteLine("\t\t" + articleComment.Name + " ID " + articleComment.Id);
                        Console.WriteLine("\t\t\t" + articleComment.Body);
                    }
                }

                // OUTPUT (ID will vary as autoincrement SQL index

                //Article 1 ID 28
                //        ArticleCategory 1 ID 41
                //                Comment1 ID 100
                //                        This is comment 1
                //                Comment2 ID 101
                //                        This is comment 2
                //                Comment3 ID 102
                //                        This is comment 3
                //Article 2 ID 29
                //        ArticleCategory 2 ID 42
                //                Comment4 ID 103
                //                        This is comment 4
                //                Comment5 ID 104
                //                        This is comment 5
                //                Comment6 ID 105
                //                        This is comment 6
                //Article 3 ID 30
                //        ArticleCategory 3 ID 43
                //                Comment7 ID 106
                //                        This is comment 7
                //                Comment8 ID 107
                //                        This is comment 8
                //                Comment9 ID 108
                //                        This is comment 9         

                Console.ReadLine();

                // BETTER WAY (imho)...(joins aren't needed thus freeing up SQL overhead)

                // fetch a master list of articles from the database
                articles = Repository.All<Article>().ToList();

                foreach (var article in articles)
                {
                    Console.WriteLine(article.Name + " ID " + article.Id);
                    Console.WriteLine("\t" + article.Category.Name + " ID " + article.Category.Id);

                    foreach (var articleComment in article.Comments)
                    {
                        Console.WriteLine("\t\t" + articleComment.Name + " ID " + articleComment.Id);
                        Console.WriteLine("\t\t\t" + articleComment.Body);
                    }
                }

                Console.ReadLine();

                // OUTPUT should be identicle
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                Console.ReadLine();
            }
        }
    }
}

以下是我个人的意见和推测,可以按照你认为合适的方式进行投掷/使用......

如果你看一下“更好的方法”的例子,这更像是我实际使用SubSonic的方式。

SubSonic基于一些简单的原则,例如

  • 每个表都是一个对象(类)
  • 每个对象实例都有一个Id(例如Article.Id)
  • 每个对象都应具有名称(或类似)

现在,如果您在使用SubSonic时以一种有意义的方式编写数据实体(表示表的表示),那么您将作为一个团队一起工作。我在使用SubSonic时并没有真正做任何连接,因为我通常不需要,而且我不想要开销。您开始在文章对象上显示“延迟加载”注释列表属性的良好做法。这很好,这意味着如果我们需要消费者代码中的注释,请转到get-em。如果我们不需要评论,请不要花时间和钱要从数据库中取出来。我以一种对我有意义的方式将ArticleCategory重组为Article关系,但可能不适合您的需求。看起来你在概念上同意了Rob(我再次同意他的意见)。

现在,该架构还有1000项其他改进。首先想到的是实现一个体面的缓存模式。例如,每次加载文章时,您可能不希望从每篇文章的数据库中获取注释。因此,您可能希望缓存文章和评论,如果添加了评论,则在“添加评论”代码中,从缓存中擦除文章,以便下次加载时重建。类别就是一个完美的例子...我通常会将类似(类似于每5分钟不可能改变的东西)加载到一个主词典中(int是类别ID)并且只引用内存列表来自我的文章代码。这些只是基本的想法,缓存,关系映射数据库或其他方面的概念可以随意变得复杂。我只是亲自尝试遵循SubSonic的心态,使数据库生成和操作变得更加容易。

注意:如果你看一下Linq2SQL的工作方式,这种方法在最基本的层面上非常相似。 Linq2SQL通常每次加载您的依赖关系,无论您是否想要它或知道它是否正在这样做。如果你愿意的话,我更喜欢SubSonic的“显而易见性”,即实际发生的事情。

很抱歉这个咆哮,但我真的希望你能在一个小的控制台应用程序中运行上面的代码,并了解我所得到的。

答案 1 :(得分:1)

首先想到的是,类别关联不应该是另一种方式吗?类别有很多文章吗?我不知道这是否有帮助 - 这是我的第一次观察。

我认为你在这里遇到了命名冲突。 “Structure.Query”是我们在Structs.tt上创建的对象,它看起来像是你用“item.Comments”以某种方式引用的命名空间。我的猜测是有一个名为“评论”的桌子让人感到困惑。

你能打开Article类并查看“Comments”属性的返回,以确保它返回一个类型,而不是查询吗?