質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

ASP.NET MVC Framework

ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。

Q&A

解決済

1回答

3955閲覧

ASP.NET MVC: 自作クラスのstringへのキャスト&検証属性の利用

sk_3122

総合スコア1126

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

ASP.NET MVC Framework

ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。

0グッド

1クリップ

投稿2018/05/14 08:43

編集2018/05/15 06:36

前提

  • ASP.NET MVC 5 (C#) を使用したシステムを開発しています。
  • モデルの実装についての質問です。

実現したいこと・試したこと

  • 1 つのフィールドに対し、複数の値を持たせたい(?)です。

↓ 現行

csharp

1// 一行分のデータのモデル 2public class HogeRow 3{ 4 public string FugaName { get; set; } // ★現行はただの string 型プロパティ 5} 6... 7 8var model = new HogeRow(); 9model.FugaName = "ふがふが";

↓ こんな感じで持ちたい

csharp

1// 一行分のデータのモデル 2public class HogeRow 3{ 4 public MyField FugaName { get; set; } // ★自作クラス型のプロパティにする 5} 6... 7 8var model = new HogeRow(); 9model.FugaName.Value = "ふがふが"; // プロパティ値。 10model.FugaName.Org = "フガフガ"; // [参照用として持ちたい] 下位権限者が入力した値が入る 11model.FugaName.Mod = "FUGA"; // [参照用として持ちたい] 上位権限者側で修正した値が入る 12 13... 14public class MyField 15{ 16 public string Value { get; set; } 17 public string Org { get; set; } 18 public string Mod { get; set; } 19 ... 20 // ToString() をオーバーライド 21 public override string ToString() 22 { 23 return this.Value; 24 } 25 // MyField → string への変換演算子を定義する (implicit = 暗黙の変換) 26 public static implicit operator string(MyField val) 27 { 28 if (val == null) return null; 29 return val.Value; 30 } 31}
  • 『 基本的に 通常の string 型プロパティと同じように扱いたいが、+α の値も持ちたい 』

というのが最終的な希望です。

問題点1:キャスト

最初は string 型を継承した自作クラスを作りたい・・・ と思ったのですが、シール型は継承できないと怒られたので前出のようにしてみました。

public static implicit operator string(MyField val)
を実装したので、以下のキャストは通ります。

csharp

1var str = (string)model.FugaName; // OK

しかし、as でのキャストがエラーになります。

csharp

1var str = model.FugaName as string; // 'MyFieldValue': シール型 'string' から派生することはできません。

【質問1】
上記が通るようにする方法はないでしょうか・・・?
(別の掲示板 stackoverflow を見てみたら "ないよ!" という書き込みがあったのですが・・・ どうしてもありませんか・・・)

問題点2:検証属性

  • 件のモデルでは、検証属性を色々使っています。

csharp

1// 一行分のデータのモデル 2public class HogeRow 3{ 4 [Required] 5 [StringLength(10, ErrorMessage = "10桁以下で入力してください")] 6 public MyField FugaName { get; set; } 7}

【質問2】
String 値を検証する 検証属性を使いたいのですが、
これも都度 「StringLengthAttribute を継承した MyStringLengthAttribute ...」 などを実装するしかないでしょうか?
(MyField.Value を検証して欲しいです)

public static implicit operator string(MyField val)
を実装したのでそのまま検証してくれないかな、とちょっと期待したのですがエラーが出ました。
('MyField': シール型 'string' から派生することはできません。)

使いたい検証属性を全部実装するしかないでしょうか

追記:そもそもの修正要件

現状、単純に
「下位権限者が入力する」
「それを上位権限側で確認する(参照するのみ)」
というシステムがあります。

それに対し、
「上位権限者側でも修正を入れたい」
という要件が上がってきました。

なので、それぞれの入力フィールドに対し
「下位権限者による入力値」Org
「上位権限者による入力値」Mod
「最終的な採用値」Value
を持ちたいと思っています。
(Org, Mod は画面表示用として持ちたいだけ)

検討1:それぞれプロパティを個別に用意する

まず考えたのは以下ですが、プロパティが多く、同様のモデルも多い為、ひとまず無しとしました。

csharp

1// 一行分のデータのモデル 2public class HogeRow 3{ 4 public string FugaName { get; set; } 5 public string OrgFugaName { get; set; } 6 public string ModFugaName { get; set; } 7 /* 8 さらに 9 「Orgのデータがあるか」 10 「Modのデータがあるか」 11 「上位権限者による編集が行われているか」 12 などの判定ロジックも欲しい。MyField のように自作クラス化するならその中に作ってしまえるけど・・・ 13 */ 14 ... 15}
検討2:モデルの入れ子にする

次に以下のパターンも考えました。

csharp

1// 一行分のデータのモデル 2public class HogeRow 3{ 4 public HogeRow Org { get; } 5 public HogeRow Mod { get; } 6 7 public string FugaName { get; set; } 8 ... 9} 10/* 11 model.FugaName 12 model.Org.FugaName 13 model.Mod.FugaName 14 のようにアクセスする 15*/

このケースについては、以下が気になった為 なしとしました。

・HogeRow には更新系のメソッドなども定義しているが、Org や Mod ではそれらのメソッドを呼んで欲しくない。(呼ばなければ良いだけなのですが、呼んでほしくないメソッドが定義されているというのが気になりました)
⇒ なお、サンプルコードでは省いていますが、HogeRow は既に別の基底クラスを継承しています。
なので「プロパティだけを定義したクラスを別に作って、Org, Mod はそのクラスで定義、HogeRow はそのクラスを継承」という方法もちょっと難しいです。

・やはり プロパティ単位で Org や Mod を扱いたい。
(model.Org.FugaName ではなく model.FugaName.Org で扱いたい)
「上位権限者による編集が行われているか」の判定等をするのにもその方がやりやすいので・・・

検討3:ディクショナリで持つ

参照用の値はディクショナリに溜めようかとも考えたのですが、
文字列でアクセスするのがちょっと嫌だったので却下しました。

csharp

1// 一行分のデータのモデル 2public class HogeRow 3{ 4 public string FugaName { get; set; } 5 ... 6 7 // key=権限区分@プロパティ名で Org, Mod の値を持つ 8 private Dictionary<string, string> dict = new Dictionary<string, string>(); 9 public string GetFieldValue(string kengenKbn, string fieldName) 10 { 11 // 取得用メソッドなどを用意 12 } 13} 14/* 15 model.FugaName 16 model.GetFieldValue("1", "FugaName") 17 model.GetFieldValue("2", "FugaName") 18 のようにアクセスする 19*/

以上です。
もし分かる方がいらっしゃいましたら よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/05/15 02:23

MVC5 とのことですが、であれば MVC4 のタグではなく、ASP.NET または ASP.NET MVC Framework のタグがありますので、そちらの方がよさそうです。
退会済みユーザー

退会済みユーザー

2018/05/15 02:28

とっても特殊なことをしているようですが(絶対不可能とまで言い切る自信はないですが)、そういうことをする必要性・理由が分かりません。できれば全体的なやりたいことのシナリオ・ストーリーも書いていただけませんか。局所的な部分を切り出して質問すると、真にやりたいことの解決にはなかなかつながりにくいケースが多いので。また、こうした方が良いという代案が出てくるかもしれませんし。
sk_3122

2018/05/15 04:15

ありがとうございます。タグを「ASP.NET MVC Framework」に直しました。あと「追記:そもそもの修正要件」以降を追加してみました。
sk_3122

2018/05/15 04:16

実現方法自体をあれこれ考えている最中なので、直接の回答ではなく より良い設計等などでも嬉しいです。よろしくお願いします m(_ _)m
退会済みユーザー

退会済みユーザー

2018/05/15 07:29 編集

せっかく説明いただいたのに申し訳ありませんが自分の頭では理解できず「こうした方が良いという代案」を出すのは無理そうです。なので、現状の問題点1,2についてレスします。「問題点1」でどうしても as 演算子が使いたいということですと、それに対しては自分は答を持っていません。そもそも、確実に string 型にキャストできるように implicit operator を実装したのだから as 演算子とか is 演算子を使う理由はないのではと思ってしまうのですが・・・
退会済みユーザー

退会済みユーザー

2018/05/15 07:20

「問題点2」については CustomValidationAttribute を使って何とかなりそうな気がします。ただ、それも「問題点1」が解決できなければ意味がないとか、既に検討済みで使えないという結論になっているかもしれないので、未検証・未確認ですが。
退会済みユーザー

退会済みユーザー

2018/05/15 07:24

ASP.NET の質問と言うとそれだけでスレッドの中身を見ない人が大勢いると思いますので、「問題点1」は C# に限った class 定義、implicit operator の実装、as 演算子の話として、「問題点2」は ASP.NET MVC のデータアノテーション検証の話として別々のスレッドに分けて質問した方が良かったかもしれませんね。
sk_3122

2018/05/15 07:41

すみません、現在あれこれ捏ねくり回しながらモデルの設計を考えているもので、質問内容自体 分かりにくかったかと思います・・・ 目を通して頂いてありがとうございます。
sk_3122

2018/05/15 07:41

【as 演算子とか is 演算子】 共通の Util などでそれを使っている箇所があったので、as での変換ができるようになれば Util は修正しなくても良いのにな・・・ という気持ちだったので、致し方ないとあれば粛々と修正します。
sk_3122

2018/05/15 07:41

【問題点2について】 やっぱり CustomValidationAttribute でしょうか・・・。こちらについても、「StringLengthAttribute などがそのまま使えれば 現在各プロパティにしていしている検証属性を全部修正して回らなくても済む」という思いだったので、CustomValidationAttribute を作って 参照箇所を全部直せばおそらくいけるとは思います。(修正箇所が結構多いので躊躇していました)
sk_3122

2018/05/15 07:42

> 別々のスレッドに分けて質問した方が   そうですね。ちょっと自分でも質問内容が整理しきれていない部分があったかもしれません。ありがとうございました。
guest

回答1

0

自己解決

結局こうしました、というのを一応 書いておきます。

現行のモデル

  • 入力フィールドに対応する string 型のプロパティを持っている
  • string の値を検証する 検証属性を指定している

csharp

1public class HogeRow 2{ 3 [Required] 4 [StringLength(20)] 5 public string HogeName { get; set; } 6 7 [Required] 8 [MyNumberRange(0, 9, Dot=False)] 9 public string HogeKingaku { get; set; } 10 .... 11} 12

修正後

  • 入力フィールドに対応するプロパティは MyField 型で定義
  • 検証属性も MyField 型のものに差し替え

csharp

1public class HogeRow 2{ 3 [MyField.Required] 4 [MyField.StringLength(20)] 5 public MyField HogeName { get; set; } 6 7 [MyField.Required] 8 [MyField.MyNumberRange(0, 9, Dot=False)] 9 public MyField HogeKingaku { get; set; } 10 .... 11} 12
  • MyField を定義

csharp

1public partial class MyField 2{ 3 // フィールド値 4 public string Value 5 { 6 get { return this._value; } 7 set { this._value = value; } 8 } 9 private string _value = null; 10 11 12 private string _org = null; 13 private bool _existsOrg = false; 14 15 // 下位権限者 入力データ 16 public string Org 17 { 18 get { return this._org; } 19 protected internal set 20 { 21 this._org = value; 22 this._existsOrg = true; 23 } 24 } 25 // 下位権限者 入力データがあるか 26 public bool ExistsOrg 27 { 28 get { return this._existsOrg; } 29 } 30 31 32 private string _mod = null; 33 private bool _existsMod = false; 34 35 // 上位権限者 修正データ 36 public string Mod 37 { 38 get { return this._mod; } 39 protected internal set 40 { 41 this._mod = value; 42 this._existsMod = true; 43 } 44 } 45 // 上位権限者 修正データがあるか 46 public bool ExistsMod 47 { 48 get { return _existsMod; } 49 } 50 51 52 // 修正されているかどうか 53 public bool IsModified 54 { 55 get 56 { 57 // 修正データがない場合は「修正なし」 58 if (this.ExistsMod == false) return false; 59 60 // 両方空の場合は「修正なし」 61 if (string.IsNullOrEmpty(this.Org) && string.IsNullOrEmpty(this.Mod)) return false; 62 63 // 片方だけ空の場合は「修正あり」 64 if (string.IsNullOrEmpty(this.Org) || string.IsNullOrEmpty(this.Mod)) return true; 65 66 // 両方値がある場合は、値が異なるなら「修正あり」 67 return (this.Org != this.Mod); 68 } 69 } 70 71 // ToString() をオーバーライド 72 public override string ToString() 73 { 74 return this.Value; 75 } 76 77 // MyField → string への変換演算子を定義する (implicit = 暗黙の変換) 78 public static implicit operator string(MyField val) 79 { 80 if (val == null) return null; 81 return val.Value; 82 } 83}
  • 必要な検証属性の MyField 対応版を用意する

csharp

1public partial class MyField 2{ 3 /// <summary> 4 /// StringLengthAttribute を MyField に対応させる 5 /// </summary> 6 public class StringLengthAttribute : System.ComponentModel.DataAnnotations.StringLengthAttribute 7 { 8 public StringLengthAttribute(int maximumLength) : base(maximumLength) 9 { 10 } 11 12 public override bool IsValid(object value) 13 { 14 var field = value as MyField; 15 return base.IsValid(field.Value); 16 } 17 } 18 19 ... 20}
  • あとは表示部分はほぼ修正なしでOK(string への暗黙のキャストを定義している為)

  • 共通 Util などで as や is で見ている部分に関しては適宜修正する

  • プロパティに値を突っ込んでいるところは修正が必要

csharp

1//row.FugaName = "あいう"; 2row.FugaName.Value = "あいう";

とりあえずこれで進めようと思います・・・が 今の時点においてもどういう実装が良いのか分からず 迷走している感・・・

投稿2018/05/15 08:08

sk_3122

総合スコア1126

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問