Iam planning to use Contrib with dapper to make my classes in desktop applications look like this
public abstract class DB : IDisposable
{
public virtual long Insert()
{
using (var db = ConFactory.GetConnection())
{
db.Open();
return db.Insert(this);
}
}
// ... and other CRUD operations
}
then any Concept class will inherits from DB class; like this
[Table("test")]
public class Test : DB
{
[Key]
public int TestId { get; set; }
public string Name { get; set; }
}
using Test class
using (Test t = new Test { Name = textBox2.Text })
{
textBox1.Text = t.Insert().ToString();
}
this sample always fails and gives me
SQL logic error near ")": syntax error
BUT when I implement Insert() method inside child class it works well !!!
the problem is: my code contains lot of classes, rewrite All CUD operations is so disturbing,
any idea to solve this with less code?
I figured out a better way to solve this... which is more flexible and more pro i think.
I'll put the solution here ... soon on github
the solution based on Extension methods, to inject CUD operation with only concept classes I proposed IDbTable base class
public abstract class IDbTable : IDisposable
{
public void Dispose()
{
// do disposing if you need
}
}
public static class DB
{
public static long Insert<T>(this T entity) where T : IDbTable
{
using (var db = ConFactory.GetConnection())
{
db.Open();
return db.Insert(entity);
}
}
public static void Delete<T>(this T entity) where T : IDbTable
{
using (var db = ConFactory.GetConnection())
{
db.Open();
db.Delete(entity);
}
}
public static void Update<T>(this T entity) where T : IDbTable
{
using (var db = ConFactory.GetConnection())
{
db.Open();
SqlMapperExtensions.Update(db, entity);
}
}
public static T Get<T>(int id)
{
using (var db = ConFactory.GetConnection())
{
db.Open();
return db.Get<T>(id);
}
}
}
ok this is all
when any class inherits from IDbTable base class, it have insert, update and delete method as extenssion
[Table("test")]
public class Test : IDbTable
{
[Key]
public int TestId { get; set; }
public string Name { get; set; }
}
.......
Test t = new Test { Name = textBox2.Text };
textBox1.Text = t.Insert().ToString();
AND it WORKS fine !!!
any suggestion to improve will be appreciated.
Related
I develop web api application using Entity Framework 6.
I'd like to integrate and start using Automapper to map to and from my EF entites models.
I've read about projections and realized it's necessary to use the Project().To<> for better performance if I decide using Automapper. However, I don't want to expose my DAL to the Automapper library.
Is there a way I can abstract away the Automapper Project()?
Thanks in advance
Create an interface with a "projection" method,you can copy the
original AutoMapper method.
Then create a concrete implementation.
After that add this interface to your repository's constructor.
Use it
Register the interface to your dependency injection container.
Press F5
Here is an complete example
(Of course you will have each class/interface in the correct layer).
using AutoMapper;
using AutoMapper.QueryableExtensions;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
namespace ConsoleApplicationMapper
{
class Program
{
static void Main(string[] args)
{
Mapper.Initialize(c =>
{
c.CreateMap<Customer, CustomerModel>();
});
//If you use a dependency injection container you don't have to use the constructors
var repository = new CustomerRepository(new EntityProjector());
foreach (var item in repository.GetCustomers())
{
Console.WriteLine(item.Name);
}
Console.ReadLine();
}
}
public class CustomerModel
{
public int CustomerId { get; set; }
public string Name { get; set; }
}
public interface IEntityProjector
{
IQueryable<TDestination> ProjectTo<TDestination>(IQueryable source, params Expression<Func<TDestination, object>>[] membersToExpand);
}
public class EntityProjector : IEntityProjector
{
public IQueryable<TDestination> ProjectTo<TDestination>(IQueryable source, params Expression<Func<TDestination, object>>[] membersToExpand)
{
return source.ProjectTo(membersToExpand);
}
}
public interface ICustomerRepository
{
IEnumerable<CustomerModel> GetCustomers();
}
public class CustomerRepository : ICustomerRepository
{
private readonly IEntityProjector projector;
public CustomerRepository(IEntityProjector theProjector)
{
projector = theProjector;
}
public IEnumerable<CustomerModel> GetCustomers()
{
MyContext context = new MyContext();
//Uncomment this if you want to confirm that only CustomerId,Name are selected and not LastName
//context.Database.Log = s => Console.WriteLine(s);
return context.Customers.SelectTo<CustomerModel>();
}
}
public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
}
public static class MyExtentions
{
static IEntityProjector projector;
static MyExtentions()
{
//You can get it from your dependecy injection container if you use one
projector = new EntityProjector();
}
//I renamed this SelectTo instead of ProjectTo so you don't have any conflict if you use AutoMapper
//Change it to to ProjectTo if you want
public static IQueryable<TDestination> SelectTo<TDestination>(this IQueryable source, params Expression<Func<TDestination, object>>[] membersToExpand)
{
return projector.ProjectTo<TDestination>(source);
}
}
}
I have following code first model:
public class Model1 : DbContext
{
public Model1()
: base("name=Model1")
{
}
public virtual DbSet<Master> Masters { get; set; }
public virtual DbSet<Slave> Slaves { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Master>().Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Slave>().Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Master>().Property(e => e.Name).IsRequired();
modelBuilder.Entity<Slave>().Property(e => e.Name).IsRequired();
}
}
public interface IEntity
{
int Id { get; }
}
public class Master : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Slave> Slaves { get; set; }
public Master()
{
Slaves = new EntityHashSet<Slave>();
}
public Master(string name)
: this()
{
Id = name.GetHashCode();
Name = name;
}
public void Update(IEnumerable<Slave> slaves, Model1 model)
{
Slaves = new EntityHashSet<Slave>(slaves.Select(s => model.Slaves.CreateOrFind(s)));
}
public void Update(IEnumerable<string> slaves, Model1 model)
{
Update(slaves.Select(s => new Slave(s)), model);
}
}
public class Slave : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Master> Masters { get; set; }
public Slave()
{
Masters = new EntityHashSet<Master>();
}
public Slave(string name)
: this()
{
Id = name.GetHashCode();
Name = name;
}
}
I'm using the following utility classes:
public class EntityHashSet<TEntity> : HashSet<TEntity> where TEntity : IEntity
{
public EntityHashSet()
: base(new EntityEqualityComparer<TEntity>())
{ }
public EntityHashSet(IEnumerable<TEntity> collection)
: base(collection, new EntityEqualityComparer<TEntity>())
{ }
}
public class EntityEqualityComparer<TEntity> : IEqualityComparer<TEntity> where TEntity : IEntity
{
public bool Equals(TEntity x, TEntity y)
{
return x.Id.Equals(y.Id);
}
public int GetHashCode(TEntity obj)
{
return obj.Id.GetHashCode();
}
}
public static class ExtensionMethods
{
public static TEntity CreateOrFind<TEntity>(this DbSet<TEntity> dbSet, TEntity entity) where TEntity : class, IEntity
{
return dbSet.Find(entity.Id) ?? dbSet.Add(entity);
}
}
When I first add master entity to the database with the following code no error is thrown:
using (var model = new Model1())
{
var m = new Master("master1");
m.Update(new[] {"slave1", "slave2", "slave3"}, model);
model.Masters.Add(m);
model.SaveChanges();
}
When I try to use the update method for existing one, DbUpdateException is thrown:
var m = model.Masters.CreateOrFind(new Master("master1"));
m.Update(new[] {"slave1", "slave2", "slave3", "slave4"}, model);
model.SaveChanges();
Additional information: An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.
Relevant inner exception:
Violation of PRIMARY KEY constraint 'PK_dbo.SlaveMasters'. Cannot insert duplicate key in object 'dbo.SlaveMasters'. The duplicate key value is (1928309069, -2136434452).
The statement has been terminated.
Why is this? I'm checking whether entities are already in the database or need to be created via CreateOrFind.
EDIT: To clarify, the line that produces the error is:
Slaves = new EntityHashSet<Slave>(slaves.Select(s => model.Slaves.CreateOrFind(s)));
Error is thrown when calling SaveChanges().
I postpone you must be use the previous ef config file ,so it will always try to insert the same value but update .
you could update or check your ef config file before update.
Found a dirty way of getting around this. Before I create new EntityHashSet I call raw SQL command deleting entries from SlaveMasters table that contains current master Id.
model.ExecuteSqlCommand("DELETE FROM SlaveMasters WHERE Master_Id = " + Id);
Slaves = new EntityHashSet<Slave>(slaves.Select(s => model.Slaves.CreateOrFind(s)));
I'm new to mongo so i dont have a clue how to do this.
I'm working on a mvc project with repository pattern, and mongo database, using mongodb-C# driver. I cant figure out how to ensure that some fields are unique when created in database, for example when user registers i want the UserName or email to be unique.
According to mongodb docs this is how you create the unique index
db.accounts.ensureIndex( { "tax-id": 1 }, { unique: true } )
How can i do that with repository pattern and mongodb-C# driver?
This is a sample of the generic repository:
public class GenericRepository<T> : IGenericRepository<T>
{
public static MongoCollection Repository
{
get { return MongoHelpers.GetCollection<T>(); }
}
public virtual T Save(T pobject)
{
//should i modify something here?
Repository.Save(pobject);
return pobject;
}
public virtual T GetByID(BsonValue id)
{
return Repository.FindOneByIdAs<T>(id);
}
public virtual T GetByCondition(Expression<Func<T, bool>> condition)
{
return Repository.AsQueryable<T>().Where(condition).FirstOrDefault();
}
... more code ...
}
Here is a sample model:
public class Membership
{
[BsonId]
public ObjectId UserId { get; set; }
public string UserName { get; set; }
//can it be solved by adding data annotations? - if so, how?
public string Email{ get; set; }
public MembershipStatus Status { get; set; }
...
}
Here is a sample of a service layer that calls the methods mentioned above:
private readonly IGenericRepository<Membership> _membershipRepository;
public RegisterService(IGenericRepository<Membership> membershipRepository)
{
_membershipRepository = membershipRepository;
}
public Membership CreateNewUser(RegisterModel registerModel, Role role, bool requireConfirmationToken)
{
...
var membership = AutoMapper.Mapper.Map<RegisterModel, Membership>(registerModel);
...mode code...
_membershipRepository.Save(membership);
return membership;
}
I can always search the database to see if a value exists, but isnt there something better/cleaner?
Thanks
I've figure out how to handle this one. These 2 sources on stackoverflow helped me doing it:
How to create indexes in MongoDB via .NET
Creating MongoDB Unique Key with C#
This is the method that I've added in the repository:
public virtual void CreateIndex(string[] fields)
{
foreach (string field in fields)
{
if (!Repository.IndexExists(field))
{
Repository.EnsureIndex(new IndexKeysBuilder().Ascending(field), IndexOptions.SetUnique(true).SetSparse(true));
}
}
}
And then i have another class where i initialize the database indexes, and i call it in global.asax for example:
public static class DatabaseConfig
{
private static IGenericRepository<Membership> _membershipRepository = new GenericRepository<Membership>();
private static IGenericRepository<Profile> _profileRepository = new GenericRepository<Profile>();
public static void CreateIndex()
{
_membershipRepository.CreateIndex(new string[] { "UserName" });
_profileRepository.CreateIndex(new string[] { "Email" });
... more code ...
}
}
I'm using EntityFramework as a DataLayer and DTO to transfer data between layer. I develop Windows Forms in N-Tier architecture and when I try to mapping from Entity to DTO in BLL:
public IEnumerable<CategoryDTO> GetCategoriesPaged(int skip, int take, string name)
{
var categories = unitOfWork.CategoryRepository.GetCategoriesPaged(skip, take, name);
var categoriesDTO = Mapper.Map<IEnumerable<Category>, List<CategoryDTO>>(categories);
return categoriesDTO;
}
I've got this error:
http://s810.photobucket.com/user/sky3913/media/AutoMapperError.png.html
The error said that I missing type map configuration or unsupported mapping. I have registered mapping using profile in this way at UI Layer:
[STAThread]
static void Main()
{
AutoMapperBusinessConfiguration.Configure();
AutoMapperWindowsConfiguration.Configure();
...
Application.Run(new frmMain());
}
and AutoMapper configuration is in BLL:
public class AutoMapperBusinessConfiguration
{
public static void Configure()
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile<EntityToDTOProfile>();
cfg.AddProfile<DTOToEntityProfile>();
});
}
}
public class EntityToDTOProfile : Profile
{
public override string ProfileName
{
get { return "EntityToDTOMappings"; }
}
protected override void Configure()
{
Mapper.CreateMap<Category, CategoryDTO>();
}
}
public class DTOToEntityProfile : Profile
{
public override string ProfileName
{
get { return "DTOToEntityMappings"; }
}
protected override void Configure()
{
Mapper.CreateMap<CategoryDTO, Category>();
}
}
I've got the same error too when mapping from DTO to Entity.
category = Mapper.Map<Category>(categoryDTO);
How to solve this?
Its because you are using Mapper.Initialize multiple times. If you look at the source code it calls Mapper.Reset() which means only the last mapping defined will work. so instead simply remove the Initialize calls and replace with Mapper.AddProfile< >
Use AutoMapper.AssertConfigurationIsValid() after the Configure() calls. If anything fails it will throw an exception with a descriptive text. It should give you more info to debug further.
Mapping DTOs to Entities using AutoMapper and EntityFramework
here we have an Entity class Country and an CountryDTO
public class Country
{
public int CountryID { get; set; }
public string ContryName { get; set; }
public string CountryCode { get; set; }
}
CountryDto
public class CountryDTO
{
public int CountryID { get; set; }
public string ContryName { get; set; }
public string CountryCode { get; set; }
}
Create Object of CountryDTO
CountryDTO collection=new CountryDTO();
collection.CountryID =1;
collection.ContryName ="India";
collection.CountryCode ="in";
Country model = Convertor.Convert<Country, CountryDTO>(collection);
dbcontext.Countries.Add(model);
dbcontext.SaveChanges();
this will work fine for a new Country, the above code will map CountryDTO to Country Entity Object and add new entities to the dbcontext and save the changes.
using System.Reflection;
public static TOut Convert<TOut, TIn>(TIn fromRecord) where TOut : new()
{
var toRecord = new TOut();
PropertyInfo[] fromFields = null;
PropertyInfo[] toFields = null;
fromFields = typeof(TIn).GetProperties();
toFields = typeof(TOut).GetProperties();
foreach (var fromField in fromFields)
{
foreach (var toField in toFields)
{
if (fromField.Name == toField.Name)
{
toField.SetValue(toRecord, fromField.GetValue(fromRecord, null), null);
break;
}
}
}
return toRecord;
}
public static List<TOut> Convert<TOut, TIn>(List<TIn> fromRecordList) where TOut : new()
{
return fromRecordList.Count == 0 ? null : fromRecordList.Select(Convert<TOut, TIn>).ToList();
}
http://bhupendrasinghsaini.blogspot.in/2014/09/convert-enity-framwork-data-in-entity.html
Here is a simple project based on a Poco class named Task:
class Program
{
static void Main(string[] args)
{
using (MyDbContext ctx = new MyDbContext())
{
// first query
DateTime compareDate = DateTime.Now + TimeSpan.FromDays(3);
var res = ctx.Tasks.Where(t => t.LastUpdate < compareDate).ToList();
// second query
res = ctx.Tasks.Where(t => t.ShouldUpdate).ToList();
}
}
}
public class MyDbContext : DbContext
{
public DbSet<Task> Tasks { get; set; }
}
public class Task
{
public int ID { get; set; }
public DateTime LastUpdate { get; set; }
public bool ShouldUpdate
{
get
{
return LastUpdate < DateTime.Now + TimeSpan.FromDays(3);
}
}
}
What I want to do is to query the context dbset including in the where clause the ShouldUpdate derived property.
The "first query works fine" (I can't write it in a single line but it doesn't matter).
As you know, we get a NotSupportedException on the "second query", with the following message:
The specified type member 'ShouldUpdate' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
That's right and I can understand why it happen, but I need to encapsulate the derived information inside the Task object so I can display the property in a grid or use it in every other place, without duplicating the logic behind it.
Is there a smart technique to do this?
NB: What is the technical name of the ShouldUplate property? derived? calculated? computed?
Finally i found THE solution..
You can store the partial queries (Expressions) in static fileds and use them like this:
class Program
{
static void Main(string[] args)
{
using (MyDbContext ctx = new MyDbContext())
{
res = ctx.Tasks.Where(Task.ShouldUpdateExpression).ToList();
}
}
}
public class MyDbContext : DbContext
{
public DbSet<Task> Tasks { get; set; }
}
public class Task
{
public int ID { get; set; }
public DateTime LastUpdate { get; set; }
public bool ShouldUpdate
{
get
{
return ShouldUpdateExpression.Compile()(this);
}
}
public static Expression<Func<Task, bool>> ShouldUpdateExpression
{
get
{
return t => t.LastUpdate < EntityFunctions.AddDays(DateTime.Now, 3);
}
}
}
Repository pattern would provide a better abstraction in this case. You can centralize the logic as follows. Define a new property TaskSource in your context.
public class MyDbContext : DbContext
{
public DbSet<Task> Tasks { get; set; }
public IQueryable<Task> TaskSource
{
get
{
return Tasks.Where(t => t.LastUpdate < EntityFunctions.AddDays(DateTime.Now, 3));
}
}
}
You need to put the ShouldUpdate logic inside the linq-to-enitites query. You can use EntityFunctions.AddDays to help you out like so
res = ctx.Tasks.Where(t => t.LastUpdate < EntityFunctions.AddDays(DateTime.Now, 3)).ToList();