I looking for some help on how to implement IN clause in the repository pattern. Rather than making single call for each and every record, I will have set of IDs, pass on this IDs to Context to get entities which satisfies the condition using Repository Pattern with EF.
I knew we can have something like this:
context.Students.Where( x => StudentIDs.contains(x.ID))
How to implement same in the repository layer or pattern with single call to DB?
If you really are a purist, yes you should abstract the DbContext entirely as you seem to imply.
I'm not sure I completely understand the issue, but something like that should do the job:
namespace EFRepo
{
class Student
{
public long Id { get; set; }
public string Name { get; set; }
}
class SchoolContext : DbContext
{
public DbSet<Student> Students { get; set; }
}
class SchoolRepository
{
private SchoolContext context = new SchoolContext();
public Student Add(string name)
{
Student student = new Student { Name = name };
context.Students.Add(student);
context.SaveChanges();
return student;
}
public IEnumerable<Student> GetStudentsByIds(IEnumerable<long> ids)
{
return context.Students.Where(x => ids.Contains(x.Id));
}
}
class Program
{
static void Main(string[] args)
{
SchoolRepository repo = new SchoolRepository();
repo.Add("Bully");
repo.Add("Crawler");
repo.Add("Tart");
foreach (Student s in repo.GetStudentsByIds(new[] { 1L, 3 }))
{
Console.WriteLine(s.Name);
}
}
}
}
Related
I have this model:
And I want to add a new Autor like below:
class Program
{
static void Main(string[] args)
{
try
{
AutorServiceClient service = new AutorServiceClient();
LivroContract[] livros = {
new LivroContract { id_tipo = 1, nome_livro = "Asp.Net MVC 5" },
new LivroContract { id_tipo = 1, nome_livro = "Asp.Net Entity Framework" }
};
AutorContract autorContract = new AutorContract()
{
nome_autor = "Novo Autor",
Livros = livros
};
if (service.Add(autorContract))
Console.WriteLine("Adicionado com Sucesso");
}
catch (Exception)
{
Console.WriteLine("Erro !!!");
}
}
}
My Autor class has a nested Livro collection and I want to insert a new Autor with its respective Livro entities.
Below is part of my Data Access code to make the insert:
public class AutorDA
{
private readonly BibliotecaEntities _context;
private readonly DbSet<Autor> _dbSet;
public AutorDA()
{
_context = new BibliotecaEntities();
_dbSet = _context.Set<Autor>();
Mapping();
}
public void Mapping()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Autor, AutorDTO>();
cfg.CreateMap<AutorDTO, Autor>();
cfg.CreateMap<ICollection<Autor>, IEnumerable<AutorDTO>>();
cfg.CreateMap<IEnumerable<AutorDTO>, ICollection<Autor>>();
cfg.CreateMap<Biblioteca, BibliotecaDTO>();
cfg.CreateMap<BibliotecaDTO, Biblioteca>();
cfg.CreateMap<ICollection<Biblioteca>, IEnumerable<BibliotecaDTO>>();
cfg.CreateMap<IEnumerable<BibliotecaDTO>, ICollection<Biblioteca>>();
cfg.CreateMap<Livro, LivroDTO>();
cfg.CreateMap<LivroDTO, Livro>();
cfg.CreateMap<ICollection<Livro>, IEnumerable<LivroDTO>>();
cfg.CreateMap<IEnumerable<LivroDTO>, ICollection<Livro>>();
cfg.CreateMap<Tipo_Livro, TipoLivroDTO>();
cfg.CreateMap<TipoLivroDTO, Tipo_Livro>();
cfg.CreateMap<ICollection<Tipo_Livro>, IEnumerable<TipoLivroDTO>>();
cfg.CreateMap<IEnumerable<TipoLivroDTO>, ICollection<Tipo_Livro>>();
});
}
public bool Add(AutorDTO dto)
{
try
{
Mapping();
Autor autor = Mapper.Map<Autor>(dto);
_dbSet.Add(autor);
_context.SaveChanges();
return true;
}
catch (Exception ex)
{
throw ex;
}
}
....
Here is my DTOs:
public class AutorDTO
{
public int id { get; set; }
public string nome_autor { get; set; }
public IEnumerable<LivroDTO> Livros { get; set; }
}
public class LivroDTO
{
public int id { get; set; }
public int id_tipo { get; set; }
public string nome_livro { get; set; }
public ICollection<AutorDTO> Autores { get; set; }
public ICollection<BibliotecaDTO> Bibliotecas { get; set; }
public TipoLivroDTO TipoLivro { get; set; }
}
I know that there is something wrong here, but I dont know what... I am trying to insert Autor and a couple of Livros entities, but I dont know how to do that, using AutoMapper and EF.
But with this code, I am only inserting Autor.
So, I have two situations I dont know how to do:
Insert a new Autor and new Livro entities
Insert a new Autor and associate it with already inserted Livro entities
How I configure AutoMapper to those two situations above ?
Finally, my last questions are:
What is the better approach for situations where we have a main entity, which has one or more child entities (1:n / n:n) ?
Is it a good idea to insert/update simultanneosly all those entities, or it is a bad idea ? If it is a bad idea, so what is best way to do insert a main class and its relations ?
As we can see, my Livro entity has other child relations, but I want to use only Autor and Livro. I need to map all model/entities just to use those two ones ?
Thanks.
I am using Entity Framework and implementing the Repository pattern. Every example that I've been of adding new objects is something like this:
class MyRepository
{
public MyContext Context { get; set; }
public Add(MyObject myObject)
{
this.Context.MyObjects.Add(myObject);
}
public Save()
{
this.Context.SaveChanges();
}
}
// A window which lets the user add items to the repository
class MyWindow
{
private MyRepository Repository { get; set; }
private void DoSomething()
{
List<MyClass> myObjects = this.Repository.GetMyObjects();
// When I create a new object, I have to add the new object to the myObjects list and separately to the repository
MyClass newObject = new MyClass();
myObjects.Add(newObject);
this.Repository.Add(newObject);
// Do stuff to the objects in "myObjects"
this.Repository.Save();
}
}
What I want to be able to do is add new objects to the myObjects list (without having to add them to the repository on a separate line), and then just call something like this.Repository.Save(myObjects) when I'm ready to save them. Having to explicitly add every new object to the repository seems to break up the separation-of-concerns model. Is there a recommended way to do this, or is my reasoning flawed?
EDIT: DDiVita - I'm not sure what you mean by "attaching the entities to the context". This is what I'm currently doing in my Repository class:
public List<MyObject> GetMyObjects()
{
return this.Context.MyObjects.ToList();
}
Then in my Context class:
class MyContext : Context
{
public DbSet<MyObject> MyObjects { get; set; }
}
You can use the AddOrUpdate extension (the link is for Version 6 of EF) method on the DbSet. With this you can specify an identifier that EF will recognize as a unique value to either update or add the entity.
Let's assume your entity MyObject looks like this and the Id is always unique in your database:
public class MyObject
{
public int Id { get; set; }
public string Name { get; set; }
}
public Save(List<MyObject> myObjects)
{
this.Context.MyObjects.AddOrUpdate(m => m.Id,myObjects.ToArray());
this.Context.SaveChanges();
}
What you could do is use AddRange
public Save(List<MyObject> myObjects)
{
this.Context.MyObjects.AddRange(myObjects);
this.Context.SaveChanges();
}
And then your code could look like this
private void DoSomething()
{
List<MyObject> myObjects = this.Repository.GetMyObjects();
MyObject newObject = new MyObject();
myObjects.Add(newObject);
// Do stuff to the objects in "myObjects"
this.Repository.Save(myObjects);
}
I have an EF6 setup against a sql server db with about 60 tables in it.
I have entities for each table. What i'm trying to do is run the same method against a set of these entities that will be known at runtime.
The method is a qa/qc routine that does some data check on particular fields that are assured to be in each table.
I guess what i want to do is make the entity a parameter to the method so i can call it consecutive times.
I would also want to make a set of entities to pass as the parameter.
something like this:
List<string> entList = new List<string>(){"Table1","Table2","Table3"};
foreach (entName in entList)
{
//create an entity with the string name
//call myQAQCMethod with the entity
}
MyQAQCMethod (entity SomeEntity)
{
//run against this entity
doQAQC(SomeEntity);
}
Can this be done? Is it a job for reflection?
EDIT
using (var context = new Context())
{
var results = context.EntityAs.Where(a => a.Prop1 == e.Prop1)
.Where(a => a.Prop2 == e.Prop2)
.Select(a => new
{
APropertyICareAbout = a.Prop1,
AnotherPropertyICareAbout = a.Prop2
}).ToArray();
}
is precisely want i want to do. The thing is I want to avoid typing this loop 60 times. I think i'm looking for a way to "feed" a set of entities to this single method.
Also, thank you very much for helping me. I'm learning a lot.
You need to abstract an interface (entity framework won't even notice):
interface IQaQcable
{
int CommonInt { get; set; }
string CommonString { get; set; }
}
public class EntityA : IQaQcable
{
public int Id { get; set; }
public int CommonInt { get; set; }
public string CommonString { get; set; }
// other properties and relations
}
public class EntityB : IQaQcable
{
public int Id { get; set; }
public int CommonInt { get; set; }
public string CommonString { get; set; }
// other properties and relations
}
// in some unknown utility class
void MyQaQcMethod<T>(T entity) where T : IQaQcable
{
doSomethingWithIQaQcableProperties(entity.CommonInt, entity.CommonString);
}
// in some unknown test class
void Test()
{
var entities = new List<IQaQcable> { new EntityA(), new EntityB() };
foreach (var e in entities)
MyQaQcMethod(e);
}
Now, you could extract a base class from which each derives that actually implements the CommonInt and CommonString properties for each entity needing them, but that can get kind of tricky with Table-Per-Type/Table-Per-Hierarchy, so I'd start with this, and then consider introducing either an abstract or concrete base class as an improvement.
EDIT
Maybe your looking for something simpler than I first thought, based on your last comment.
Let's give ourselves what the DbContext for this might look like:
class Context : DbContext
{
public virtual DbSet<EntityA> EntityAs { get; set; }
public virtual DbSet<EntityB> EntityBs { get; set; }
}
So, it could just be that you wish to do this:
using (var context = new Context())
{
var results = context.EntityAs.Where(a => a.Prop1 == e.Prop1)
.Where(a => a.Prop2 == e.Prop2)
.Select(a => new
{
APropertyICareAbout = a.Prop1,
AnotherPropertyICareAbout = a.Prop2
}).ToArray();
}
Keeping in mind, if there is some set of properties in common across entity classes, you could still do something like the following:
IEnumerable<T> MyQaQcMethod(IQueryable<T> entities, T referenceEntity) where T : IQaQcAble
{
return entities.Where(e => SomePredicate(e, referenceEntity));
}
void Test()
{
using (var context = new Context())
{
// EntityA implements IQaQcAble
var resultsForA = MyQaQcMethod(context.EntityAs, defaultEntity).ToArray();
// so does EntityB, so can call with either
var resultsForB = MyQaQcMethod(context.EntityBs, defaultEntity).ToArray();
}
}
Keep in mind, to avoid modifying the generated entity classes, you could implement the interface members — and the interface — in a separate source file using partial classes. E.g.
// IQaQcAble.cs
internal interface IQaQcAble
{
int CommonInt { get; set; }
string CommonString { get; set; }
}
// a class whose existing property names match the interface
public partial class EntityA : IQaQcAble
{
int IQaQcAble.CommonInt
{
get { return CommonInt; }
set { CommonInt = value; }
}
string IQaQcAble.CommonString
{
get { return CommonString; }
set { CommonString = value; }
}
}
// a class whose property names differ
public partial class EntityB : IQaQcAble
{
int IQaQcAble.CommonInt
{
get { return SomeOtherInt; }
set { SomeOtherInt = value; }
}
string IQaQcAble.CommonString
{
get { return SomeOtherInt.ToString(); }
set { SomeOtherInt = Convert.ToInt32(value); }
}
}
Edit Is this post lacking sufficient information to get some guidance?
I have this method to insert an entity into the database:
public void Insert(T entity)
{
_context.Set<T>().Add(entity);
_context.SaveChanges();
}
When I inspect entity before adding it to the context, my CustomerRole field is there. Once the add has taken place, the context doesn't seem to have it. Because of this, I am receiving this error:
Entities in 'CcDataContext.Customers' participate in the
'Customer_CustomerRole' relationship. 0 related
'Customer_CustomerRole_Target' were found. 1
'Customer_CustomerRole_Target' is expected.
These images show what I mean:
Inspecting my entity
Inspecting the context
Can anyone explain this behaviour and what I can do about it?
This is the structure of my classes (cut down for brevity):
public class Customer : BaseEntity
{
public CustomerRole CustomerRole { get; set; }
}
class CustomerMap : EntityTypeConfiguration<Customer>
{
public CustomerMap()
{
HasRequired(t => t.CustomerRole)
.WithMany(t => t.Customers);
}
}
public class CustomerRole : BaseEntity
{
private ICollection<Customer> _customers;
public ICollection<Customer> Customers
{
get { return _customers ?? (new List<Customer>()); }
set { _customers = value; }
}
}
I can confirm that customer map is being added to the configuration and my database is built in line with them.
This is the call I am making which does the insert:
public Customer InsertGuestCustomer()
{
var customer = new Customer();
CustomerRole guestRole = GetCustomerRoleByName("Guest");
if (guestRole == null)
throw new Exception("Customer Role is not defined!");
customer.UserName = "";
customer.EmailAddress = "";
customer.Password = "";
customer.IsAdmin = false;
customer.CustomerRole = guestRole;
_customerRepository.Insert(customer);
return customer;
}
I have no other data in my database, this would be the first customer record and only one CustomerRole. My Customer table has a Foreign Key pointing to my CustomerRole.Id table / column.
Mark your navigation properties as virtual and initialize the collection property in the entity constructor rather than from the property getter.
public class Customer : BaseEntity
{
public virtual CustomerRole CustomerRole { get; set; }
}
...
public class CustomerRole : BaseEntity
{
public CustomerRole()
{
Customers = new List<Customer>();
}
public virtual ICollection<Customer> Customers { get; protected set; }
}
In your Customers property, you were returning a new List in the getter when the backing field was null, but you never assigned this to your backing field.
I am in the process of creating a domain model and would like to have a "BaseEntity" class with an "Id" property (and some other audit tracking stuff). The Id property is the primary key and each Entity in my Domain Model will inherit from the BaseEntity class. Pretty straightforward stuff.....
public class BaseEntity
{
[Key]
public int Id { get; set; }
public DateTime LastUpdate { get; set; }
public string LastUpdateBy { get; set; }
}
public class Location : BaseEntity
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
Using the example above, I would like to map the "Id" field to a "LocationId" column. I understand that I can use the modelBuilder to do this for each entity explicitly by doing something like this:
modelBuilder.Entity<Location>().Property(s => s.Id).HasColumnName("LocationId");
But I would like to do this for every Entity in my domain model and it would be ugly.
I tried the following bit of reflection but did not have any luck. For whatever reason, the compiler "cannot resolve symbol type":
foreach (var type in GetTypesInNamespace(Assembly.Load("Domain.Model"),"Domain.Model"))
{
modelBuilder.Entity<type>().Property(x=>x.Id).....
}
Is there a way to define a convention to override the default PrimaryKey convention to map my "Id" property to a "ClassNameId" property in the database? I am using Entity Framework 6.
You should take a look at Custom Code First Conventions. You need EF6 for it to work, but it looks like you're already using it.
Just to give you an overview, take a look at the following convention I've used to convert PascalCase names to underscore names. It includes a convention for id properties... It also includes an optional table name prefix.
public class UnderscoreNamingConvention : IConfigurationConvention<PropertyInfo, PrimitivePropertyConfiguration>,
IConfigurationConvention<Type, ModelConfiguration>
{
public UnderscoreNamingConvention()
{
IdFieldName = "Id";
}
public string TableNamePrefix { get; set; }
public string IdFieldName { get; set; }
public void Apply(PropertyInfo propertyInfo, Func<PrimitivePropertyConfiguration> configuration)
{
var columnName = propertyInfo.Name;
if (propertyInfo.Name == IdFieldName)
columnName = propertyInfo.ReflectedType.Name + IdFieldName;
configuration().ColumnName = ToUnderscore(columnName);
}
public void Apply(Type type, Func<ModelConfiguration> configuration)
{
var entityTypeConfiguration = configuration().Entity(type);
if (entityTypeConfiguration.IsTableNameConfigured) return;
var tableName = ToUnderscore(type.Name);
if (!string.IsNullOrEmpty(TableNamePrefix))
{
tableName = string.Format("{0}_{1}", TableNamePrefix, tableName);
}
entityTypeConfiguration.ToTable(tableName);
}
public static string ToUnderscore(string value)
{
return Regex.Replace(value, "(\\B[A-Z])", "_$1").ToLowerInvariant();
}
}
You use it like this
modelBuilder.Conventions.Add(new UnderscoreNamingConvention { TableNamePrefix = "app" });
EDIT: In your case, the Apply method should be something like this:
public void Apply(PropertyInfo propertyInfo, Func<PrimitivePropertyConfiguration> configuration)
{
if (propertyInfo.Name == "Id")
{
configuration().ColumnName = propertyInfo.ReflectedType.Name + "Id";
}
}
Try this out in your DbContext class;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<int>()
.Where(p => p.Name.Equals("Id"))
.Configure(c => c.HasColumnName(c.ClrPropertyInfo.ReflectedType.Name + "Id"));
}
int is the CLR Type of my Primary Key fields. I want to refer to all keys in code as Id but DBA's require keys to be Id with Table entity name prefix. Above gives me exactly what I want in my created database.
Entity Framework 6.x is required.
In Entity Framework 6 Code First:
modelBuilder.Entity<roles>().Property(b => b.id).HasColumnName("role_id");
and update-database...
Change in model
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long id { get; set; }
to:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long role_id { get; set; }
Then remove this:
//modelBuilder.Entity<roles>().Property(b => b.id).HasColumnName("role_id");
A start to the Dynamic approach if NOT using custom conventions
modelBuilder.Entity<Location>().Property(s => s.Id).HasColumnName("LocationId");
You can do this using reflection on the context. Pseudo Code as explanation:
Reflect Context to get a list of POCO names
For each POCO in a dbcontext.
Map Property Id -> string PocoName+Id
Here are the extensions I use for this type of solution.
// DBSet Types is the Generic Types POCO name used for a DBSet
public static List<string> GetModelTypes(this DbContext context) {
var propList = context.GetType().GetProperties();
return GetDbSetTypes(propList);
}
// DBSet Types POCO types as IEnumerable List
public static IEnumerable<Type> GetDbSetPropertyList<T>() where T : DbContext {
return typeof (T).GetProperties().Where(p => p.PropertyType.GetTypeInfo()
.Name.StartsWith("DbSet"))
.Select(propertyInfo => propertyInfo.PropertyType.GetGenericArguments()[0]).ToList();
}
private static List<string> GetDbSetTypes(IEnumerable<PropertyInfo> propList) {
var modelTypeNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
.Select(p => p.PropertyType.GenericTypeArguments[0].Name)
.ToList();
return modelTypeNames;
}
private static List<string> GetDbSetNames(IEnumerable<PropertyInfo> propList) {
var modelNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
.Select(p => p.Name)
.ToList();
return modelNames;
}
However, you will still need to employee dynamic lambda to finish.
Continue that topic here: Dynamic lambda example with EF scenario
EDIT:
Add link to another question that address the common BAse Config class approach
Abstract domain model base class when using EntityTypeConfiguration<T>
Piggybacking on #Monty0018 's answer but this just need to be updated a little if, like me, you're using Entity Framework 7 and/or SQLite.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
try
{
_builder = modelBuilder;
var typeName = typeof(T).Name;
_builder
.Entity(typeof(T))
.Property<int>("Id")
.ForSqliteHasColumnName(typeName + "Id");
}
catch (Exception e)
{
throw e;
}
}