Connection Between Kendo and SQL Server in ASP.NET Core - sql-server

I am trying to do CRUD operation using Kendo UI to show my data in a grid view.
I can read the data from my database with the following code in my controller (my tables are in SQL Server and connected through a connection string):
[Area("Admin")]
public class HeaderMenuController : Controller
{
private readonly IUnitOfWork _unitOfWork;
public HeaderMenuController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public IActionResult Index()
{
return View();
}
public DataSourceResult Products_Read([DataSourceRequest] DataSourceRequest request)
{
return _unitOfWork.HeaderMenu.GetAll().ToDataSourceResult(request);
}
}
index.cshtml
#(Html.Kendo().Grid<MSDACE.Models.HeaderViewModel>()
.Name("grid")
.Columns(columns =>
{
columns.Bound(product => product.HeaderMenuID).Width(100);
columns.Bound(product => product.HeaderMenuName);
columns.Bound(product => product.HeaderMenuDispOrder).Width(250);
columns.Command(commands =>
{
commands.Destroy(); // The "destroy" command removes data items.
}).Title("Commands").Width(200);
})
.ToolBar(toolbar =>
{
toolbar.Create(); // The "create" command adds new data items.
toolbar.Save(); // The "save" command saves the changed data items.
})
.Editable(editable => editable.Mode(GridEditMode.InCell)) // Use in-cell editing mode.
.DataSource(dataSource =>
dataSource.Ajax()
.Batch(true) // Enable batch updates.
.Model(model =>
{
model.Id(product => product.HeaderMenuID); // Specify the property which is the unique identifier of the model.
model.Field(product => product.HeaderMenuID).Editable(false); // Make the ProductID property not editable.
})
.Create(create => create.Action("Products_Create", "HeaderMenu")) // Action method invoked when the user saves a new data item.
.Read(read => read.Action("Products_Read", "HeaderMenu")) // Action method invoked when the Grid needs data.
.Update(update => update.Action("Products_Update", "HeaderMenu")) // Action method invoked when the user saves an updated data item.
.Destroy(destroy => destroy.Action("Products_Destroy", "HeaderMenu")) // Action method invoked when the user removes a data item.
)
.Pageable()
)
The problem is I can't the delete or create operations.
I have written the GetAll, Remove, Get and other functions, but my problem is to reflect in the Kendo Grid
IUnitOfWork.cs
public interface IUnitOfWork : IDisposable
{
IHeaderMenuRepository HeaderMenu { get; }
void Save();
}
IHeaderMenuRepository.cs
public class HeaderMenuRepository : Repository<HeaderMenu>, IHeaderMenuRepository
{
private readonly ApplicationDbContext _db;
public HeaderMenuRepository(ApplicationDbContext db) : base(db)
{
_db = db;
}
public IEnumerable<SelectListItem> GetHeaderMenuList()
{
return _db.HeaderMenu.Select(i => new SelectListItem()
{
Text = i.HeaderMenuName,
Value = i.HeaderMenuID.ToString()
});
}
public void Update(HeaderMenu headerMenu)
{
var objFrobDb = _db.HeaderMenu.FirstOrDefault(s => s.HeaderMenuID == headerMenu.HeaderMenuID);
objFrobDb.HeaderMenuName = headerMenu.HeaderMenuName;
objFrobDb.HeaderMenuDesc = headerMenu.HeaderMenuDesc;
objFrobDb.HeaderMenuActive = headerMenu.HeaderMenuActive;
objFrobDb.HeaderMenuDispOrder = headerMenu.HeaderMenuDispOrder;
_db.SaveChanges();
}
IRepository
public interface IRepository<T> where T : class
{
T Get(int id);
IEnumerable<T> GetAll(
Expression<Func<T, bool>> filter = null,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
string includeProperties = null
);
T GetFirstOrDefault(
Expression<Func<T, bool>> filter = null,
string includeProperties = null
);
void Add(T entity);
void Remove(int id);
void Remove(T entity);
}
UnitOfWork.cs
public class UnitOfWork : IUnitOfWork
{
private readonly ApplicationDbContext _db;
public UnitOfWork(ApplicationDbContext db)
{
_db = db;
HeaderMenu = new HeaderMenuRepository(_db);
}
public IHeaderMenuRepository HeaderMenu { get; private set; }
public void Dispose()
{
_db.Dispose();
}
public void Save()
{
_db.SaveChanges();
}
}
Repository.cs
public class Repository<T> : IRepository<T> where T : class
{
protected readonly DbContext Context;
internal DbSet<T> dbSet;
public Repository(DbContext context)
{
Context = context;
this.dbSet = context.Set<T>();
}
public void Add(T entity)
{
dbSet.Add(entity);
}
public T Get(int id)
{
return dbSet.Find(id);
}
public IEnumerable<T> GetAll(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = null)
{
IQueryable<T> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
//include properties will be comma seperated
if (includeProperties != null)
{
foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
}
if (orderBy != null)
{
return orderBy(query).ToList();
}
return query.ToList();
}
public T GetFirstOrDefault(Expression<Func<T, bool>> filter = null, string includeProperties = null)
{
IQueryable<T> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
//include properties will be comma seperated
if (includeProperties != null)
{
foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
}
return query.FirstOrDefault();
}
public void Remove(int id)
{
T entityToRemove = dbSet.Find(id);
Remove(entityToRemove);
}
public void Remove(T entity)
{
dbSet.Remove(entity);
}
}
HeaderMenuRepository.cs
public class HeaderMenuRepository : Repository<HeaderMenu>, IHeaderMenuRepository
{
private readonly ApplicationDbContext _db;
public HeaderMenuRepository(ApplicationDbContext db) : base(db)
{
_db = db;
}
public IEnumerable<SelectListItem> GetHeaderMenuList()
{
return _db.HeaderMenu.Select(i => new SelectListItem()
{
Text = i.HeaderMenuName,
Value = i.HeaderMenuID.ToString()
});
}
public void Update(HeaderMenu headerMenu)
{
var objFrobDb = _db.HeaderMenu.FirstOrDefault(s => s.HeaderMenuID == headerMenu.HeaderMenuID);
objFrobDb.HeaderMenuName = headerMenu.HeaderMenuName;
objFrobDb.HeaderMenuDesc = headerMenu.HeaderMenuDesc;
objFrobDb.HeaderMenuActive = headerMenu.HeaderMenuActive;
objFrobDb.HeaderMenuDispOrder = headerMenu.HeaderMenuDispOrder;
_db.SaveChanges();
}
}
ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
public DbSet<HeaderMenu> HeaderMenu { get; set; }
}
The problem is I cant to the Delete, Create or Edit operation!
I Belive that I can not make the connection between the Kendo and my Database and have to implement something in my controller.
I try the Kendo.MVC examples but the problem are the northwind and sqllite the used!
Could please help me by this issue to implement the CRUD on Grid using the data in my sqlserver tables.

