たぶん誰もが思いつくであろう、DisplayName属性の値を表示するHtmlHelperの拡張メソッドです。
どうして標準でないんだろう。あれ、知らないだけなのか...?
たぶん誰もが思いつくであろう、DisplayName属性の値を表示するHtmlHelperの拡張メソッドです。
どうして標準でないんだろう。あれ、知らないだけなのか...?
最近、ちょこちょこASP.NET MVC(主に2)の開発を経験して、失敗とそれに対する自分なりの正解を覚え書きします。
今まで試行錯誤とWebで先輩方の記事を読んだり、ソースコードを読んだりして、頭の中だけでやっていたんですが、やっぱり少しづつでも文章にして、整理していかないといけないと思ったので。
ちゃんとURLを設計してから、組み立てる。
しっかりしたURL設計がないと、ルーティングやコントローラの構成が破綻してしまいがち。
あとでこれを調整するのは辛い作業だった。
それぞれの機能をちゃんと考えて、相応しいURLを設計する。全てはそれから。
片方に仕様変更が入った時、もう片方にも影響が出て大変。
この歪を下手に吸収しようとすると、どんどんワケのわからない状態になって、分けておいたほうがよほど楽だったということになってしまった。
最初からビューごとにビューモデルを分けて作っていく。
※ @onosさんにご指摘頂いたので追記です。
単にビューごとにビューモデルを分けて作るのではなく、どこを切り離し、どこを共通化するのか、しっかり設計する必要がある。
例えば、検証周りの設定は単に分割して作ってしまうと、同じ設定を多方でバラバラに設定することになってしまう可能性がある。
そういった共通化と分離のバランスを取っていく。
検証のやり方はともかく、これは混同しないほうがいいと思った。
そもそも、クライアント側とサーバ側とでは、検証の目的が違うはず。
クライアント側は、ユーザビリティの向上が主な目的。
サーバ側は、データを守ることが主な目的。
検証の設定を無理に共有させようとしない。
それぞれのコンテキストに必要十分な検証を別々に設定する。また、各検証の目的を曖昧にしない。
エンティティの制御はモデル内で完結しないと、何が起こるのかわからなくなってしまう。
また、ビューでエンティティを使っていると、エンティティがビューに依存していってしまう。
そうして、ビジネスロジックとは関係ないモノがモデル内で増殖してしまった。
モデルのアウトラインとなる、サービス層のインターフェイスにエンティティを使用しない。
DTO的なものを、それぞれのコンテキスト別に用意し、それにエンティティのデータを移して使う。
AutoMapperはとても便利。
ふと書いておこうと思いついて、パッとでてくるものだけ殴り書きです。
こうやってベストプラクティス的なものを構築していくのは楽しい。
public class Version
{
public Version()
{
}
public Version(int major, int minor, int build, int revision)
{
Major = major;
Minor = minor;
Build = build;
Revision = revision;
}
public int Major { get; set; }
public int Minor { get; set; }
public int Build { get; set; }
public int Revision { get; set; }
}
public class Version : IComparable, IComparable<Version>
{
public Version()
{
}
public Version(int major, int minor, int build, int revision)
{
Major = major;
Minor = minor;
Build = build;
Revision = revision;
}
public int Major { get; set; }
public int Minor { get; set; }
public int Build { get; set; }
public int Revision { get; set; }
public int CompareTo(object obj)
{
return CompareTo(obj as Version);
}
public int CompareTo(T other)
{
if (other == null)
return 1;
int majorCompared = Major.CompareTo(other.Major);
if (majorCompared != 0)
return majorCompared;
int minorCompared = Minor.CompareTo(other.Minor);
if (minorCompared != 0)
return minorCompared;
int buildCompared = Build.CompareTo(other.Build);
if (buildCompared != 0)
return buildCompared;
return Revision.CompareTo(other.Revison);
}
}
[TestFixture]
public class ComplexComparerTest
{
[Test]
public void Comparer_Test()
{
var comparer = new ComplexComparer<Version>();
comparer
.Member(v => v.Major)
.Member(v => v.Minor)
.Member(v => v.Build)
.Member(v => v.Revision)
;
var ver_1_0_0_0 = new Version(1, 0, 0, 0);
var ver_2_0_0_0 = new Version(2, 0, 0, 0);
Assert.IsTrue(0 < comparer.Compare(ver_1_0_0_0, ver_2_0_0_0));
Assert.IsTrue(0 > comparer.Compare(ver_2_0_0_0, ver_1_0_0_0));
}
}
ラムダ式で比較に利用するメンバを設定するメソッド(Memberメソッド)を用意し、設定した順に比較が行われるようにしたい。
public class ComplexComparer<T> : IComparer, IComparer<T>
{
public ComplexComparer<T> Member<TMember>(Expression<Func<T, TMember>> expression)
where TMember : IComparable
{
// ここ実装
return this;
}
public int Compare(object x, object y)
{
return Compare((T)x, (T)y);
}
public int Compare(T x, T y)
{
// ここ実装
return 0;
}
}
public class ComplexComparer<T> : IComparer, IComparer<T>
{
private readonly IList<Comparison<T>> _comparisons = new List<Comparison<T>>();
public ComplexComparer<T> Member<TMember>(Expression<Func<T, TMember>> expression)
where TMember : IComparable
{
// あとで実装
return this;
}
public int Compare(object x, object y)
{
return Compare((T)x, (T)y);
}
public int Compare(T x, T y)
{
foreach (var comparison in _comparisons)
{
var compared = comparison(x, y);
if (compared != 0)
return compared;
}
return 0;
}
}
public ComplexComparer<T> Member<TMember>(Expression<Func<T, TMember>> expression)
where TMember : IComparable
{
var compiled = expression.Compile();
_comparisons.Add((x, y) =>
{
T xMember = compiled(x);
T yMember = compiled(y);
if (xMember != null)
return xMember.CompareTo(yMember);
if (yMember != null)
return yMember.CompareTo(xMember) * -1;
return 0;
});
return this;
}
public int Compare(T x, T y)
{
if (x == null || y == null)
{
if (x != null) return 1;
if (y != null) return -1;
return 0;
}
foreach (var comparison in _comparisons)
{
var compared = comparison(x, y);
if (compared != 0)
return compared;
}
return 0;
}
public class ComplexComparer<T> : IComparer, IComparer<T>
{
private readonly IList<Comparison<T>> _comparisons = new List<Comparison<T>>();
public ComplexComparer<T> Member<TMember>(Expression<Func<T, TMember>> expression)
where TMember : IComparable
{
var compiled = expression.Compile();
_comparisons.Add((x, y) =>
{
T xMember = compiled(x);
T yMember = compiled(y);
if (xMember != null)
return xMember.CompareTo(yMember);
if (yMember != null)
return yMember.CompareTo(xMember) * -1;
return 0;
});
return this;
}
public int Compare(object x, object y)
{
return Compare((T)x, (T)y);
}
public int Compare(T x, T y)
{
if (x == null || y == null)
{
if (x != null) return 1;
if (y != null) return -1;
return 0;
}
foreach (var comparison in _comparisons)
{
var compared = comparison(x, y);
if (compared != 0)
return compared;
}
return 0;
}
}
public class Version : IComparable, IComparable<Version>
{
public Version()
{
}
public Version(int major, int minor, int build, int revision)
{
Major = major;
Minor = minor;
Build = build;
Revision = revision;
}
public int Major { get; set; }
public int Minor { get; set; }
public int Build { get; set; }
public int Revision { get; set; }
private static readonly ComplexComparer<Version> _comparer =
new ComplexComparer<Version>()
.Member(v => v.Major)
.Member(v => v.Minor)
.Member(v => v.Build)
.Member(v => v.Revision);
public int CompareTo(object obj)
{
return CompareTo(obj as Version);
}
public int CompareTo(T other)
{
return _comparer.Compare(this, other);
}
}
工場建設クラフトゲーム、Factorio の MOD である Space Exploration のクリア記録です。 はじめに プレイ時間は約 350 時間、2023年10月から2025年2月にかけて15ヶ月間に及びました。この期間中3人の友人と毎週末、工場勤務に明け暮れました...