服务层如何适合我的存储库实现?

如何解决服务层如何适合我的存储库实现?

| 我创建了一个POCO模型类和一个处理持久性的存储库类。由于POCO无法访问存储库,因此存储库中有很多业务逻辑任务似乎并不正确。从我阅读的内容来看,似乎需要一个位于UI使用者和存储库层之间的服务层。我不确定它到底应该如何工作... 除了服务层之外,还应该有一个单独的业务逻辑层,还是服务层的作用? 每个存储库应该有一项服务吗? 服务层是UI可以实例化模型对象的唯一方法,还是存储库向服务提供新的模型实例? 我是否将我的参数,模型和其他验证放在服务层中,以进行诸如检查以确保输入有效并在更新之前数据库中存在要更新的项目之类的事情? 模型,存储库和UI都可以对服务层进行调用,还是仅供UI使用? 服务层应该是所有静态方法吗? 从UI调用服务层的典型方法是什么? 模型与服务层应进行哪些验证? 这是我现有图层的一些示例代码:
public class GiftCertificateModel
{
    public int GiftCerticiateId {get;set;}
    public string Code {get;set;}
    public decimal Amount {get;set;}
    public DateTime ExpirationDate {get;set;}

    public bool IsValidCode(){}
}


public class GiftCertificateRepository
{
    //only way to access database
    public GiftCertificateModel GetById(int GiftCertificateId) { }
    public List<GiftCertificateModel> GetMany() { }
    public void Save(GiftCertificateModel gc) { }
    public string GetNewUniqueCode() { //code has to be checked in db }

    public GiftCertificateModel CreateNew()
    {
        GiftCertificateModel gc = new GiftCertificateModel();
        gc.Code = GetNewUniqueCode();
        return gc;
    }              
}
更新: 我目前正在使用Web表单和经典的ADO.NET。我希望最终迁移到MVC和EF4。 更新:非常感谢@Lester的出色解释。现在,我了解到需要为每个存储库添加一个服务层。该层将是UI或其他服务与存储库通信的唯一方式,并将包含不适合域对象的任何验证(例如-需要调用存储库的验证)
public class GiftCertificateService()
{

