web-dev-qa-db-ja.com

プロジェクト全体で部分クラスを使用する必要がありますか?

すべてのデータベースロジックを含むクラスライブラリがあります。私のDAL/BLL。

同じデータベースとクラスを使用するWebプロジェクトがいくつかあるので、データレイヤーを独自のプロジェクトに抽象化することをお勧めします。

ただし、特定のプロジェクトのクラスに機能を追加することになると、特定のクラスにメソッドを追加する必要があります。

たとえば、私のデータレイヤーにはProductオブジェクトとSomeItemオブジェクトがあります。

// Data Access Layer project

namespace DAL {
  public class Product { 
     //implementation here 
  }

  public class SomeItem {
     //implementation here 
  }
}

1つのプロジェクトで、さまざまなコンテンツアイテムで使用されるインターフェイスを追加したいので、次のクラスを呼び出します。

// This is in Web Project
namespace DAL {
  public partial class Product : ICustomBehaviour {

    #region ICustomBehaviour Implementation
       TheSharedMethod();
    #endregion
  }
}

same名前空間を使用して、部分クラスを別のプロジェクトに作成する(依存関係を作成する)ことは良い考えですか?このタイプの機能を機能させるにはどうすればよいですか?

コンパイル時にそれらをマージしたくはないようですので、私は何が間違っているのかわかりません。

42
Armstrongest

プロジェクト間で部分クラスを作成することはできません。部分クラスは、コンパイル時のみの構文糖衣です。タイプ全体が単一のアセンブリ、つまり1つのプロジェクトになります。

(ちなみに、元のDALファイルでは、クラスも部分的であると宣言する必要があります。)

79
Jon Skeet

レイヤーを整理する最良の方法についての質問にはお答えできませんが、部分クラスをエミュレートするための最良の方法についての質問にお答えすることはできます。

ここにいくつかの考えがあります:

  • 最初に思い浮かぶのは継承です。これは常に最良のソリューションであるとは限りませんが、オブジェクトを基本クラスのように扱うことができるようにする必要がある場合があるため、選択肢がない場合があります。
  • 構成も適切な選択です(つまり、クラスを別のクラスでラップする)。これにより、DALからのデカップリングが少し改善されますが、実装が面倒な場合があります。
  • メソッドを1つまたは2つだけ既存のクラスに追加する必要がある場合は、 extension method の使用も検討できますが、これらを使用しすぎると、スパゲッティコードがすぐに作成される可能性があります。
5
Jason Baker

Visual Studio 2015以降を使用すると、プロジェクト間で部分クラスを分割できますshared projectsこちらのMSDNブログもご覧ください )。

私の状況では、次のものが必要でした。

  • 複数のクライアントアプリケーションによってインターフェイスとして使用されるいくつかのクラスを定義するクラスライブラリ。
  • クライアントアプリケーション。
  • データベースにテーブルを作成するセットアップアプリケーション。
    インストーラーの制限により、このセットアップは自己完結型である必要があります。 .NETフレームワーク以外のアセンブリを参照することはできません。セットアップはいくつかの列挙定数をテーブルに挿入する必要があるため、理想的にはクラスライブラリを参照する必要があります。
    セットアップは共有プロジェクトをインポートできます。
  • 共有プロジェクトは貼り付けコードのコピーに似ているため、共有プロジェクトにはできるだけ移動しないようにします。

次の例は、部分クラスと共有プロジェクトがクラスを異なるプロジェクトに分割する方法を示しています。

クラスライブラリのAddress.cs:

namespace SharedPartialCodeTryout.DataTypes
{
    public partial class Address
    {
        public Address(string name, int number, Direction dir)
        {
            this.Name = name;
            this.Number = number;
            this.Dir = dir;
        }

        public string Name { get; }
        public int Number { get; }
        public Direction Dir { get; }
    }
}

クラスライブラリは、通常のVisual Studioクラスライブラリです。 SharedProjectをインポートしますが、その.csprojには何も特別なものは含まれていません。

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.Microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
<!-- standard Visual Studio stuff removed -->
    <OutputType>Library</OutputType>
<!-- standard Visual Studio stuff removed -->
  </PropertyGroup>
<!-- standard Visual Studio stuff removed -->
  <ItemGroup>
    <Reference Include="System" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Address.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <Import Project="..\SharedProject\SharedProject.projitems" Label="Shared" />
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Address.DirectionはSharedProjectに実装されています。

namespace SharedPartialCodeTryout.DataTypes
{
    public partial class Address
    {
        public enum Direction
        {
            NORTH,
            EAST,
            SOUTH,
            WEST
        }
    }
}

SharedProject.shprojは次のとおりです。

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.Microsoft.com/developer/msbuild/2003">
  <PropertyGroup Label="Globals">
    <ProjectGuid>33b08987-4e14-48cb-ac3a-dacbb7814b0f</ProjectGuid>
    <MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
  </PropertyGroup>
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
  <PropertyGroup />
  <Import Project="SharedProject.projitems" Label="Shared" />
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
</Project>

