Automate custom code generation when "Run Custom Tool" on tt file - c#

I have this edmx, database first, in project "DAL".
But the tt file from edmx is in another project "DomainModel".
Both projects are in the same solution.
Now, whenever I created new table in database, update model from database, I have to manually re-insert IEntity and public EntityState EntityState { get; set; } in every table class generated from "Run Custom Tool" on tt file.
public partial class newTable : IEntity
{
public EntityState EntityState { get; set; }
}
public partial class oldTable : IEntity
{
public EntityState EntityState { get; set; }
}
I also have to manually re-insert Configuration.LazyLoadingEnabled = false; and Configuration.ProxyCreationEnabled = false; in the following code
public partial class myEntities : DbContext
{
public myEntities()
: base("name=myEntities")
{
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
}
}
is there anyway for me to automate these? especially the first part, with hundred of tables.

You may try to tweak tt, however I'd recommend you to utilize partial feature, it is there for purpose :). Put put all amendments to the separate file which is not replaced on every model update. This is still manual or semi-manual work, but you need to do that only once and then you'll need to update it only for new tables.
// DbModelPatches.cs
public partial class newTable : IEntity
{
public EntityState EntityState { get; set; }
}
public partial class oldTable : IEntity
{
public EntityState EntityState { get; set; }
}
If default constructor is generate by tt you cannot replace it in partial file, but you can define another one, parametreized, and put all changes there.
public partial class myEntities : DbContext
{
public myEntities(string name)
: base("name={name}")
{
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
}
// or static factory method:
public static myEntities CreateContext()
{
var res = new myEntities();
res.Configuration.LazyLoadingEnabled = false;
res.Configuration.ProxyCreationEnabled = false;
return res;
}
}

Related

How to make XPO one-to-one relationship with a simple code?

Shortly,
Here's a code. It doesn't work.
I try to insert property with same key in both table.
DevExpress tickets doesn't resolve it also First Ticket and Second Ticket
See First Ticket link. see last reply. That what I do here. not working.
DevExpress First Ticket Last Comment-> To implement this relationship, do not add any relationship object from the designer's toolbox. Instead, create two properties - a property of the MasterBusinessPartner type for the MasterCustomers class and a property of the MasterCustomer type for the MasterBusinessPartner class. This will be sufficient to generate the code you demonstrated in the first code snippet. If you want to implement additional synchronization logic, similar to what is shown in the How to: Implement One-to-One Relationships topic, you can override the OnChanged method, as described in the ORM Wizard one-to-one relation partial classes ticket.
My Schema ( 2 Tables one-to one) Each menu only have single MenuBLOB row.
[Persistent("Menu")]
public class Menu : XPLiteObject
{
[Key, Association("kk")]
public MenuBLOB MenuKey { get; set; }
public long MenuID { get; set; }
public Menu(Session session) : base(session) { }
public Menu(Session session, XPClassInfo classInfo) : base(session, classInfo) { }
}
[Persistent("MenuBLOB")]
public class MenuBLOB : XPLiteObject
{
public long MenuID { get; set; }
public string Base64Value { get; set; }
[Key, Association("kk")]
public Menu MenuKey { get; set; }
public MenuBLOB(Session session) : base(session) { }
public MenuBLOB(Session session, XPClassInfo classInfo) : base(session, classInfo) { }
}
Exception of type 'System.StackOverflowException' was thrown

Keep default behaviour defined in abstract using a different type in the derived class

Using the context of Models and View Models from MVVM, how can I deal with this situation?
I have two abstract classes that will be the base of all models and view models in my program; BaseModel and BaseViewModel.
They look like the following.
public abstract class BaseModel {
public string Name { get; set; }
}
public abstract class BaseViewModel {
protected readonly BaseModel baseModel;
public BaseViewModel(BaseModel baseModel) {
this.baseModel = baseModel;
}
public string Name {
get => baseModel.Name;
set {
baseModel.Name = value;
}
}
}
I also have these two classes that will be used with a specific view in my program; UseableModel and UseableViewModel.
They look like the following.
public class UseableModel : BaseModel {
public string NewVar { get; set; }
}
public class UseableViewModel : BaseViewModel {
public UseableViewModel(UseableModel model) : base(model) { }
public string NewVar {
get => baseModel.NewVar;
set {
baseModel.NewVar = value;
}
}
}
The problem is this obviously won't work as UseableViewModel.NewVar tries to make reference to a property that doesn't exist in BaseModel.
What is the best way to allow every view model to keep the behaviour defined in the BaseViewModel but to use its own model that is extended from BaseModel?
Potential solutions
There are several potential solutions such as write a Name property in each view model but that could be a lot of duplication depending on how many view model there were.
You could also define a separate model in the view model like
//...
private readonly UseableModel model;
public UseableViewModel(UseableModel model) : base(model)
{
this.model = model
}
//...
But then there is the problem of data sets being duplicated and wasting space as well as there being inconsistent data in between the two models.
So is there a way to do that avoids the duplication of method definitions and avoids the issue described immediately above?
You could make BaseModel and BaseViewModel implement generics. Something like:
public abstract class BaseModel<TModel>
{
public string Name { get; set; }
}
public abstract class BaseViewModel<TModel> where TModel : BaseModel<TModel>
{
protected readonly TModel baseModel;
public BaseViewModel(TModel baseModel)
{
this.baseModel = baseModel;
}
public string Name
{
get => baseModel.Name;
set => baseModel.Name = value;
}
}
public class UseableModel : BaseModel<UseableModel>
{
public string NewVar { get; set; }
}
public class UseableViewModel : BaseViewModel<UseableModel>
{
public UseableViewModel(UseableModel model) : base(model) { }
public string NewVar
{
get => baseModel.NewVar;
set => baseModel.NewVar = value;
}
}

Dapper.Contrib AS Entity, Why it fails?

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.

Entity Framework many to many duplicate value in join table when updating entity

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)));

Entity Framework automatic migrations enabled does not work

I have this db configuration
public class AppDbContext : DbContext
{
public AppDbContext(string connectionStringOrName)
: base(connectionStringOrName)
{
Database.SetInitializer(new AppDbInitializer());
}
public AppDbContext()
: this("name=AppDbContext")
{
}
public DbSet<User> Users { get; set; }
public DbSet<Log> Logs { get; set; }
}
and I have this migration configuration
public class AppDbInitializer : MigrateDatabaseToLatestVersion<AppDbContext,AppDbMigrationConfiguration>
{
}
public class AppDbMigrationConfiguration : DbMigrationsConfiguration<AppDbContext>
{
public AppDbMigrationConfiguration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
protected override void Seed(AppDbContext context)
{
if (context.Users.Any()) return;
AddAdmin(context, "Admin", "admin#test.com");
}
}
And I added another field to Log entity.
Can Entity Framework automatically detect and apply changes?
If Automatic Migrations are enabled, it should auto detect any small changes in the model.
But for larger changes, eg addition of new entity, I have seen to manually apply migration, which you can do with "Add-Migration" and then running "Update-Database"

Categories