    public void Redeem(string code,decimal amount)
    {
        GiftCertificate gc = new GiftCertificate();
        if (!gc.IsValidCode(code))
        {
            throw new ArgumentException(\"Invalid code\");
        }

        if (amount <= 0 || GetRemainingBalance(code) < amount)
        {
            throw new ArgumentException(\"Invalid amount\");
        }

        GiftCertificateRepository gcRepo = new GiftCertificateRepository();
        gcRepo.Redeem(code,amount);
    }

    public decimal GetRemainingBalance(string code)
    {
        GiftCertificate gc = new GiftCertificate();            
        if (!gc.IsValidCode(code))
        {
            throw new ArgumentException(\"Invalid code\");
        }

        GiftCertificateRepository gcRepo = new GiftCertificateRepository();
        gcRepo.GetRemainingBalance(code);
    }

    public SaveNewGC(GiftCertificate gc)
    {
        //validates the gc and calls the repo save method
        //updates the objects new db ID
    }

}
问题 我是否要向服务中添加与我的模型相同的(可能更多)属性(金额,代码等),还是仅提供接受GiftCertificate对象和直接参数的方法? 当调用Service构造函数时,是否要创建GiftCertificate实体的默认实例,还是根据需要创建新实例(例如-对于服务中需要在实体中调用验证方法的验证方法?),有关创建默认值的相同问题储存库实例...? 我知道我通过服务公开了仓库的功能,我是否也公开了实体中的方法(例如-IsValidCode等)? UI可以直接直接创建新的GiftCertificate对象,而无需通过服务(例如-从实体调用参数验证方法)。如果没有,如何执行呢? 在UI层上,当我想创建新的礼券时,是否直接从UI层调用模型/服务验证(例如IsValidExpirationDate等),还是先对对象进行水化处理,然后将其传递以进行验证并然后将某种验证摘要返回给用户界面? 另外,如果我想从UI层进行兑换,我是否首先要从UI调用模型/服务验证方法以提供用户反馈,然后再调用将在内部再次运行相同检查的Redeem方法? 调用服务以从UI进行兑换操作的示例:
string redeemCode = RedeemCodeTextBox.Text;

GiftCertificateService gcService = new GiftCertificateService();
GiftCertificate gc = new GiftCertificate(); //do this to call validation methods (should be through service somehow?)

if (!gc.IsValid(redeemCode))
{
    //give error back to user
}

if (gcService.GetRemainingBalance(redeemCode) < amount)
{
    //give error back to user
}

//if no errors
gcService.Redeem(code,amount);
从UI创建新的礼品证书的示例:
GiftCertificateService gcService = new GiftCertificateService();
GiftCertificate gc = new GiftCertificate();

if (!gc.IsValidExpDate(inputExpDate))
{
    //give error to user..
}

//if no errors...
gc.Code = gcService.GetNewCode();
gc.Amount = 10M;
gc.ExpirationDate = inputExpDate;
gcService.SaveNewGC(gc);
//method updates the gc with the new id...
GC的创建方式以及实体/服务之间的验证分离方式令人感到有些不对。用户/消费者应该不必担心在什么地方进行的验证……建议?     

解决方法

看看S#arp Architeture。它就像用于构建ASP.NET MVC应用程序的最佳实践架构框架。通用体系结构模式是每个实体有1个存储库,仅负责数据访问,每个实体有1个服务,仅负责业务逻辑以及控制器和服务之间的通信。 要根据S#arp Architeture回答您的问题: 除了服务层之外,还应该有一个单独的业务逻辑层,还是服务层的作用? 模型应负责字段级验证(例如,使用必需的字段属性),而控制器可以在保存之前验证数据(例如,在保存之前检查状态)。 每个存储库是否应该有一个服务层? 是的-每个存储库应有一项服务(每个存储库应没有1个服务层,但我想您是这么想的)。 服务层是UI可以实例化模型对象的唯一方法,还是存储库向服务提供新的模型实例? 存储库和服务可以根据需要返回单个实体,实体集合或数据传输对象(DTO)。控制器会将这些值传递给模型中的静态构造函数方法,该方法将返回模型的实例。 使用DTO:
GiftCertificateModel.CreateGiftCertificate(int GiftCerticiateId,string Code,decimal Amount,DateTime ExpirationDate)
我是否将我的参数,模型和其他验证放在服务层中,以进行诸如检查以确保输入有效并在更新之前数据库中存在要更新的项目之类的事情? 模型可以验证字段级值ex。通过检查必填字段,年龄或日期范围等来确保输入有效。服务应进行所需的任何验证,这些验证需要检查模型值ex之外。检查礼品券是否尚未兑现,请检查礼品券所针对的商店的属性。 模型,存储库和UI都可以对服务层进行调用,还是仅供UI使用? 控制器和其他服务应该是唯一调用服务层的服务。服务应该是唯一调用存储库的服务。 服务层应该是所有静态方法吗? 它们可以,但是如果没有,则更易于维护和扩展。如果每个实体/子类只有1个服务,则对实体的更改和添加/删除子类的更改将更容易更改。 从UI调用服务层的典型方法是什么? 控制器调用服务层的一些示例:
giftCertificateService.GetEntity(giftCertificateId); (which in turn is just a call to the giftCertificateRepository.GetEntity(giftCertificateId)

giftCertificateService.Redeem(giftCertificate);
模型与服务层应进行哪些验证? 上面已经回答了。 更新 由于您使用的是WebForms,因此掌握其中一些概念可能会有些困难,但是我所提到的所有内容都是适用的,因为我所描述的是通用的MVC范例。 ADO.NET用于数据访问无关紧要,因为数据访问是通过存储库分离的。 我是否要向服务中添加与我的模型相同的(可能更多)属性(金额,代码等),还是仅提供接受GiftCertificate对象和直接参数的方法? 您需要准确看一下服务的名称所暗示的含义-控制器可以调用的操作。您将不需要模型中定义的属性,因为它们已在模型中可用。 当调用Service构造函数时,是否要创建GiftCertificate实体的默认实例,还是根据需要创建新实例(例如-对于服务中需要在实体中调用验证方法的验证方法?),有关创建默认值的相同问题储存库实例...? 控制器和服务应分别具有服务和存储库的专用字段。您不应该为每种操作/方法实例化。 我知道我通过服务公开了仓库的功能,我是否也公开了实体中的方法(例如-IsValidCode等)? 不太清楚您的意思。如果服务返回实体,则实体上的那些方法已经公开。如果他们返回DTO,则表明您仅对某些信息感兴趣。 对于验证,我可以看到您为什么有些担心,因为直接在模型上进行了验证,而在服务中进行了其他类型的验证。我使用的经验法则是,如果验证需要调用db,则应在服务层中完成。 UI可以直接直接创建新的GiftCertificate对象,而无需通过服务(例如-从实体调用参数验证方法)。如果没有,如何执行呢? 在UI层上,当我想创建新的礼券时,是否直接从UI层调用模型/服务验证(例如IsValidExpirationDate等),还是先对对象进行水化处理,然后将其传递以进行验证并然后将某种验证摘要返回给用户界面? 对于这两个问题,让我们来看一个场景: 用户输入信息以创建新证书并提交。存在字段级别的验证,因此,如果文本框为空或美元金额为负,则抛出验证错误。假设所有字段均有效,则控制器将调用服务“ 6”。 该服务将检查其他业务逻辑,例如商店是否已经发行了太多的礼券。如果存在多个错误代码,则它返回状态的枚举,或者引发错误信息异常。 最后,服务呼叫calls7ѭ。     ,您不必为每个实体创建存储库,有关更多信息,请参见此处,   通常每个定义一个存储库   在域中聚合。那就是:我们   每个实体没有存储库!如果   我们来看一个简单的订单输入   系统实体订单可能是   订单集合的根。因此,我们   将有一个订单存储库。 每个存储库应该有一项服务吗? ->并非总是如此,因为您可能在一项服务中使用多个存储库。 服务创建了Model实例,存储库将永远不会与Model进行交互,实际上,它返回模型将随后使用的Entity。 在UI级别处理输入/范围等类型的验证(您可以使用javascript或任何其他库),并让Services仅处理业务方面。您可以得到Attributes的好处,而Attributes也会做同样的事情。 UI-> Service-> Repository,如果存储库正在调用服务,则thr必须是IMO错误。 您编码更改, 将模型和存储库分开。
public class GiftCertificateModel
{
}
public class GiftCertificateRepository
{
   //Remove Model related code from here,and just put ONLY database specific code here,(no business logic also). Common methods would be Get,GetById,Insert,Update etc. 

    Since essence of Repository is to have common CRUD logic at one place soyou don\'t have to write entity specific code. 
    You will create entity specific repository in rare cases,also by deriving base repository.

}

public class GiftCertificateService()
{
    //Create Model instance here
    // Use repository to fill the model (Mapper)

}
    ,您可以创建一个名为GiftCertificateService的服务。 这样,您可以将不属于GiftCertificateModel职责的任何任务协调到其服务中。 (不要与WCF服务混淆)。 该服务将控制所有任务,因此您的UI(或任何可能的调用方)将使用服务中定义的方法。 然后,服务将调用模型上的方法,使用存储库,创建事务等。 对于前。 (基于您提供的示例代码):
public class GiftCertificateService 
{
   public void CreateCertificate() 
   {
      //Do whatever needs to create a certificate.
      GiftCertificateRepository gcRepo = new GiftCertificateRepository();
      GiftCertificateModel gc = gcRepo.CreateNew();
      gc.Amount = 10.00M;
      gc.ExpirationDate = DateTime.Today.AddMonths(12);
      gc.Notes = \"Test GC\";
      gcRepo.Save(gc);
   }
}
UI将调用CreateCertificate方法(传递参数等),并且该方法也可能返回某些内容。 注意:如果您希望该类在UI上起作用,请创建一个控制器类(如果您正在执行MVC)或一个演示者类(如果您正在执行MVVM,并且不想将所有内容都放在ViewModel中),然后使用该类中的GiftCertificateService。     

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-