【问题标题】:C# database access, Dapper, SQL and POCOs - programming designC# 数据库访问、Dapper、SQL 和 POCO - 编程设计
【发布时间】:2012-06-30 15:09:36
【问题描述】:

假设我们有一个用 C# 表示的 SQL 表,如下所示:

public class Product
{
   public int ID { get; set; }
   public string Name { get; set; }
   public string Picture { get; set; } // filename of the picture, e.g. apple.jpg
   public int CategoryID { get; set; }
}

现在我们将查询数据库并检索对象,假设使用如下值:

ID = 1
Name = Yellow apple
Picture = apple.jpg
CategoryID = 25

一切正常。我现在正在思考的事情是:如果我想展示一个产品,我需要一些没有从数据库中查询到的附加信息,比如图像的确切文件路径,我们只有

苹果.jpg

,但我们可能需要像

这样的东西

~/images/apple.jpg


所以,我想到了 3 种可能性:

1.) 向 Product 类添加一个新属性

public string PictureUrl
{
    get
    {
        return "~/images/apple.jpg";
    }
}

2.) 在执行演示逻辑期间指定完整的 url,比如说:

public void ShowProductDetails()
{
    Product p = ProductRepo.GetProduct(id);

    txtName.Text = p.Name;
    imgPicture.ImageUrl = "~/images/" + p.Picture;
}

3.) 使用装饰器模式

第一种方法对我来说似乎是错误的(尽管我已经使用了很长时间),因为我正在尝试创建一个分层的 Web 应用程序。我不确定硬编码这是一个好方法。

第二种方法更好,但更糟糕的是它不能轻易重复使用。如果我有多个地方在做同样的事情并且发生了一些变化,......如果我指定一些静态常量来保存路径,也许它会起作用......

就可维护性而言,第三种可能性似乎相当复杂。我的班级数量可能要翻倍。如果我现在有 30 节课,它会突然变成 60 节:/

做这种事情的最佳/推荐方式是什么?如果我向我的 POCO 添加不包含在 db 架构中的属性,我将无法使用 Dapper.Contrib 或 Rainbow 和类似库,因为即使“选择”工作正常,我也无法“插入”或“删除”。我必须为每个命令硬编码 sql 字符串,这在一段时间后变得非常乏味,当你一直在做同样的事情时。

编辑:

Govind KamalaPrakash Malviya 的解决方案很棒,但不能每次都使用。我需要一种方法来解决任何类型的属性,即使是那些更复杂的属性 - 例如某些相册的照片数量。将照片的数量与相册一起查询是个好主意,但将其分配给什么?使用装饰器模式创建装饰类?

您如何解决此类架构问题?

【问题讨论】:

    标签: asp.net sql oop design-patterns dapper


    【解决方案1】:

    我认为您应该在表示层中操作它,因为仅表示层的图像路径。所以使用第三个,但使用实用方法更容易

    public class PathUtility
    {
        public static string ImageUrl(string imageName)
        {
    
              if(string.IsNullOrEmpty(imageName))
              {
                   throw new Exception("Image name not valid!!");
              } 
              else
              {
                   return "YourImageDirectroyUrl" + imageName;
              }
    
        } 
    }
    

    并轻松使用它

    PathUtility.ImageUrl("apple.jpg");
    

    【讨论】:

    • 公共静态字符串 ImageUrl(string imageName, string directoryUrl="defaulturl")
    • 是的,这看起来很有希望。我也在想类似的东西,只是忘了提。但是,如果所需的额外属性有点复杂怎么办?就像我想与相册列表一起显示的相册中的照片数量。我不会把它放到我的 sql 模式中,并且不能使用这种方法来完成。
    【解决方案2】:

    我通常通过保持实体对象不变并创建一个额外的数据容器来解决这个问题,该容器将保存对相应实体的引用或实现实体对象本身的相应属性。在后一种情况下,我使用映射库 (AutoMapper) 将数据从实体复制到增强型容器。

    填充额外属性的逻辑通常位于工厂(或工厂方法)中。这取决于你,你想把它放在你的架构中的什么地方。在当前项目中,我们将它们包含在客户端的数据访问外观中,因为我们不想让太多的 DTO 使数据访问层混乱。这当然意味着,数据访问层仍然需要支持检索额外的属性。在您的情况下,像int GetNumberOfPhotosForAlbum(Album album) 这样的操作。

    我们发现,数据访问层不断增长的合约带来的好处大于风险,这当然可能需要支持许多不同的调用,例如上面的示例,而不仅仅是EnhancedAlbum GetEnhancedAlbumWithAllKindsOfExtraProperties(long albumId)。在某些情况下,这也可能成为性能问题,因为服务调用频率增加会产生开销。最后,您需要决定什么最适合您的项目。

    我喜欢这种方法,因为我的实体 (Album) 保持不变,并且我在持久性、客户端逻辑和映射之间保持了明确的关注点分离。

    示例:

    class Album
    {
        string Name { get; set; }
    }
    
    class EnhancedAlbum
    {
        Album Album { get; set; }
        int NumberOfPhotos { get; set; }
    }
    
    class EnhancedAlbumFactory
    {
        private MyDataService _dataService;
    
        //include some means of constructing or (better) injecting the data service
    
        EnhancedAlbum GetEnhancedAlbum(Album album)
        {
            return new EnhancedAlbum
                   {
                       Album = Album,
                       NumberOfPhotos = _dataService.GetNumberOfPhotosForAlbum(album);
                   };
        }
    }
    

    【讨论】:

    • 感谢您的回答!举个例子,使用你的方法会是什么样子?听起来有点像装饰者模式与工厂模式的结合。 :-)
    • 我添加了一个简单的例子。它使用工厂,但这也可能只是另一个类中的工厂方法。它没有实现装饰器模式,但如果需要,可以扩展为这样做。不过我会尽量保持简单。
    • 谢谢。我会等待其他人是否提出不同的解决方案,如果没有,我可能会选择你的答案,因为它似乎比 Govinds 的答案更适合我的需求。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-30
    • 2012-09-20
    • 2014-07-16
    • 1970-01-01
    • 2012-02-23
    • 2011-05-07
    • 1970-01-01
    相关资源
    最近更新 更多