我已经创建了一个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;
    }              
}

UPDATE:

UPDATE:非常感谢@Lester的精彩解释.我现在明白了,我需要为我的每个存储库添加一个服务层.该层将是UI或其他服务与存储库通信的唯一方式,并将包含任何不适合域对象的验证(例如,需要调用repo的验证)

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
    }

}

Questions

  1. 我是否向服务中添加了与我的模型相同(可能更多)的属性(金额、代码等),还是只提供接受GiftCertificate对象和直接参数的方法?

  2. 我是在调用服务构造函数时创建GiftCertificate实体的默认实例,还是根据需要创建新实例(例如,对于服务中需要调用实体中验证方法的验证方法?还有,关于创建默认存储库实例的相同问题...?

  3. 我知道我通过服务公开了repo的功能,我是否也公开了来自实体的方法(例如,IsValidCode等)?

  4. UI可以直接创建一个新的GiftCertificate对象,而不需要通过服务(例如,从实体调用参数验证方法).若否,如何执行?

  5. 在UI层上,当我想要创建一个新的礼品证书时,我是直接从UI层调用模型/服务验证(如IsValidExpirationDate等),还是先将对象水合,然后将其传递给验证,然后将某种验证摘要返回到UI?

另外,如果我想从UI层进行兑换,是否首先从UI调用模型/服务验证方法以提供用户反馈,然后调用将在内部再次运行相同判断的redeem方法?

Example for calling service to do a Redeem operation from 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);

Example for creating a new Gift certificate from 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...

GCs的创建方式以及验证如何在实体/服务之间分离,让人感觉有些不对劲.用户/消费者不必关心在哪个地方进行了哪些验证...劝告

推荐答案

看看S#arp Architeture .这就像是构建ASP的最佳实践体系 struct 框架.NET MVC应用程序.一般的体系 struct 模式是每个实体有一个存储库,只负责数据访问,每个存储库有一个服务,只负责业务逻辑和控制器与服务之间的通信.

根据S#arp架构回答您的问题:

In addition to the service layer, should there also be a separate business logic layer, or is that the role of the service layer?

模型应负责字段级验证(例如,使用所需的字段属性),而控制器可在保存前验证数据(例如,保存前判断状态).

Should there be one service layer per repository?

是的-每个存储库应该有service个服务层(不是每个存储库有一个服务层,但我猜您是这个意思).

Is the service layer the only way that the UI can instance the model objects or does the repository provide the new model instance to the service?

存储库和服务可以根据需要返回单个实体、实体集合或数据传输对象(DTO).控制器将这些值传递给模型中的静态构造函数方法,该方法将返回模型的实例.

例如使用DTO:

GiftCertificateModel.CreateGiftCertificate(int GiftCerticiateId, string Code, decimal Amount, DateTime ExpirationDate)

Do I put my parameter, model and other validations in the service layer that do things like check to make sure a input is valid and that a item to update exists in the database before updating?

模型验证字段级别的值,例如通过判断必填字段、年龄或日期范围等确保输入有效.服务应执行任何需要在模型值之外进行判断的验证,例如判断礼券是否尚未兑换,判断礼券所在store 的属性).

Can the model, repository and UI all make calls to the service layer, or is it just for the UI to consume?

控制器和其他服务应该是唯一调用服务层的服务.服务应该是唯一一个调用存储库的服务.

Is the service layer supposed to be all static methods?

如果每个实体/子类都有一个服务,那么对实体的更改和添加/删除子类就更容易更改.

What would be a typical way to call the service layer from the UI?

控制器调用服务层的一些示例:

giftCertificateService.GetEntity(giftCertificateId); (which in turn is just a call to the giftCertificateRepository.GetEntity(giftCertificateId)

giftCertificateService.Redeem(giftCertificate);

What validations should be on the model vs the service layer?

上面已经回答了.

UPDATE

由于您使用的是WebForms,所以理解其中一些概念可能会有点困难,但我所提到的一切都是适用的,因为我所描述的是一个通用的MVC范例.阿多.NET for data access并不重要,因为数据访问是通过存储库解耦的.

Do I add the same (and possibly more) properties to the service as I have on my model (amount, code, etc) or do I only offer methods that accept GiftCertificate objects and direct parameters?

您需要按照服务名称的确切含义来看待这些服务——控制器可以调用的操作.您不需要在模型中定义的属性,因为它们在模型中已经可用.

Do I create a default instance of the GiftCertificate entity when the Service constructor is called or just create new ones as needed (eg - for validation methods in the service that need to call validation methods in the entity? Also, same question about creating a default repository instance...?

控制器和服务应分别具有服务和存储库的专用字段.您不应该 for each 操作/方法实例化.

I know i expose the functionality of the repo via the service, do I also expose the methods from the entity as well (eg - IsValidCode, etc)?

不太清楚你在这里的意思.如果服务返回实体,那么这些实体上的方法已经公开.如果他们返回DTO,那就意味着你只对某些信息感兴趣.

对于验证,我可以理解您为什么有点担心,因为有直接在模型上进行的验证和在服务中进行的其他类型的验证.我使用的经验法则是,如果验证需要调用数据库,那么应该在服务层完成.

It is ok for the UI to simply create a new GiftCertificate object directly without going through the service (eg - to call parameter validation methods from the entity). If not, how to enforce it?

On the UI layer, when I want to create a new gift certificate, do I call the model/service validations (like IsValidExpirationDate, etc) directly from the UI layer OR do I hydrate the object first, then pass it in to be validated and then return some sort of validation summary back to the UI?

对于这两个问题,让我们来看一个场景:

用户输入信息以创建新证书并提交.有字段级别的验证,因此如果文本框为空或美元金额为负,则会抛出验证错误.假设所有字段都有效,控制器将调用服务gcService.Save(gc).

该服务将判断其他业务逻辑,例如store 是否已经发放了太多的礼券.如果存在多个错误代码,则返回状态的枚举,或者抛出包含错误信息的异常.

最后,服务调用gcRepository.Save(gc).

Asp.net相关问答推荐

如何处理当前文件中基本文件中的S onClick方法

IISExpress未在ARM64 Mac/.NET 4.8上启动

在 aspx 页面中使用 if else 和 eval

在 ASP.NET 中访问没有值的查询字符串参数

SignalR + Autofac + OWIN:为什么 GlobalHost.ConnectionManager.GetHubContext 不起作用?

IE10 SCRIPT5009:__doPostBack未定义

使用会话变量有多安全 - asp.net / c#

解析器错误消息:文件/TestSite/Default.aspx.cs不存在

ASP.NET 控件无法在 Visual Studio 2008 的代码隐藏中引用

使用 IIS 的 ASP.NET 调试超时

回发后动态创建的控件丢失数据

从后面的 asp.net 代码中读取表单身份验证 cookie

oAuth ASP.NET 成员资格提供程序

Owin 中是否有来自 Global.asax 的 Application_End?

elmah:没有 HttpContext 的异常?

.Net System.Mail.Message 添加多个收件人地址

DropDownList 的 SelectedValue 与 SelectedItem.Value

配置授权服务器端点

判断 IQueryable 结果集的最佳方法是什么?

如何调试 w3wp clr.dll 错误