Related

How to log database table value changes programmatically in terms of auditing?

May be this could be accomplished using a trigger and an audit log table in SQL server. Or perhaps it could be accomplished by overriding the SaveChanges() method in Entity Framework. My concern is how to write the code to get it done and which one will be efficient. Can anybody help me?
If you log changes through the code, alongside data, you can add additional information to the audit log such as IP, user info, client info, ... and it is really helpful. The downside would be if someone changes data directly via the database you cannot find out what data has changed and the performance of logging data change by audit log table is better. If you just need to capture data change and don't need to find out who and from where data has changed choose the database approach.
Here is an implementation to capture data changes by EF Core:
public interface IAuditableEntity
{
}
public class AuditLogEntity
{
public int Id { get; set; }
public string UserName { get; set; }
public DateTime CreateDate { get; set; }
public ChangeType ChangeType { get; set; }
public string EntityId { get; set; }
public string EntityName { get; set; }
public IEnumerable<EntityPropertyChange> Changes { get; set; }
}
public class EntityPropertyChange
{
public string PropertyName { get; set; }
public string OldValue { get; set; }
public string NewValue { get; set; }
}
public enum ChangeType
{
Add = 1,
Edit = 2,
Remove = 3
}
In DbContext:
public class RegistryDbContext : DbContext
{
private readonly IHttpContextAccessor _contextAccessor;
public RegistryDbContext(DbContextOptions<RegistryDbContext> options, IHttpContextAccessor contextAccessor) :
base(options)
{
_contextAccessor = contextAccessor;
}
public DbSet<AuditLogEntity> AuditLogs { get; set; }
public override int SaveChanges()
{
CaptureChanges();
return base.SaveChanges();
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
{
CaptureChanges();
return await base.SaveChangesAsync(cancellationToken);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AuditLogEntity>().Property(p => p.UserId).IsUnicode(false).HasMaxLength(36).IsRequired(false);
modelBuilder.Entity<AuditLogEntity>().Property(p => p.EntityId).IsUnicode(false).HasMaxLength(36).IsRequired();
modelBuilder.Entity<AuditLogEntity>().Property(p => p.EntityName).IsUnicode(false).HasMaxLength(256).IsRequired(false);
builder.Ignore(p => p.Changes);
modelBuilder.Entity<AuditLogEntity>()
.Property(p => p.Changes).IsUnicode().HasMaxLength(int.MaxValue).IsRequired(false)
.HasConversion(
changes => JsonConvert.SerializeObject(changes, Formatting.None, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Include,
TypeNameHandling = TypeNameHandling.Auto
}),
changes => JsonConvert.DeserializeObject<IList<EntityPropertyChange>>(changes, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Include,
TypeNameHandling = TypeNameHandling.Auto
}));
}
private void CaptureChanges()
{
var changes = ChangeTracker
.Entries<IAuditableEntity>()
.Where(e =>
e.State == EntityState.Added ||
e.State == EntityState.Modified ||
e.State == EntityState.Deleted)
.Select(GetAuditLogItems)
.ToList();
AuditLogs.AddRange(changes);
}
private AuditLogEntity GetAuditLogItems(EntityEntry entry)
{
var auditEntity = new AuditLogEntity
{
CreateDate = DateTime.Now,
EntityId = entry.Properties.FirstOrDefault(f => f.Metadata.IsPrimaryKey())?.CurrentValue?.ToString(),
EntityName = entry.Metadata.Name,
UserName = _contextAccessor?.HttpContext?.User?.Name.ToString(),
};
switch (entry.State)
{
case EntityState.Added:
auditEntity.ChangeType = ChangeType.Add;
auditEntity.Changes = GetChanges(entry.Properties, e => true).ToList();
foreach (var entityChange in auditEntity.Changes)
entityChange.OldValue = null;
break;
case EntityState.Modified:
auditEntity.ChangeType = ChangeType.Edit;
auditEntity.Changes = GetChanges(entry.Properties, e => e.IsModified).ToList();
break;
case EntityState.Deleted:
auditEntity.ChangeType = ChangeType.Remove;
break;
}
return auditEntity;
}
private IEnumerable<EntityPropertyChange> GetChanges(IEnumerable<PropertyEntry> properties,
Func<PropertyEntry, bool> predicate) => properties
.Where(predicate)
.Select(property =>
new EntityPropertyChange
{
PropertyName = property.Metadata.Name,
OldValue = property.OriginalValue?.ToString(),
NewValue = property.CurrentValue?.ToString()
});
}
I use IAuditableEntity (an empty interface) to mark entities that I want to capture changes.
public class CustomerEntity : IAuditableEntity
{
...
}
You can also use Audit.NET library to capture changes.

