C# +代码合约 - 抛出异常

发布于 2024-10-08 19:31:47 字数 3678 浏览 1 评论 0原文

更新 - 由于缺乏我方面的解释,我重写了这篇文章。

您对使用代码契约在无效输入上引发异常有何看法? (我正在根据我的服务合同进行编码,该合同要求用户名不能为空或包含空格)

MembershipServiceContracts.cs - 位于子文件夹的服务层

[ContractClassFor(typeof (IMemberShipService))]
internal abstract class MemberShipServiceContracts : IMemberShipService
{
    #region IMemberShipService Members

    public MembershipCreateStatus CreateUser(string userName, string password, string email)
    {
        Contract.Requires(!String.IsNullOrWhiteSpace(userName), "Test");
        Contract.Requires(!String.IsNullOrWhiteSpace(password));
        Contract.Requires(!String.IsNullOrWhiteSpace(email));

        return default(MembershipCreateStatus);
    }

    #endregion

}

MembershipService.cs - 位于在我的服务层中

[ContractClass(typeof (MemberShipServiceContracts))]
public interface IMemberShipService
{
    MembershipCreateStatus CreateUser(string userName, string password, string email);
}

public class MemberShipService : IMemberShipService
{
    private readonly MembershipProvider _provider;

    public MemberShipService()
        : this(null)
    { }

    public MemberShipService(MembershipProvider provider)
    {
        _provider = provider ?? Membership.Provider;
    }

    #region IMemberShipService Members

    public MembershipCreateStatus CreateUser(string userName, string password, string email)
    {
        MembershipCreateStatus status;
        _provider.CreateUser(userName, password, email, null, null, true, null, out status);

        return status;
    }

    #endregion
}

AccountController.cs - 位于 UI 层

现在这是有趣的部分...

如果

    [Authorize(Roles = "Developer")]
    [HttpPost]
    public ActionResult Create(CreateUserViewModel model)
    {
        if (!String.IsNullOrEmpty(model.UserName))
        {
            throw new ArgumentException("UserName May not be null or contain only white spaces.", model.UserName);
        }

        if (!String.IsNullOrEmpty(model.Password))
        {
            throw new ArgumentException("Password May not be null or contain only white spaces", model.Password);
        }

        if (!String.IsNullOrEmpty(model.Email))
        {
            throw new ArgumentException("Email May not be null or contain only white spaces", model.Email);
        }

        if (!ModelState.IsValid)
        {
            return Json("Model validation failed");
        }

        MembershipCreateStatus newUser = _memberShipService.CreateUser(model.UserName, model.Password,
                model.Email);

        return Json(newUser != MembershipCreateStatus.Success ? "Failed" : "Success");
    }

[Authorize(Roles = "Developer")]
[HttpPost]
public ActionResult Create(CreateUserViewModel model)
{
    Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(model.UserName),
            "UserName May not be null or contain only white spaces.");

    Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(model.Password),
            "Password May not be null or contain only white spaces");

    Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(model.Email),
            "Email May not be null or contain only white spaces");

    if (!ModelState.IsValid)
    {
        return Json("Model validation failed");
    }

    MembershipCreateStatus newUser = _memberShipService.CreateUser(model.UserName, model.Password,
            model.Email);

    return Json(newUser != MembershipCreateStatus.Success ? "Failed" : "Success");
}

代码收缩 CreateUser() 方法未实现?

提前致谢。

UPDATE - Due to lack of explanation from my side, I've rewritten the post.

What do you think about using Code Contracts to throw exceptions on invalid input?
(I'm coding against a contract for my Service which requires the UserName not to be null or contain whitespaces)

MembershipServiceContracts.cs - Located in the service layer in a subfolder

[ContractClassFor(typeof (IMemberShipService))]
internal abstract class MemberShipServiceContracts : IMemberShipService
{
    #region IMemberShipService Members

    public MembershipCreateStatus CreateUser(string userName, string password, string email)
    {
        Contract.Requires(!String.IsNullOrWhiteSpace(userName), "Test");
        Contract.Requires(!String.IsNullOrWhiteSpace(password));
        Contract.Requires(!String.IsNullOrWhiteSpace(email));

        return default(MembershipCreateStatus);
    }

    #endregion

}

MembershipService.cs - Located in my service layer

[ContractClass(typeof (MemberShipServiceContracts))]
public interface IMemberShipService
{
    MembershipCreateStatus CreateUser(string userName, string password, string email);
}

public class MemberShipService : IMemberShipService
{
    private readonly MembershipProvider _provider;

    public MemberShipService()
        : this(null)
    { }

    public MemberShipService(MembershipProvider provider)
    {
        _provider = provider ?? Membership.Provider;
    }

    #region IMemberShipService Members

    public MembershipCreateStatus CreateUser(string userName, string password, string email)
    {
        MembershipCreateStatus status;
        _provider.CreateUser(userName, password, email, null, null, true, null, out status);

        return status;
    }

    #endregion
}

AccountController.cs - located at the UI layer

Now this is the interesting part...

Should I use:

    [Authorize(Roles = "Developer")]
    [HttpPost]
    public ActionResult Create(CreateUserViewModel model)
    {
        if (!String.IsNullOrEmpty(model.UserName))
        {
            throw new ArgumentException("UserName May not be null or contain only white spaces.", model.UserName);
        }

        if (!String.IsNullOrEmpty(model.Password))
        {
            throw new ArgumentException("Password May not be null or contain only white spaces", model.Password);
        }

        if (!String.IsNullOrEmpty(model.Email))
        {
            throw new ArgumentException("Email May not be null or contain only white spaces", model.Email);
        }

        if (!ModelState.IsValid)
        {
            return Json("Model validation failed");
        }

        MembershipCreateStatus newUser = _memberShipService.CreateUser(model.UserName, model.Password,
                model.Email);

        return Json(newUser != MembershipCreateStatus.Success ? "Failed" : "Success");
    }

or:

[Authorize(Roles = "Developer")]
[HttpPost]
public ActionResult Create(CreateUserViewModel model)
{
    Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(model.UserName),
            "UserName May not be null or contain only white spaces.");

    Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(model.Password),
            "Password May not be null or contain only white spaces");

    Contract.Requires<ArgumentException>(!String.IsNullOrWhiteSpace(model.Email),
            "Email May not be null or contain only white spaces");

    if (!ModelState.IsValid)
    {
        return Json("Model validation failed");
    }

    MembershipCreateStatus newUser = _memberShipService.CreateUser(model.UserName, model.Password,
            model.Email);

    return Json(newUser != MembershipCreateStatus.Success ? "Failed" : "Success");
}

