【问题标题】:Generic WCF Service通用 WCF 服务
【发布时间】:2017-05-15 14:46:46
【问题描述】:

我知道关于这个主题有很多有答案和没有答案的问题,但似乎没有一个与我的问题完全匹配。

我正在尝试为我的项目实体编写管理器服务。对于主要的、大的和重要的,我已经完成了,但是业务包含很多描述符(只有 ID 和名称的实体,其他实体使用),我也希望能够将它们作为服务进行管理。

一种解决方案是为它们中的每一个设置一个特定的管理器,但由于描述符的数量,这肯定会破坏可维护性。于是我写了2个接口如下

public interface IIdentifiableEntity
{
    string EntityId { get; set; }
}

public interface IDescriptorEntity: IIdentifiableEntity
{
    string EntityDesc { get; set; }
}

这是由于我的主要实体实现了 IIdentifiableEntity 而我的描述符实现了 IDescriptorEntity

这是我的描述符实体之一的示例:

public class JobCode: IDescriptorEntity
{
    [DataMember]
    public string JobCodeID { get; set; }
    [DataMember]
    public string JobCodeDesc { get; set; }

    public string EntityId
    {
        get
        {
            return JobCodeID;
        }

        set
        {
            JobCodeID = value;
        }
    }

    public string EntityDesc
    {
        get
        {
            return JobCodeDesc;
        }

        set
        {
            JobCodeDesc = value;
        }
    }
}

然后我写了我的服务合同如下:

public interface IDescriptorService : IServiceContract
{
    [OperationContract]
    [FaultContract(typeof(NotFoundException))]
    T Get<T>(string id) where T : class, IDescriptorEntity, new();

    [OperationContract]
    T[] GetAll<T>() where T : class, IDescriptorEntity, new();

    [OperationContract]
    [TransactionFlow(TransactionFlowOption.Allowed)]
    T Add<T>(T descriptor) where T : class, IDescriptorEntity, new();

    [OperationContract]
    [TransactionFlow(TransactionFlowOption.Allowed)]
    T Update<T>(T descriptor) where T : class, IDescriptorEntity, new();

    [OperationContract]
    [TransactionFlow(TransactionFlowOption.Allowed)]
    void Delete<T>(string id) where T : class, IDescriptorEntity, new();
}

和我的经理(服务本身)如下:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall,
    ConcurrencyMode = ConcurrencyMode.Multiple,
    ReleaseServiceInstanceOnTransactionComplete = false)]
[Export(typeof(IDescriptorService))]
public class DescriptorManager : ManagerBase, IDescriptorService
{
    public DescriptorManager()
    {
        ObjectBase.Container.SatisfyImportsOnce(this);
    }

    #region No MEF Discovery constructors
    public DescriptorManager(IDataRepositoryFactory dataRepositoryFactory)
    {
        _DataRepositoryFactory = dataRepositoryFactory;
    }
    #endregion

    [Import]
    IDataRepositoryFactory _DataRepositoryFactory;