そして、その.projitemsは次のとおりです。

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.Microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
    <HasSharedItems>true</HasSharedItems>
    <SharedGUID>33b08987-4e14-48cb-ac3a-dacbb7814b0f</SharedGUID>
  </PropertyGroup>
  <PropertyGroup Label="Configuration">
    <Import_RootNamespace>SharedProject</Import_RootNamespace>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="$(MSBuildThisFileDirectory)Address.Direction.cs" />
  </ItemGroup>
</Project>

通常のクライアントはAddressを含み、Address.Direction

using SharedPartialCodeTryout.DataTypes;
using System;

namespace SharedPartialCodeTryout.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            // Create an Address
            Address op = new Address("Kasper", 5297879, Address.Direction.NORTH);
            // Use it
            Console.WriteLine($"Addr: ({op.Name}, {op.Number}, {op.Dir}");
        }
    }
}

通常のクライアントcsprojはクラスライブラリを参照し、SharedProjectを参照しません

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.Microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
<!-- Removed standard Visual Studio Exe project stuff -->
    <OutputType>Exe</OutputType>
<!-- Removed standard Visual Studio Exe project stuff -->
  </PropertyGroup>
<!-- Removed standard Visual Studio Exe project stuff -->
  <ItemGroup>
    <Reference Include="System" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="App.config" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\SharedPartialCodeTryout.DataTypes\SharedPartialCodeTryout.DataTypes.csproj">
      <Project>{7383254d-bd80-4552-81f8-a723ce384198}</Project>
      <Name>SharedPartialCodeTryout.DataTypes</Name>
    </ProjectReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

DbSetupは列挙型のみを使用します。

DbSetup.csprojはクラスライブラリを参照しません。 SharedProjectのみをインポートします。

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.Microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
<!-- Removed standard Visual Studio Exe project stuff -->
    <OutputType>Exe</OutputType>
<!-- Removed standard Visual Studio Exe project stuff -->
  <?PropertyGroup>
<!-- Removed standard Visual Studio Exe project stuff -->
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="Microsoft.CSharp" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="App.config" />
  </ItemGroup>
  <Import Project="..\SharedProject\SharedProject.projitems" Label="Shared" />
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

結論として:

部分クラスをプロジェクト間で分割できますか?

はい、Visual Studioの共有プロジェクトを使用します。

同じ名前空間を使用して別のプロジェクトで(依存関係を作成して)部分クラスを作成することは良い考えですか?

多くの場合、そうではありません(他の回答を参照)。状況によっては、自分が何をしているのかわかっている場合に便利です。

5

部分クラスは同じアセンブリに存在する必要があります。それ以外の場合、コンパイラは部分クラスをどこにマージするかをどのように決定しますか?

4
Kent Boogaart

このスキームが機能しない理由はわかりません。

2つのファイルには、ストレージメカニズム(またはその他の機能)が含まれています。継承を指定しますが、ビジネスロジックは含まれません。

  • ProductDataAccess.cs
  • ProductWeb.cs

1つのファイルにはビジネスロジックが含まれています。

  • ProductBusinessLogic.cs

次に、2つのプロジェクトを作成します。

  • WebProjectには、ProductWeb.csおよびProductBusinessLogic.csが含まれています。
  • DataProjectにはProductDataAccess.csとProductBusinessLogic.csが含まれています

どちらのプロジェクトも同じビジネスロジックを使用します。

1
Johan Nilsson

いいえ。異なるプロジェクトに部分クラスを書き込むことはできません。一度にコンパイラがコンパイルする単一のプロジェクトを取得し、そのプロジェクトのクラス、メソッド、フィールドなどのリストのみをスキャンするためです。したがって、部分クラスの一部が他のプロジェクト、コンパイラはそれらを見つけることができません。

0
user3363647

Linqより前の開発に関してはNeilに同意しますが、Linq2SQLデザイナーによって生成された部分クラスからビジネスロジックを分割するためにこれを実行できたらいいのにと思います。例えば:

Northind.DAL (prj)
-NorthindDataContext (EntityNamespace set to "Northwind.BLL")
--Product() (Entity, partial class auto-generated)
--Category() (Entity, partial class auto-generated)
--Supplier() (Entity, partial class auto-generated)

Northind.BLL (prj)
-Product() : IMyCustomEnityInterface, BaseEntity (override OnValidate(), etc)
-Category() : IMyCustomEnityInterface, BaseEntity (override OnValidate(), etc)
-Supplier() : IMyCustomEnityInterface, BaseEntity (override OnValidate(), etc)

残念ながら、これを行うことはできません... LINQを使用するときにレイヤー/層を分割する推奨方法を知りたいのですが。

0
user47460

ジョン・スキートの答えに同意します。

とにかく、このような問題に取り組むのは良い選択ではないと思います。コードの層/層を分割する最良の方法を示す優れた設計パターンがすでにあります。これは、MicrosoftがWinForms/WebFormsデザイナーファイルを分離して、ユーザーがそれらを壊すのを防ぐことができるようにするための、単なる構文上の砂糖です。

0
Neil Barnwell