to throw the an exception if the code contracts for the CreateUser() method isnt fulfilled?

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

凯凯我们等你回来 2024-10-15 19:31:47

代码合同中的失败表明您的代码存在需要修复的严重错误。它不会取代用户输入的验证。相反,您将首先验证用户输入,然后在确定有效时尝试处理数据(否则提示用户重新输入)。如果您的有效数据不满足目标合约,那么将会抛出一个无法捕获的异常,因为您显然有一个错误。

我希望这是一个灵丹妙药,但很快意识到我仍然需要我的验证。从那以后,我开始真正喜欢代码合约取代我常用的所有防护代码的方式。

想一想您每次都会遇到 NullReferenceException(对我来说太多了!)。代码契约可以消除这种痛苦。

R.

Ps 另外,不要使用合约来验证安全敏感数据,因为代码合约可以在运行时关闭......

A failure in a Code Contract indicates that your code has a serious bug that should be fixed. It doesn't replace validation of user input. Rather, you would first validate your user input, then attempt to process the data if determined to be valid (otherwise prompt the user to re-enter). If you then don't satisfy the target contract with your valid data, then an uncatchable exception will be thrown as you pretty obviously have a bug.

I hoped it would be a silver bullet but quickly realised I still needed my validation. After that, I grew to really love the way code contracts will replace all of my usual guard code.

Think of all the times you would get a NullReferenceException (too many for me!). Code Contracts can take that pain all away.

R.

P.s. Also, don't use contracts to validate security-sensitive data as code contracts can be turned off at runtime...

黎夕旧梦 2024-10-15 19:31:47

恐怕要说:这取决于你想如何使用代码契约。

您最好查看用户手册,部分“使用指南”。这是一小段摘录:

最简单的合约工具使用
如果您决定不需要
执行参数验证
发布版本中的运行时(用法 1)。
在这种情况下,您可以使用合同
开发期间的工具,但不是
运送的位。请记住,你可以
发送合同参考组件
连同你的发布位所以 18
客户端可以获得运行时检查
您的参数验证
通过调用站点进行调试构建需要
检查。第二个最简单的方法
如果您需要参数验证
您的发布版本将打开
所有构建中的合同检查(使用
2)。因此,您可以利用
生成运行时的工具
您的条件字符串并
为您执行契约继承。
您可以选择生产特定的
您的参数的例外情况
验证,或者有默认值
合约异常。

我建议您在做出任何重大决定之前阅读整个部分。

I'm afraid to say: it depends on how you want to use code contracts.

You'd better look at the user manual, section "Usage Guidelines". Here's a small excerpt:

The easiest use of the contract tools
is if you decide that you don't need
to perform argument validation at
runtime in release builds (Usage 1).
In that case, you use the contract
tools during development, but not on
the shipped bits. Remember, you can
ship a contract reference assembly
along with your release bits so 18
clients can get runtime checking of
your parameter validations on their
debug builds via call-site requires
checking. The second easiest approach
if you need argument validation in
your release build is to turn on
contract checking in all builds (Usage
2). You therefore take advantage of
the tools to produce the runtime
strings of your conditions and to
perform contract inheritance for you.
You can choose to produce specic
exceptions for your parameter
validations, or have the default
ContractException.

I'd suggest you read the whole section before making any big decisions.

冰雪梦之恋 2024-10-15 19:31:47

两种方法都很好。

检查先决条件的 3 种方法是

  1. Contract.Requires -> 。进入该方法时,特定条件必须为真。

  2. 合同.需要->与上面相同,但如果不满足条件则抛出异常
    Requires 始终被编译,因此使用此方法需要对工具产生硬依赖。在使用此方法之前,您应该决定是否需要这样做。

  3. 你的第一个方法 -->这种类型的前提条件的好处是它总是可以执行运行时检查。

在第一种方法中,您可能需要添加 Contract.EndContractBlock();

我自己正在探索此功能,但我认为您可以在服务层中抛出异常,这是可以的。

华泰

Both of the approaches are fine .

The 3 ways that one has for checking pre conditions are

  1. Contract.Requires -> a particular condition must be true upon entry to the method.

  2. Contract.Requires -> Same as above but throw exception if condition not met
    Requires is always compiled, so use of this method entails a hard dependency on the tools. You should decide if you want that before using this method.

  3. Your first approach --> The benefit of this type of precondition is that it is always there to perform the runtime check.

In the first approach you might want to add Contract.EndContractBlock();

I am exploring this feature myself but I think you can throw exception in your service layer and it is ok.

HTH

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文