    [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
    public TEntityType Get<TEntityType>(string id) where TEntityType : class, IDescriptorEntity, new() => ExecuteFaultHandledOperation(() =>
    {
        IDataRepository<TEntityType> repository = _DataRepositoryFactory.GetDataRepositoryOf<TEntityType>();

        TEntityType entity = repository.Get(id);

        if (entity == null)
        {
            NotFoundException ex
                = new NotFoundException(string.Format("{0} with ID of {1} is not in the database", typeof(TEntityType).Name, id));

            throw new FaultException<NotFoundException>(ex, ex.Message);
        }

        return entity;
    });

    [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
    public TEntityType[] GetAll<TEntityType>() where TEntityType : class, IDescriptorEntity, new() => ExecuteFaultHandledOperation(() =>
    {
        IDataRepository<TEntityType> repository = _DataRepositoryFactory.GetDataRepositoryOf<TEntityType>();

        IEnumerable<TEntityType> entities = repository.Get();

        return entities.ToArray();
    });

    [OperationBehavior(TransactionScopeRequired = true)]
    [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
    public TEntityType Update<TEntityType>(TEntityType entity) where TEntityType : class, IDescriptorEntity, new() => ExecuteFaultHandledOperation(() =>
    {
        IDataRepository<TEntityType> repository = _DataRepositoryFactory.GetDataRepositoryOf<TEntityType>();

        TEntityType updatedEntity = repository.Update(entity);

        return updatedEntity;
    });

    [OperationBehavior(TransactionScopeRequired = true)]
    [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
    public TEntityType Add<TEntityType>(TEntityType entity) where TEntityType : class, IDescriptorEntity, new() => ExecuteFaultHandledOperation(() =>
    {
        IDataRepository<TEntityType> repository = _DataRepositoryFactory.GetDataRepositoryOf<TEntityType>();

        TEntityType updatedEntity = repository.Add(entity);

        return updatedEntity;
    });

    [OperationBehavior(TransactionScopeRequired = true)]
    [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
    public void Delete<TEntityType>(string id) where TEntityType : class, IDescriptorEntity, new() => ExecuteFaultHandledOperation(() =>
    {
        IDataRepository<TEntityType> repository = _DataRepositoryFactory.GetDataRepositoryOf<TEntityType>();

        repository.Remove(id);
    });
}

并将托管应用程序作为控制台应用程序如下:

class Program
{
    static void Main(string[] args)
    {
        ObjectBase.Container = MEFLoader.Init();

        Console.WriteLine("Starting up services...");
        Console.WriteLine("");

        SM.ServiceHost hostDescriptorManager = new SM.ServiceHost(typeof(DescriptorManager));
        SM.ServiceHost hostPositionManager = new SM.ServiceHost(typeof(PositionManager));
        SM.ServiceHost hostEmployeeManager = new SM.ServiceHost(typeof(EmployeeManager));

        StartService(hostDescriptorManager, "DescriptorManager");
        StartService(hostPositionManager, "PositionManager");
        StartService(hostEmployeeManager, "EmployeeManager");

        Console.WriteLine("");
        Console.WriteLine("Press [Enter] to exit.");
        Console.ReadLine();

        StopService(hostDescriptorManager, "DescriptorManager");
        StopService(hostPositionManager, "PositionManager");
        StopService(hostEmployeeManager, "EmployeeManager");
    }

    static void StartService(SM.ServiceHost host, string serviceDescription)
    {
        host.Open();
        Console.WriteLine("Service {0} started", serviceDescription);

        foreach (var endpoint in host.Description.Endpoints)
        {
            Console.WriteLine("Listening on endpoints:");
            Console.WriteLine("Adress: {0}", endpoint.Address.Uri);
            Console.WriteLine("Binding: {0}", endpoint.Binding.Name);
            Console.WriteLine("Contract: {0}", endpoint.Contract.Name);
        }

        Console.WriteLine();
    }

    static void StopService(SM.ServiceHost host, string serviceDescription)
    {
        host.Close();
        Console.WriteLine("Service {0} stopped.", serviceDescription);
    }
}

我还想要一些组合和 DI(我正在使用 MEF 组合整个应用程序),因此我将我的实体(包括描述符)放在我加载的单独程序集中。因此,在我的类和接口中直接硬编码“KnownType”是行不通的。

所以我尝试关注this example from MSDN 并将已知类型写入配置文件(我可以稍后更改以保持我的组合)。在我尝试将 JobCode 描述符添加为 KnownType 的部分配置之后。

<system.runtime.serialization>
<dataContractSerializer>
  <declaredTypes>
    <add type="Core.Common.Contracts.IDescriptorEntity,Core.Common.Contracts">
      <knownType type="XXXX.Business.Entities.JobCode,XXXX.Business.Entities"/>
    </add>
  </declaredTypes>
</dataContractSerializer>

然而,当我尝试运行主机时,在 IDescriptorService 接口的第一个方法中再次出现“Open Generics”错误。

我做错了什么?

【问题讨论】:

    标签: c# entity-framework wcf generics


    【解决方案1】:

    一种解决方案是为每个人指定一个特定的管理器,但由于描述符的数量,这肯定会破坏可维护性

    这确实是正确的解决方案。 WCF 服务必须有一个契约,告诉客户端期望的实体形状。您编写一个 T4 模板来生成所有服务合同和服务类。

    退一步说,在您的实体上为 CRUD 提供 Web 服务并不是一个很好的做法。您通常最好直接访问实体模型,而不是跨 Web 服务。它显然要简单得多,并且性能要好得多。跨网络服务访问您的实体并不能真正完成任何有用的事情。

    【讨论】:

    • 我不同意“没有完成任何事情”部分。也许我真的可以在没有“创建”、“更新”和“删除”服务的情况下生活,但服务中的“获取”允许我提供对我的实体的访问,这些应用程序甚至没有由我的组编码,而无需为他们提供对数据库或他们可能直接或间接使用的类的访问权限(在此处阅读继承)来打乱我的数据。无论如何,我的问题实际上没有解决方案?如果是这样,那么 KnownTypeAttribute 根本就没有用吗?
    • 您是否考虑过使用 OData 代替 SOAP?使用 OData,您可以一次发布整个模型,而不是发布单个服务操作。 KnownTypeAttribute 只是告诉 WCF 生成与您的服务合同中引用的类型不同的类型对应的 WSDL。
    猜你喜欢
    • 2011-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-31
    • 2011-11-21
    • 2010-11-19
    • 1970-01-01
    相关资源
    最近更新 更多