Ich habe an einigen Stellen gelesen, dass EF bereits eigene UnitOfWork und Transaktionen implementiert.Muss ich UnitOfWork für EF Core übernehmen und wenn ja, gibt es irgendwo ein Standardmuster?
ich bei den folgenden Lösungen suchen: https://docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
Ich weiß wirklich nicht, wie das, weil ich möchte nicht hinzufügen müssen manuell in jede Art von Repo-I wie haben sich gegen den Grund das GenericRepository, an das ich bereits viel gearbeitet habe.
Suche Auch bei der UnitOfWork Attribute hier beschriebenen Lösung aber wegen der Gründe, vom Autor selbst diskutierten die Sicherung weg: Entity Framework Core 1.0 unit of work with Asp.Net Core middleware or Mvc filter
Aber lassen Sie mich versuchen, unter meiner Frage in der Diskussion um das Layout.
Ich habe einen Generic Repo und einen Generic Service. Sie sind in meinem Startup wie folgt registriert:
services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddScoped(typeof(IGenericService<>), typeof(GenericService<>));
Mein Allgemein Repo sieht wie folgt aus (Schnittstellen zur Kürze Weglassen):
public enum FilteredSource
{
All,
GetAllIncluding,
}
public class GenericRepository<T> : IGenericRepository<T>
where T: BaseEntity
{
protected readonly ApplicationDbContext _context;
protected DbSet<T> _dbSet;
public GenericRepository(ApplicationDbContext context)
{
_context = context;
_dbSet = context.Set<T>();
}
// no eager loading
private IQueryable<T> All => _dbSet.Cast<T>();
// eager loading
private IQueryable<T> GetAllIncluding(
params Expression<Func<T, object>>[] includeProperties) =>
includeProperties.Aggregate(All, (currentEntity, includeProperty) => currentEntity.Include(includeProperty));
// eager loading
public async Task<T> GetSingleIncludingAsync(
long id, params Expression<Func<T, object>>[] includeProperties)
{
IQueryable<T> entities = GetAllIncluding(includeProperties);
//return await Filter<long>(entities, x => x.Id, id).FirstOrDefaultAsync();
return await entities.SingleOrDefaultAsync(e => e.Id == id);
}
// no eager loading
public async Task<T> GetSingleIncludingAsync(long id)
{
return await _dbSet.SingleOrDefaultAsync(e => e.Id == id);
}
/// <summary>
/// Takes in a lambda selector and let's you filter results from GetAllIncluding or All.
/// </summary>
/// <param name="selector">labmda expression to filter results by.</param>
/// <param name="getFilteredSource">All or GetAllIncluding as the method to get results from.</param>
/// <param name="includeProperties">array of eager load lamda expressions.</param>
/// <returns></returns>
public async Task<IEnumerable<T>> GetFiltered(
Expression<Func<T, bool>> selector, FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
var results = default(IEnumerable<T>);
switch (filteredSource)
{
case FilteredSource.All:
results = All.Where(selector);
break;
case FilteredSource.GetAllIncluding:
results = GetAllIncluding(includeProperties).Where(selector);
break;
}
return await results.AsQueryable().ToListAsync();
}
public async Task<IEnumerable<T>> GetUnFiltered(
FilteredSource filteredSource,
Expression<Func<T, object>>[] includeProperties = null)
{
var results = default(IEnumerable<T>);
switch (filteredSource)
{
case FilteredSource.All:
results = All;
break;
case FilteredSource.GetAllIncluding:
results = GetAllIncluding(includeProperties);
break;
}
return await results.AsQueryable().ToListAsync();
}
public async Task<T> InsertAsync(T entity)
{
if (entity == null)
{
throw new ArgumentNullException($"No {nameof(T)} Entity was provided for Insert");
}
await _dbSet.AddAsync(entity);
return entity;
}
public async Task<T> UpdateAsync(T entity)
{
T entityToUpdate = await
_dbSet.AsNoTracking().SingleOrDefaultAsync(e => e.Id == entity.Id);
if (entityToUpdate == null)
{
//return null;
throw new ArgumentNullException($"No {nameof(T)} Entity was provided for Update");
}
_dbSet.Update(entity);
return entity;
}
public async Task<T> DeleteAsync(T entity)
{
_dbSet.Remove(entity);
return await Task.FromResult(entity);
}
public Task SaveAsync() => _context.SaveChangesAsync();
}
Service Layer sieht wie folgt aus:
Ein Beispiel für einen MVC Core Web API-Controller, der den Dienst verwendet, sieht folgendermaßen aus:
[Route("api/[controller]")]
public class EmployeesController : Controller
{
private IGenericService<Employee> _genericService;
public EmployeesController(IGenericService<Employee> genericService)
{
_genericService = genericService;
}
// GET: api/employees
[HttpGet]
public async Task<IEnumerable<Employee>> GetEmployeesAsync(
string firstName = null, string lastName = null)
{
return await _genericService.GetFiltered(
e => (string.IsNullOrEmpty(firstName) || e.FirstName.Contains(firstName))
&& (string.IsNullOrEmpty(lastName) || e.LastName.Contains(lastName)),
FilteredSource.GetAllIncluding,
new Expression<Func<Employee, object>>[] { a => a.Organization,
b => b.PayPlan,
c => c.GradeRank,
d => d.PositionTitle,
e => e.Series,
f => f.BargainingUnit }
);
}
// GET api/employees/5
[HttpGet("{id}", Name = "GetEmployeeById")]
public async Task<IActionResult> GetEmployeeByIdAsync(long id)
{
var employee = await _genericService.GetSingleIncludingAsync(id,
a => a.Organization,
b => b.PayPlan,
c => c.GradeRank,
d => d.PositionTitle,
e => e.Series,
f => f.BargainingUnit);
if (employee == null)
{
return NotFound();
}
else
{
return new ObjectResult(employee);
}
}
// PUT api/employees/id
[HttpPut("{id}")]
public async Task<IActionResult> PutEmployeeAsync([FromBody] Employee emp)
{
var employee = await _genericService.UpdateAsync(emp);
if (employee == null)
{
return NotFound();
}
return new ObjectResult(employee);
}
}
hier sind also meine Fragen: So wie ich es verstehe, UnitOfWork verwendet wird, falls Sie in zwei Repositorys in einen Dienst oder Controller bringen. Wenn Sie Daten in Repo 1 manipulieren und es passiert und dann Daten in Repo 2 manipuliert und es fehlschlägt (oder umgekehrt), möchten Sie alles zurückrollen.
Bis jetzt verwende ich nicht zwei Repos. Aber wenn diese Situation auftritt, wird es sein, weil ich zwei GenericServices zu einem Controller bringe, der wiederum zwei GenericRepos einbringt.
Jetzt wollen wir über Umfang sprechen. Ich muss meine GenericRepo als Scoped einbringen. Nicht Singleton. Wenn ich ein Objekt bei einer Anfrage abfrage und dann versuche, dieses Objekt bei der nächsten Anfrage zu aktualisieren, erhalte ich den Fehler, dass das Objekt nicht aktualisiert werden kann, weil es bereits von der vorherigen Anfrage vom Singleton Repo verfolgt wird. So bringe ich es in Scoped wie im StartUp-Segment gezeigt:
services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddScoped(typeof(IGenericService<>), typeof(GenericService<>));
ich als scoped auch in den Dienst bringen. Ziemlich viel raten sollte ich es auch als Scoped einbringen.
Wenn ich jetzt einen GenericService vom Typ Employee -> der ein GenericRepository vom Typ Employee in einen Controller bringt und ich auch GenericService vom Typ Case einbringe -> welches ein GenericRepository vom Type Case bekommt, sind diese beiden verschieden GenerischeRepos? Oder sind sie das gleiche Repo? Werden sie als dieselbe Transaktion behandelt und alle bestehen oder versagen?
Oder muss ich UnitOfWork manuell implementieren?
Ein weiterer Faktor, ich denke, dass geht in diese, ob die in Core-DI Linie gebacken wird nach Singleton oder Scoped:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyConn")));
Wie ich Ihre Frage, meinen Standpunkt zu verstehen ist: Nein, Sie brauchen nicht zu UOW auf implementieren. NET Core, weil es besser ist, die Controller und Business-Objekte oder Repositories nach Features zu gestalten: HumanResources statt Employee, Sales statt Order. Auch Uow ist ein Objekt, das Änderungen erlaubt. Wenn Sie also Ihre Repositories haben, können Sie Änderungen im Action Controller festschreiben –
Im Allgemeinen sind Singletons böse, besonders in einer Webanwendung. https://duckduckgo.com/singletonitis –