【发布时间】:2019-04-03 22:28:26
【问题描述】:
我正在使用Microsoft.EntityFrameworkCore.DbQuery 从我的数据库中的表值函数的结果中返回 POCO。我注意到我的所有 POCO 都没有设置任何属性——它们都是默认值——只要它们的访问修饰符不是public。就我而言,POCO 都明确地实现了接口,因此消费者不(或不应该)知道任何关于 POCO 定义的信息,因此我想将它们设置为 internal。
为什么 POCO 必须公开?或者,有没有办法让它不必公开?下面是一些示例代码来证明这一点:
CoreLibrary.dll (.NET Standard 2.0)
using System.Collections.Generic;
namespace CoreLibrary
{
public interface IWidgetRepository
{
IReadOnlyCollection<IWidget> FindAllWidgets();
IReadOnlyCollection<IWidget> FindWidgets(string withText);
}
public interface IWidget
{
int ID { get; }
string Name { get; }
string Value { get; }
}
}
DataAccess.dll (.NET Standard 2.0)
using CoreLibrary;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
namespace DataAccess
{
public sealed class WidgetRepositoryApi : IWidgetRepository
{
private DatabaseContext _dbContext;
public WidgetRepositoryApi(string databaseServerName, string databaseName)
=> _dbContext = new DatabaseContext(databaseServerName, databaseName);
public IReadOnlyCollection<IWidget> FindAllWidgets()
=> _dbContext.Widget.ToList();
public IReadOnlyCollection<IWidget> FindWidgets(string withText)
{
string sqlQuery = "SELECT * FROM dbo.fn_SearchWidgetText(@Text)";
var ret = _dbContext.GetWidgetFullTextSearchMatches
.FromSql(sqlQuery, new SqlParameter("@Text",
System.Data.SqlDbType.NVarChar) { Value = withText });
return ret.ToList().AsReadOnly();
}
}
internal partial class DatabaseContext : DbContext
{
private string _dbConnectionString;
internal DatabaseContext(string databaseServerName, string databaseName)
=> _dbConnectionString =
$"Server={databaseServerName};Database={databaseName};Trusted_Connection=True;";
internal virtual DbSet<DBSetWidget> Widget { get; set; }
internal virtual DbQuery<DBQueryWidget> GetWidgetFullTextSearchMatches { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(_dbConnectionString);
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DBSetWidget>(entity =>
{
entity.Property(e => e.Id).HasColumnName("ID");
entity.Property(e => e.Name)
.IsRequired()
.HasMaxLength(50);
entity.Property(e => e.Value).IsRequired();
});
}
}
/********************************************************************
* NOTE: there need to be two widget implementations (POCOs)
* because otherwise the code throws
* System.InvalidCastException: 'Unable to cast object of type
* 'Microsoft.EntityFrameworkCore.Internal.
* InternalDbQuery`1[DataAccess.DBSetWidget]' to type
* 'Microsoft.EntityFrameworkCore.DbSet`1[DataAccess.DBSetWidget]'.'
*******************************************************************/
internal partial class DBQueryWidget : IWidget
{
internal int Id { get; set; }
internal string Name { get; set; }
internal string Value { get; set; }
int IWidget.ID => Id;
string IWidget.Name => Name;
string IWidget.Value => Value;
}
internal partial class DBSetWidget : IWidget
{
internal int Id { get; set; }
internal string Name { get; set; }
internal string Value { get; set; }
int IWidget.ID => Id;
string IWidget.Name => Name;
string IWidget.Value => Value;
}
}
ConsumerConsoleApp.exe (.NET Core 2.1)
using DataAccess;
namespace ConsumerConsoleApp
{
class Program
{
static void Main(string[] args)
{
var repo = new WidgetRepositoryApi(args[0], args[1]);
var allWidgets = repo.FindAllWidgets();
var someWidgets = repo.FindWidgets("facebook");
}
}
}
SQL 数据库中的小部件表
CREATE TABLE [dbo].[Widget](
[ID] [INT] IDENTITY(1,1) NOT NULL,
[Name] [NVARCHAR](50) NOT NULL,
[Value] [NVARCHAR](MAX) NOT NULL,
CONSTRAINT [PK_Widget] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
数据库中的 fn_SearchWidgetText
CREATE FUNCTION [dbo].[fn_SearchWidgetText]
(
@Text NVARCHAR(1000)
)
RETURNS TABLE
AS
RETURN
(
SELECT *
FROM dbo.Widget
WHERE CONTAINS(Value, @Text)
)
如果我将我的 DataAccess 命名空间中的所有访问修饰符更改为 public,现在 POCO 将被正确填充:
【问题讨论】:
-
他们真的不必公开。请显示未设置实体对象属性的示例。我无法想象 EF 如何在不设置属性值的情况下实现实体对象。
-
我怎么能证明这一点?你需要数据库来测试它。
-
如果只有一个类 + 接口 + 映射组合的行为方式与您描述的方式相同,那将会很有趣。为了测试,我们可以让 EF 生成一个数据库。
-
@GertArnold -- 我已经玩过它并缩小了一些范围。请查看更新。
-
您将过多的 OOP 放入本应是简单存储模型的东西中。如果您想使用 EF 通用处理此类实体(例如具有接口约束的通用方法),则必须隐式(公开)实现接口。由于您的课程是
internal,我不明白为什么它们的属性不应该是public。internal对internal类的属性的修饰符不会使它们更具内部性。
标签: .net .net-core entity-framework-core .net-standard access-modifiers