where doese breeze fits into ntier architecture

i am Trying to fit in breezeJS with my existing architecture. I have a structure like
html/JS/Angular :: based view using hot-towel angular.
web api controllers :: whom the view calls.
Services layer :: that is being called from Web api. Any business logic goes here.
Unit of Work :: And (if) business logic requires to talk to data base for CRUDs it calls UOW.
Repository Pattern :: UOW is actually wrapping repositories. and repositores in turn talking to DbContexts.
Uptill now i was able to conver normal repositories implementation into the one using
public EFContextProvider<MyContext> DbContext { get; set; }
instead of just DbContext and i am also exposing MetaData using a string property with in UOW and IQueryables are returned using DbContext.Context.SomeEntity
Question 1 : Am i on right track ??
Question 2 : Most of the breeze examples are suggesting one SaveChanges method that give you all the entities that were changed and it will persist it at once. What if i want to trigger some business logic before Add,Update and Delete. i want to call me AddSomething service method and want to have a particular type of entity being sent to AddSomething and run some business logic before persistence. How can i put it together.
my code looksl ike
[BreezeController]//This is the controller
public class BreezeController : ApiController
{
private readonly ISomeService someService;
public BreezeController(ISomeService someService)
{
this.someService = someService;
}
// ~/breeze/todos/Metadata
[HttpGet]
public string Metadata()
{
return someService.MetaData();
}
// ~/breeze/todos/Todos
// ~/breeze/todos/Todos?$filter=IsArchived eq false&$orderby=CreatedAt
[HttpGet]
public IQueryable<Node> Nodes()
{
return nodesService.GetAllNodes().AsQueryable();
}
// ~/breeze/todos/SaveChanges
//[HttpPost]
//public SaveResult SaveChanges(JObject saveBundle)
//{
// return _contextProvider.SaveChanges(saveBundle);
//}
Below is the service
public class SomeService : BaseService, ISomeService
{
private readonly IUow Uow;
public SomeService(IUow Uow)
: base(Uow)
{
this.Uow = Uow;
}
public IEnumerable<Something> GetAllNodes()
{
return Uow.Somethings.GetAll();
}
}
every service can expose one property through base. that is actually the meta data
public class BaseService : IBaseService
{
private readonly IUow Uow;
public BaseService(IUow Uow)
{
this.Uow = Uow;
}
public string MetaData()
{
return Uow.MetaData;
}
}
and the my UOW looks like
public class VNUow : IUow, IDisposable
{
public VNUow(IRepositoryProvider repositoryProvider)
{
CreateDbContext();
repositoryProvider.DbContext = DbContext;
RepositoryProvider = repositoryProvider;
}
// Code Camper repositories
public IRepository<Something> NodeGroup { get { return GetStandardRepo<Something>(); } }
} }
public IRepository<Node> Nodes { get { return GetStandardRepo<Node>(); } }
/// <summary>
/// Save pending changes to the database
/// </summary>
public void Commit()
{
//System.Diagnostics.Debug.WriteLine("Committed");
DbContext.Context.SaveChanges();
}
public string MetaData // the Name property
{
get
{
return DbContext.Metadata();
}
}
protected void CreateDbContext()
{
// DbContext = new VNContext();
DbContext = new EFContextProvider<VNContext>();
// Load navigation properties always if it is true
DbContext.Context.Configuration.LazyLoadingEnabled = false;
// Do NOT enable proxied entities, else serialization fails
DbContext.Context.Configuration.ProxyCreationEnabled = true;
// Because Web API will perform validation, we don't need/want EF to do so
DbContext.Context.Configuration.ValidateOnSaveEnabled = false;
//DbContext.Configuration.AutoDetectChangesEnabled = false;
// We won't use this performance tweak because we don't need
// the extra performance and, when autodetect is false,
// we'd have to be careful. We're not being that careful.
}
protected IRepositoryProvider RepositoryProvider { get; set; }
private IRepository<T> GetStandardRepo<T>() where T : class
{
return RepositoryProvider.GetRepositoryForEntityType<T>();
}
private T GetRepo<T>() where T : class
{
return RepositoryProvider.GetRepository<T>();
}
private EFContextProvider<VNContext> DbContext { get; set; }
#region IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (DbContext != null)
{
DbContext.Context.Dispose();
}
}
}
#endregion
}
in the end Repository Implementaion looks like
public class EFRepository<T> : IRepository<T> where T : class
{
public EFRepository(EFContextProvider<VNContext> dbContext)
{
if (dbContext == null)
throw new ArgumentNullException("dbContext");
DbContext = dbContext;
DbSet = DbContext.Context.Set<T>();
}
protected EFContextProvider<VNContext> DbContext { get; set; }
protected DbSet<T> DbSet { get; set; }
public virtual IQueryable<T> GetAll()
{
return DbSet;
}
public virtual IQueryable<T> GetAllEagerLoad(params Expression<Func<T, object>>[] children)
{
children.ToList().ForEach(x => DbSet.Include(x).Load());
return DbSet;
}
public virtual IQueryable<T> GetAllEagerLoadSelective(string[] children)
{
foreach (var item in children)
{
DbSet.Include(item);
}
return DbSet;
}
public virtual IQueryable<T> GetAllLazyLoad()
{
return DbSet;
}
public virtual T GetById(int id)
{
//return DbSet.FirstOrDefault(PredicateBuilder.GetByIdPredicate<T>(id));
return DbSet.Find(id);
}
public virtual T GetByIdLazyLoad(int id, params Expression<Func<T, object>>[] children)
{
children.ToList().ForEach(x => DbSet.Include(x).Load());
return DbSet.Find(id);
}
public virtual void Add(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Context.Entry(entity);
if (dbEntityEntry.State != EntityState.Detached)
{
dbEntityEntry.State = EntityState.Added;
}
else
{
DbSet.Add(entity);
}
}
public virtual void Update(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Context.Entry(entity);
if (dbEntityEntry.State == EntityState.Detached)
{
DbSet.Attach(entity);
}
dbEntityEntry.State = EntityState.Modified;
}
public virtual void Delete(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Context.Entry(entity);
if (dbEntityEntry.State != EntityState.Deleted)
{
dbEntityEntry.State = EntityState.Deleted;
}
else
{
DbSet.Attach(entity);
DbSet.Remove(entity);
}
}
public virtual void Delete(int id)
{
var entity = GetById(id);
if (entity == null) return; // not found; assume already deleted.
Delete(entity);
}
}
Much of this question is broad question and answers will be primarily opinion based... that said, here's my two cents: keep it simple. Carefully consider whether you truly need 3, 4 and 5, especially whether you need to implement UoW or the Repository Pattern yourself. The EF DbContext implements both, you could use it in your controllers directly if you wanted.
If you have custom logic that needs to execute prior to savechanges utilize one of the interceptor methods: BeforeSaveEntity or BeforeSaveEntites. Here's the documentation for those methods:
http://www.getbreezenow.com/documentation/contextprovider#BeforeSaveEntity
Breeze supports "Named saves" where you specify the name of the specific server endpoint ( i.e. your service method) on a per save basis. See:
http://www.getbreezenow.com/documentation/saving-changes
This would look something like this on your client.
var saveOptions = new SaveOptions({ resourceName: "CustomSave1" });
em.saveChanges(entitiesToSave, saveOptions).then(function (saveResult) {
// .. do something interesting.
}
and on your server
[HttpPost]
public SaveResult CustomSave1(JObject saveBundle) {
ContextProvider.BeforeSaveEntityDelegate = CustomSave1Interceptor;
return ContextProvider.SaveChanges(saveBundle);
}
private Dictionary<Type, List<EntityInfo>> CustomSave1Interceptor(Dictionary<Type, List<EntityInfo>> saveMap) {
// In this method you can
// 1) validate entities in the saveMap and optionally throw an exception
// 2) update any of the entities in the saveMap
// 3) add new entities to the saveMap
// 4) delete entities from the save map.
// For example
List<EntityInfo> fooInfos;
if (!saveMap.TryGetValue(typeof(Foo), out fooEntities)) {
// modify or delete any of the fooEntites
// or add new entityInfo instances to the fooEntities list.
}
}

BindingList<> (master) with a composed BindingList<> (child) reference

I have a situation where a BindingList<> represents a collection of POCOs that have sub-collections of similar nature, Here is a sample code of two such POCOs and their respective lists:
The DirectoryTypePoco
public class DirectoryTypePoco : IBasePoco
{
public DirectoryTypePoco()
{
}
public DirectoryTypePoco(Int16 directoryTypeId, String directoryImplementation, String directoryDescription, DirectoryDefinitionPocoList directoryDefinition)
{
DirectoryTypeId = directoryTypeId;
DirectoryImplementation = directoryImplementation;
DirectoryDescription = directoryDescription;
DirectoryDefinition = directoryDefinition;
}
public Int16 DirectoryTypeId { get; set; }
public String DirectoryImplementation { get; set; }
public String DirectoryDescription { get; set; }
public DirectoryDefinitionPocoList DirectoryDefinition { get; set; }
public object GenerateEntity(GenericRepository repository, params object[] parameters)
{
var lastMaxEntityId = repository.GetQuery<DirectoryType>().Select(select => #select.DirectoryTypeId).DefaultIfEmpty().Max();
var newEntity = new DirectoryType
{
DirectoryTypeId = (short)(lastMaxEntityId + 1),
DirectoryImplementation = this.DirectoryImplementation,
DirectoryDescription = this.DirectoryDescription
};
return newEntity;
}
}
And the BindingList<DirectoryTypePoco>:
public class DirectoryTypePocoList : BindingList<DirectoryTypePoco>
{
public DirectoryTypePocoList()
{
using (var repository = new GenericRepository(new PWRDbContext()))
{
var query = repository.GetQuery<DirectoryType>();
foreach (var r in query)
{
Add(new DirectoryTypePoco(r.DirectoryTypeId, r.DirectoryImplementation, r.DirectoryDescription, new DirectoryDefinitionPocoList(r.DirectoryTypeId)));
}
}
}
public DirectoryTypePocoList(short directoryTypeId)
{
using (var repository = new GenericRepository(new PWRDbContext()))
{
var query = repository.GetQuery<DirectoryType>(where => where.DirectoryTypeId == directoryTypeId);
foreach (var r in query)
{
Add(new DirectoryTypePoco(r.DirectoryTypeId, r.DirectoryImplementation, r.DirectoryDescription, new DirectoryDefinitionPocoList(r.DirectoryTypeId)));
}
}
}
}
The second object: DirectoryDefinitionPoco
public class DirectoryDefinitionPoco : IBasePoco
{
public DirectoryDefinitionPoco()
{
}
public DirectoryDefinitionPoco(Int16 directoryTypeId, Byte parameterId, String parameterName, String parameterValidation, Boolean encryptionRequired, PocoChangeType changeType = PocoChangeType.None)
{
DirectoryTypeId = directoryTypeId;
ParameterId = parameterId;
ParameterName = parameterName;
ParameterDescription = parameterName;
ParameterRequired = false;
ParameterValidation = parameterValidation;
EncryptionRequired = encryptionRequired;
}
public Int16 DirectoryTypeId { get; set; }
public Byte ParameterId { get; set; }
public String ParameterName { get; set; }
public String ParameterDescription { get; set; }
public String ParameterValidation { get; set; }
public Boolean ParameterRequired { get; set; }
public Boolean EncryptionRequired { get; set; }
public object GenerateEntity(GenericRepository repository, params object[] parameters)
{
var masterId = (short) parameters[0];
var lastMaxEntityId = repository.GetQuery<DirectoryDefinition>(where => where.DirectoryTypeId == masterId).Select(select => #select.ParameterId).DefaultIfEmpty().Max();
var newEntity = new DirectoryDefinition
{
DirectoryTypeId = (short)parameters[0],
ParameterId = (byte)(lastMaxEntityId + 1),
ParameterName = this.ParameterName,
ParameterDescription = this.ParameterDescription,
ParameterValidation = this.ParameterValidation,
ParameterRequired = this.ParameterRequired,
EncryptionRequired = this.EncryptionRequired
};
return newEntity;
}
}
And BindingList<DirectoryDefinitionPoco>:
public class DirectoryDefinitionPocoList : BindingList<DirectoryDefinitionPoco>
{
public DirectoryDefinitionPocoList(short directoryTypeId)
{
using (var repository = new GenericRepository(new PWRDbContext()))
{
var query = repository.GetQuery<DirectoryDefinition>(where => where.DirectoryTypeId == directoryTypeId);
foreach (var r in query)
{
Add(new DirectoryDefinitionPoco(r.DirectoryTypeId, r.ParameterId, r.ParameterName, r.ParameterValidation, r.EncryptionRequired));
}
}
}
public List<DirectoryDefinition> GetSourceQuery()
{
List<DirectoryDefinition> result;
using (var repository = new GenericRepository(new PWRDbContext()))
{
result = repository.GetQuery<DirectoryDefinition>().ToList();
}
return result;
}
public List<DirectoryDefinition> GetSourceQuery(short directoryTypeId)
{
List<DirectoryDefinition> result;
using (var repository = new GenericRepository(new PWRDbContext()))
{
result = repository.GetQuery<DirectoryDefinition>(where => where.DirectoryTypeId == directoryTypeId).ToList();
}
return result;
}
}
On the form, I load the data into the grid through a BindingSource component. The child rows are added properly and the data is valid.
Here is the issue: I'm able to add new DirectoryTypePoco but when try to add a DirectoryDefinitionPoco, in the code, the the DirectoryDefinitionPocoobject that I get has a zero for it's parent object. In the above picture, the Test5.dll234 is a DirectoryTypePoco with DirectoryTypeId = 8 and all child under it are ok except the new one I create. What am I suppose to do to make sure I have Master-Child relation in this case?
Ok. It seems that there are two thing I should have noticed in my design.
The individual child Poco needs to know the parent Poco through a reference.
The DevExpress Grid has methods that allow for retrieving the attached data to a parent row while in the child view' particular row.
The first part is straightforwards: add a new property in the child poco of parent poco type.
This however, in my case, doesn't solve my issue as when I visually add a new row on the grid, the default constructor is invoked and it takes no parameters and hence the parent poco reference will remain NULL and the Ids (numeric) will be defaulted to 0
The second point helped fix my issue completely. I was able to conjure up an extension method for the XtraGrid's GridView as follows:
public static class DevExpressGridHelper
{
public static IBasePoco GetPocoFromSelectedRow(this BaseView view)
{
return (IBasePoco)view.GetRow(((GridView)view).FocusedRowHandle);
}
public static IBasePoco GetParentPocoFromSelectedRow(this GridView view)
{
if (view.ParentView !=null)
{
// return (IBasePoco)(view.ParentView).GetRow(((GridView)(view.ParentView)).FocusedRowHandle);
return (IBasePoco)((GridView)view.ParentView).GetFocusedRow();
}
return null;
}
}
And used it as follows:
private void GridMain_Level_1_RowUpdated(object sender, RowObjectEventArgs e)
{
var view = sender as GridView;
if (view == null)
{
return;
}
var pocoObject = e.Row as DirectoryDefinitionPoco;
if (pocoObject == null)
{
return;
}
var parentPocoObject = view.GetParentPocoFromSelectedRow();
if (parentPocoObject == null)
{
return;
}
if (view.IsNewItemRow(e.RowHandle))
{
Create(pocoObject, parentPocoObject);
}
else
{
Update(pocoObject);
}
}

Map a Uri field using Dapper

What is the cleanest way to map a string column to a Uri property using Dapper?
Here's the cleanest I've been able to come up with so far (using the ITypeMap functionality):
Query:
SELECT * FROM TableWithAStringAddressColumn
POCO:
public class MyPoco
{
[ColumnSetter("DapperAddress")]
public Uri Address { get; set; }
private string DapperAddress { set { this.Address = new Uri(value); } }
}
Extensions:
partial class SqlMapper
{
public static void InitializeTypeMaps()
{
SqlMapper.SetTypeMap(
typeof(MyPoco),
new CustomPropertyTypeMap(typeof(MyPoco), SqlMapper.CustomSetterMapper));
// call out every other class that needs this kind of mapping
}
public static Func<Type, string, PropertyInfo> CustomSetterMapper =
(type, columnName) =>
{
PropertyInfo prop = type
.GetProperties()
.FirstOrDefault(p => string.Equals(columnName, p.Name, StringComparison.OrdinalIgnoreCase));
if (prop != null)
{
// find out if we need to use a different setter
ColumnSetterAttribute setterAttribute = prop.GetCustomAttributes(false).OfType<ColumnSetterAttribute>().LastOrDefault();
if (setterAttribute != null)
{
PropertyInfo setterProp = type
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.FirstOrDefault(p => string.Equals(setterAttribute.Setter, p.Name, StringComparison.OrdinalIgnoreCase));
if (setterProp == null)
{
throw new InvalidOperationException(string.Format("Setter property misconfigured (Property={0}, Setter={1})", prop.Name, setterAttribute.Setter));
}
else
{
prop = setterProp;
}
}
}
return prop;
};
}
Custom Attribute:
public class ColumnSetterAttribute : Attribute
{
public string Setter { get; set; }
public ColumnSetterAttribute(string setter)
{
this.Setter = setter;
}
}
[edit] I'm looking for a solution I can use without needing to call out all columns in all my queries (I'd like to find a solution where I can use SELECT *).
Seems like a lot of work...
Wouldn't this be ok?
public class MyPoco
{
private string _uriMapper;
public Uri SomeUri
{
get { return new Uri(_uriMapper); }
}
public string Mapper { set { _uriMapper = value; } }
}
Edit:
public class UriContainer
{
private string _uriMapper;
public string UriMapper { set { _uriMapper = value; } }
public int Id { get; set; }
public Uri SomeUri { get {return new Uri(_uriMapper);} }
}
public class DbTests
{
[Test]
public void Can_Get_A_Uri()
{
using (var c = new SqlConnection("hello"))
{
c.Open();
var uri = c.Query<UriContainer>("select *, someuri as urimapper from uris where id = 3").Single();
Console.WriteLine(uri.SomeUri);
}
}
}

Storing system settings and configurations for a website in a database?

How do I structure the database? I use a repository pattern with entity framework and code-first to code the models.
For example: I want an admin to set a string to be appended to every username.
I was thinking about a key-value table (settings) that has the following columns? SettingsId, Name, Value. With this method, I would need to manually go in, create a record Name:AppendedToUsername, Value:nil. I would then write repository methods specifically for each settings I need. For eg.
public string GetAppenedToUsername()
{
db.Settings.FirstOrDefault(s => s.Name == "AppendedToUsername").Select(s => s.Value);
}
Is there any better way of designing this database?
It's a good solution. I only recommend to create a strongly typed class with these settings and use caching for them.
Cache service:
public class CacheService
{
private ObjectCache Cache
{
get { return MemoryCache.Default; }
}
public object Get(string key)
{
return Cache[key];
}
public void Set(string key, object data, int cacheTime)
{
CacheItemPolicy policy = new CacheItemPolicy();
policy.AbsoluteExpiration = DateTime.Now.AddMinutes(cacheTime);
Cache.Add(new CacheItem(key, data), policy);
}
public bool IsSet(string key)
{
return (Cache[key] != null);
}
public void Invalidate(string key)
{
Cache.Remove(key);
}
}
AppSetting:
public class AppSetting
{
public const string StrSettingKey = "StrSetting";
private CacheService CacheService { get; set; }
private DbContext DbContext { get; set; }
public AppSetting(ICacheService cache, DbContext db)
{
CacheService = CacheService;
DbContext = db;
}
public string StrSetting
{
get
{
if (CacheService.IsSet(StrSettingKey))
{
return (string) CacheService.Get(StrSettingKey);
}
else
{
var value = DbContext.Settings.Single(s => s.Name == StrSettingKey).Select(s => s.Value);
CacheService.Set(StrSettingKey, value, 60); //one hour
return value;
}
}
set
{
var item = DbContext.Settings.Single(s => s.Name == StrSettingKey);
item.Value = value;
DbContext.SaveChanges();
CacheService.Set(StrSettingKey, value);
}
}
}

Resources