Castle ActiveRecord Save() will update but not create - c#

I'm updating a program and adding a new table to the database. The program uses Castle's ActiveRecord with repositories. I've set the classes and repository up and can get the test case out of the database fine. However, when I try to add a new record to the database, nothing happens. No error and no new record. If I get the test case out of the database and change something then call Save(), the record in the database changes.
This is the code I'm using to test:
ICountryRepository _countryRepository = Country.GetRepository(site.Id);
//get test case and update
Country c = _countryRepository.FindById(1).Value;
c.Name = "c";
_countryRepository.Save(c);
//create new Country and save
Country d = new Country();
d.Id = 2;
d.Name = "d";
_countryRepository.Save(d);
Now as it's a maintenence project with no real time to stop and study how the Castle framework is doing everything, I'm learning as I go along. I've picked up how to do things by studying the rest of the code and there are occasions in the code where the above code has been used to create new records, so I'm sure Save() is the right function to call.
I've tried dropping the create code in with another part of the code that inserts an object into a different table, just to make sure there weren't different levels of permission in play. There are no differences in the database between the table I added and the table I'm basing my code off.
As I've said, my experiences with ActiveRecord and Castle's framework here are few, it could/will be something quite simple. So hopefully someone can point it out to me.
Thanks.
Edit:
Country Class:
[ActiveRecord("Country")]
[SearchEntity]
public class Country : AbstractEntity<Country, ICountryRepository>
{
int _countryId;
string _name;
int _action;
[PrimaryKey("CountryId")]
public int Id
{
get { return _countryId; }
set { _countryId = value; }
}
[SearchProperty]
[Property(Length = 100)]
public string Name
{
get { return _name; }
set { _name = value; }
}
[Property]
public int PostRegisterActionId
{
get { return _action; }
set { _action = value; }
}
}
AbstractRepository since CountryRepository does nothing at the moment:
[Transient]
public abstract class AbstractRepository<T> : IRepository<T> where T : AbstractEntity, new()
{
#region Private Vars
protected FutureQueryRunner _futureQueryRunner;
protected IDynamicSearchService _dynamicSearchService;
protected bool _caching;
protected int _cachedPages;
protected CachingFutureQueryOfList<T> _cachedQuery;
protected IServiceBus _serviceBus;
private string _entityTypeName = string.Empty;
#endregion
#region Constructors
public AbstractRepository(IDynamicSearchService dynamicSearchService, IServiceBus serviceBus)
{
_dynamicSearchService = dynamicSearchService;
_serviceBus = serviceBus;
}
#endregion
#region Public Methods
public virtual void Save(T instance)
{
ActiveRecordMediator<T>.Save(instance);
}
public virtual void Create(T instance)
{
ActiveRecordMediator<T>.Create(instance);
}
public void Delete(T instance)
{
ActiveRecordMediator<T>.Delete(instance);
}
public virtual IFutureQueryOf<T> New()
{
return new NullFutureQuery<T>();
}
public virtual IFutureQueryOf<T> FindById(int id/*, params string[] eagerAssociations*/) // eager associations buggy
{
DetachedCriteria criteria = GetDefaultCriteria()
.Add(Expression.IdEq(id));
/*foreach (string eager in eagerAssociations)
criteria.SetFetchMode(eager, NHibernate.FetchMode.Eager);*/
return new FutureQueryOf<T>(_futureQueryRunner)
.SetCriteria(criteria);
}
public virtual IFutureQueryOfList<T> FindAll(string sortBy, bool sortAsc)
{
DetachedCriteria criteria = GetDefaultCriteria();
if (!string.IsNullOrEmpty(sortBy))
criteria.AddOrder(sortAsc ? NHibernate.Criterion.Order.Asc(sortBy) : NHibernate.Criterion.Order.Desc(sortBy));
return new FutureQueryOfList<T>(_futureQueryRunner)
.SetCriteria(criteria);
}
public virtual IFutureQueryOfList<T> FindAll(int page, int resultsPerPage, string sortBy, bool sortAsc)
{
return FindAll(new DefaultCriteriaProvider<T>(DetachedCriteria.For<T>()),
page,
resultsPerPage,
sortBy,
sortAsc,
"FindAll");
}
public virtual IFutureQueryOfList<T> FindAll(string searchString, int page, int resultsPerPage, string sortBy, bool sortAsc)
{
return FindAll(new SearchStringCriteriaProvider<T>(_dynamicSearchService, searchString),
page,
resultsPerPage,
sortBy,
sortAsc,
"FindAllSearchString_" + searchString);
}
public virtual IFutureQueryOfList<T> FindAll(IListFilter filter, int page, int resultsPerPage, string sortBy, bool sortAsc)
{
return FindAll(new FilterCriteriaProvider<T>(_dynamicSearchService, filter),
page,
resultsPerPage,
sortBy,
sortAsc,
"FindAllListFilter"); // TODO - the cache key needs to represent individual filters
}
public virtual IFutureQueryOf<int> GetCount(string searchString)
{
return new FutureQueryOf<int>(_futureQueryRunner)
.SetCriteria(AddDefaultCriteria(new SearchStringCriteriaProvider<T>(_dynamicSearchService, searchString).GetDetachedCriteria())
.SetProjection(Projections.RowCount()));
}
public virtual IFutureQueryOf<int> GetCount()
{
return new FutureQueryOf<int>(_futureQueryRunner)
.SetCriteria(GetDefaultCriteria()
.SetProjection(Projections.RowCount()));
}
public virtual string EntityType
{
get
{
if (string.IsNullOrEmpty(_entityTypeName))
_entityTypeName = typeof(T).Name;
return _entityTypeName;
}
}
public IRepository<T> EnableCaching(bool caching)
{
_caching = caching;
return this;
}
public IRepository<T> WithPagesToCache(int cachedPages)
{
_cachedPages = cachedPages;
return this;
}
public virtual IRepository<T> ForSite(int siteId)
{
return this;
}
public IRepository<T> RunFutureQueriesWith(FutureQueryRunner futureQueryRunner)
{
_futureQueryRunner = futureQueryRunner;
return this;
}
#endregion
#region Protected Methods
protected virtual DetachedCriteria AddDefaultCriteria(DetachedCriteria criteria)
{
return criteria;
}
protected DetachedCriteria GetDefaultCriteria()
{
return AddDefaultCriteria(DetachedCriteria.For<T>());
}
protected IFutureQueryOf<U> NewQueryOf<U>(DetachedCriteria criteria)
{
return new FutureQueryOf<U>(_futureQueryRunner).SetCriteria(criteria);
}
protected IFutureQueryOfList<U> NewQueryOfList<U>(DetachedCriteria criteria)
{
return new FutureQueryOfList<U>(_futureQueryRunner).SetCriteria(criteria);
}
#endregion
#region Private Methods
private IFutureQueryOfList<T> FindAll(ICriteriaProvider<T> criteriaProvider, int page, int resultsPerPage, string sortBy, bool sortAsc, string cacheKey)
{
CachingFutureQueryOfList<T> rtnVal = null;
bool cached = false;
if (_cachedQuery != null && _caching)
{
rtnVal = _cachedQuery;
cached = rtnVal.SetPage(page, sortBy, sortAsc, cacheKey);
}
if (!cached)
{
rtnVal = new CachingFutureQueryOfList<T>(_futureQueryRunner, page, _cachedPages, resultsPerPage, cacheKey)
.SetCriteria(AddDefaultCriteria(criteriaProvider.GetDetachedCriteria()), sortBy, sortAsc);
if (_caching)
_cachedQuery = rtnVal;
}
return rtnVal;
}
#endregion
#region Criteria Providers
private interface ICriteriaProvider<U>
{
DetachedCriteria GetDetachedCriteria();
}
private class DefaultCriteriaProvider<U> : ICriteriaProvider<U>
{
private DetachedCriteria _criteria;
public DefaultCriteriaProvider(DetachedCriteria criteria)
{
_criteria = criteria;
}
public DetachedCriteria GetDetachedCriteria()
{
return _criteria;
}
}
private class SearchStringCriteriaProvider<U> : ICriteriaProvider<U>
{
private IDynamicSearchService _searchService;
private string _searchString;
public SearchStringCriteriaProvider(IDynamicSearchService searchService, string searchString)
{
_searchService = searchService;
_searchString = searchString;
}
public DetachedCriteria GetDetachedCriteria()
{
return _searchService.GetDetachedCriteria<U>(_searchString);
}
}
private class FilterCriteriaProvider<U> : ICriteriaProvider<U>
{
private IDynamicSearchService _searchService;
private IListFilter _filter;
public FilterCriteriaProvider(IDynamicSearchService searchService, IListFilter filter)
{
_searchService = searchService;
_filter = filter;
}
public DetachedCriteria GetDetachedCriteria()
{
return _searchService.GetDetachedCriteria<U>(_filter);
}
}
#endregion
}

If you're explicitly setting the Country PK, as it seems you're doing, you probably need to call Create() instead of Save() (I don't know if your repository implementation exposes this).
If this didn't work, please post your class mappings and repository implementation.

Related

MongoDB Composite Key: InvalidOperationException: {document}.Identity is not supported

I am having issues with hydrating a class which consists of a composite ID which in turn has a base class, I am getting an error saying InvalidOperationException: {document}.Identity is not supported.
The class i am trying to write to the database is below:
public class Product : IEntity<Product>
{
public readonly Sku Sku;
public string Name { get; private set; }
public string Description { get; private set; }
public bool IsArchived { get; private set; }
public Identity<Product> Identity => Sku;
public Product(Sku sku, string name, bool isArchived)
{
Sku = sku;
Name = name;
IsArchived = isArchived;
}
}
public interface IEntity<T>
{
Identity<T> Identity { get; }
}
In turn has an ID Sku which is a class formed of the below composite values (VendorId and a local Value within Sku):
public class Sku : Identity<Product>
{
public readonly VendorId VendorId;
public readonly string Value;
public Sku(VendorId vendorId, string value)
{
VendorId = vendorId;
Value = value;
}
protected override IEnumerable<object> GetIdentityComponents()
{
return new object[] {VendorId, Value};
}
}
public class VendorId : Identity<Vendor>
{
public readonly string Value;
public VendorId(string value)
{
Value = value;
}
protected override IEnumerable<object> GetIdentityComponents()
{
return new object[] {Value};
}
}
I have a base class for my entities Identity which i use in my DDD libraries, essentially the ToString() output here could be used as the ID if this would simplify things:
public abstract class Identity<T> : IEquatable<Identity<T>>
{
public override bool Equals(object obj) { /* snip */ }
public bool Equals(Identity<T> other) { /* snip */ }
public override int GetHashCode() { /* snip */ }
public override string ToString()
{
var id = string.Empty;
foreach (var component in GetIdentityComponents())
{
if (string.IsNullOrEmpty(id))
id = component.ToString(); // first item, dont add a divider
else
id += "." + component;
}
return id;
}
protected abstract IEnumerable<object> GetIdentityComponents();
}
I register the mappings on app start:
// rehydrate readonly properties via matched constructor
// https://stackoverflow.com/questions/39604820/serialize-get-only-properties-on-mongodb
ConventionRegistry
.Register(nameof(ImmutablePocoConvention), new ConventionPack { new ImmutablePocoConvention() }, _ => true);
BsonClassMap.RegisterClassMap<Product>(cm =>
{
cm.AutoMap();
cm.MapIdMember(c => c.Sku);
});
BsonClassMap.RegisterClassMap<Vendor>(cm =>
{
cm.AutoMap();
cm.MapIdMember(c => c.Id);
});
However when i go and write, i get InvalidOperationException: {document}.Identity is not supported.
// my respositoru method
public void Upsert<T>(T entity) where T : IEntity<T>
{
this.Database
.GetCollection<T>(product.GetType().FullName)()
.ReplaceOneAsync(x=>x.Identity.Equals(entity.Identity), entity, new UpdateOptions() {IsUpsert = true})
.Wait();
}
var product = new Product(new Sku(new VendorId("dell"), "12434" ),"RAM", false );
myProductRepo.Upsert(product);
Not sure if this is now overly complicated by me persisting direct from my entities layer (or if i just use an automapper and simpler POCO)... or if I am missing some mapping directives.
Appreciate any help or pointers.
I was looking at the hydration via constructor post which is done through GetProperties.
So public readonly Sku Sku; doesn't show up through classMap.ClassType.GetTypeInfo().GetProperties(_bindingFlags) because it is only can be accessed as member field.
You can change it to public Sku Sku { get; } so it is hydrated through constructor via GetProperties and change all the readonly fields (Sku - VendorId, Value & VendorId - Value fields) to have property getter method.
Also, You've to add cm.MapProperty(c => c.Identity) so x=>x.Identity.Equals(entity.Identity) can be serialized when used as expression because Identity cannot be hydrated and registered through ImmutablePocoConventionas it is not a constructor arg when automap logic runs.
Code changes:
public class Sku : Identity<Product>
{
public VendorId VendorId { get; }
public string Value { get; }
}
public class VendorId : Identity<Vendor>
{
public string Value { get; }
}
BsonClassMap.RegisterClassMap<Product>(cm =>
{
cm.AutoMap();
cm.MapIdMember(c => c.Sku);
cm.MapProperty(c => c.Identity);
});
Here is the code i used:
public class ProductMongoRepository : IProductRepository
{
public ICollection<Product> SearchBySkuValue(string sku)
{
return ProductsMongoDatabase.Instance.GetEntityList<Product>();
}
public Product GetBySku(Sku sku)
{
var collection = ProductsMongoDatabase.Instance.GetCollection<Product>();
return collection.Find(x => x.Sku.Equals(sku)).First();
}
public void SaveAll(IEnumerable<Product> products)
{
foreach (var product in products)
{
Save(product);
}
}
public void Save(Product product)
{
var collection = ProductsMongoDatabase.Instance.GetCollection<Product>();
collection
.ReplaceOneAsync(
x => x.Sku.Equals(product.Sku),
product,
new UpdateOptions() { IsUpsert = true })
.Wait();
}
}
Setting up the mapping here and support for readonly fields via constructor, for more complex scenarios and manual POCO mapping we could use BsonSerializer.RegisterSerializer(typeof(DomainEntityClass), new CustomerSerializer());
public sealed class ProductsMongoDatabase : MongoDatabase
{
private static volatile ProductsMongoDatabase instance;
private static readonly object SyncRoot = new Object();
private ProductsMongoDatabase()
{
BsonClassMap.RegisterClassMap<Sku>(cm =>
{
cm.MapField(c => c.VendorId);
cm.MapField(c => c.SkuValue);
cm.MapCreator(c => new Sku(new VendorId(c.VendorId.VendorShortname), c.SkuValue));
});
BsonClassMap.RegisterClassMap<VendorId>(cm =>
{
cm.MapField(c => c.VendorShortname);
cm.MapCreator(c => new VendorId(c.VendorShortname));
});
BsonClassMap.RegisterClassMap<Product>(cm =>
{
cm.AutoMap();
cm.MapIdMember(c => c.Sku);
cm.MapCreator(c => new Product(c.Sku, c.Name, c.IsArchived));
});
BsonClassMap.RegisterClassMap<Vendor>(cm =>
{
cm.AutoMap();
cm.MapIdMember(c => c.Id);
cm.MapCreator(c => new Vendor(c.Id, c.Name));
});
}
public static ProductsMongoDatabase Instance
{
get
{
if (instance != null)
return instance;
lock (SyncRoot)
{
if (instance == null)
instance = new ProductsMongoDatabase();
}
return instance;
}
}
}
The above implementation (which is a singleton) derives from the below base (any queries or writes are done in the parent implementation):
public abstract class MongoDatabase
{
private readonly IConfigurationRepository _configuration;
private readonly IMongoClient Client;
private readonly IMongoDatabase Database;
protected MongoDatabase()
{
//_configuration = configuration;
var connection = "mongodb://host:27017";
var database = "test";
this.Client = new MongoClient();
this.Database = this.Client.GetDatabase(database);
}
public List<T> GetEntityList<T>()
{
return GetCollection<T>()
.Find(new BsonDocument()).ToList<T>();
}
public IMongoCollection<T> GetCollection<T>()
{
return this.Database.GetCollection<T>(typeof(T).FullName);
}
}
My Sku domain model:
public class Sku : Identity<Product>
{
public readonly VendorId VendorId;
public readonly string SkuValue;
public Sku(VendorId vendorId, string skuValue)
{
VendorId = vendorId;
SkuValue = skuValue;
}
protected override IEnumerable<object> GetIdentityComponents()
{
return new object[] {VendorId, SkuValue};
}
}
My Product domain model:
public class Product : IEntity<Product>
{
public readonly Sku Sku;
public string Name { get; private set; }
public bool IsArchived { get; private set; }
public Product(Sku sku, string name, bool isArchived)
{
Sku = sku;
Name = name;
IsArchived = isArchived;
}
public void UpdateName(string name)
{
Name = name;
}
public void UpdateDescription(string description)
{
Description = description;
}
public void Archive()
{
IsArchived = true;
}
public void Restore()
{
IsArchived = false;
}
// this is used by my framework, not MongoDB
public Identity<Product> Identity => Sku;
}
My VendorID:
public class VendorId : Identity<Vendor>
{
public readonly string VendorShortname;
public VendorId(string vendorShortname)
{
VendorShortname = vendorShortname;
}
protected override IEnumerable<object> GetIdentityComponents()
{
return new object[] {VendorShortname};
}
}
Then i have my entity and identity types:
public interface IEntity<T>
{
Identity<T> Identity { get; }
}
public abstract class Identity<T> : IEquatable<Identity<T>>
{
private const string IdentityComponentDivider = ".";
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj)) return true;
if (ReferenceEquals(null, obj)) return false;
if (GetType() != obj.GetType()) return false;
var other = obj as Identity<T>;
return other != null && GetIdentityComponents().SequenceEqual(other.GetIdentityComponents());
}
public override string ToString()
{
var id = string.Empty;
foreach (var component in GetIdentityComponents())
{
if (string.IsNullOrEmpty(id))
id = component.ToString(); // first item, dont add a divider
else
id += IdentityComponentDivider + component;
}
return id;
}
protected abstract IEnumerable<object> GetIdentityComponents();
public override int GetHashCode()
{
return HashCodeHelper.CombineHashCodes(GetIdentityComponents());
}
public bool Equals(Identity<T> other)
{
return Equals(other as object);
}
}

Transaction script implementation

Could you please advice me which of these two (if any) approaches to Transaction Script patter is correct?
Basically I need to implement "pure" TS, which is not to bend it in some convenient way. And the question is when I get data from Table Data Gateway shall I store them, for different parts (logical part for sake of clarity) of transaction and use them where do I need, OR get data don't store them but directly use them and if the same data are needed again call table gateway and ask for it.
Example: http://pastebin.com/hGCgrfEs
Thanks. Hopefully it makes sense if not let me know :)
The TSDoSomething2 class is closer to a good solution, i propose this to implement a SOLID transcript implementation:
public class OrderBusiness : IAcceptVisitOrder
{
private readonly string _id;
private int _status;
public OrderBusiness(string id, int status)
{
_id = id;
_status = status;
}
public void ChangeStatus(int newStatus)
{
//validation logic....
_status = newStatus;
}
public void Accept(IOrderVisitor visitor)
{
if (visitor == null) throw new ArgumentNullException(nameof(visitor));
visitor.Visit(_id, _status);
}
}
public class Client
{
private readonly OrderApplicationService _service;
//Get from Service locator like ninject, etc
private readonly IReader<OrderBusiness, string> _reader = new OrderBusinessReader();
private readonly IOrderVisitor _visitor = new UpdateOrderVisitor();
public Client()
{
_service = new OrderApplicationService(_reader, _visitor);
}
public void Run(UpdateOrderCommand command)
{
_service.When(command);
}
}
public class UpdateOrderCommand
{
[Required]
public string Id { get; set; }
[Required]
public int Status { get; set; }
}
public class OrderApplicationService
{
private readonly IReader<OrderBusiness, string> _reader;
private readonly IOrderVisitor _visitor;
public OrderApplicationService(IReader<OrderBusiness, string> reader, IOrderVisitor visitor)
{
if (reader == null) throw new ArgumentNullException(nameof(reader));
if (visitor == null) throw new ArgumentNullException(nameof(visitor));
_reader = reader;
_visitor = visitor;
}
public void When(UpdateOrderCommand command)
{
var order = GetBusinessObject(command.Id);
order.ChangeStatus(command.Status);
order.Accept(_visitor);
}
private OrderBusiness GetBusinessObject(string id)
{
if (id == null) throw new ArgumentNullException(nameof(id));
return _reader.Read(id);
}
}
public interface IReader<out T, in TK>
{
T Read(TK id);
}
public interface IAcceptVisitOrder
{
void Accept(IOrderVisitor visitor);
}
public interface IOrderVisitor
{
void Visit(string id, int status);
}
public class OrderBusinessReader : IReader<OrderBusiness, string>
{
public OrderBusiness Read(string id)
{
//Read data from db with EF, ADO.NET, ETC
var status = 0;
return new OrderBusiness(id, status);
}
}
class UpdateOrderVisitor : IOrderVisitor
{
public void Visit(string id, int status)
{
//Persist to database, etc
}
}
Hope this helps!Regards!

Set tableAttribute mapping dynamically in linq to sql

I was wondering if it is possible to dynamically set the table mapping for the linq classes:
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.[Comfort 0601$Contact]")]
public partial class Contact : INotifyPropertyChanging, INotifyPropertyChanged
{
...
The reason I want to change this dynamically or programmatically is because the database I'm using is created by Navision. There the tables have a prefix of the company name to which these tables belong to. Therefor I would like to be able to change that prefix.
[global::System.Data.Linq.Mapping.TableAttribute(Name = "dbo.["+SPResources.Database.Company+"$Contact]")]
whit Company as:
public const string Company = "Comfort 0601";
I've tried this, but it only work if i declared Company as a constant.
The table structure between companies are identical. just the name & content change.
I hope someone can give some advice about this. I'm also not sure if it even is possible.
Think the correct solution for adding table-prefix is to use SQL Server 2005 (Or newer), and then create a db-user for each required prefix, and change default schema for each db-user from "dbo" to something unique.
Another solution is to extend the existing DataContext with the ability to "correct" the MappingSource at runtime:
[Table] // Look no name
public class DbRecord
{
[Column(IsPrimaryKey=true)]
public long Id { get; set; }
}
CustomDataContext.UpdateCustomTable(typeof(DbRecord), "DbTable");
using (CustomDataContext dc = new CustomDataContext(dbConnection))
{
Table<DbRecord> dbTable = dc.GetTable<DbRecord>();
var query = from item in dbTable;
}
This is the custom DataContext (I guess one could easily change it to add a prefix to an existing table-name) (Replace Dictionary with ConcurrentDictionary if in multi-threaded environment):
public class CustomDataContext : DataContext
{
static CustomMappingSource _sharedMappingSource = new CustomMappingSource();
public static void UpdateCustomTable(Type rowType, string tableName)
{
_sharedMappingSource.UpdateCustomTable(rowType, tableName);
}
public CustomDataContext(System.Data.IDbConnection connection) : base(connection, _sharedMappingSource) { }
public CustomDataContext(string fileOrServerOrConnection) : base(fileOrServerOrConnection, _sharedMappingSource) { }
}
internal class CustomMappingSource : MappingSource
{
AttributeMappingSource mapping = new AttributeMappingSource();
Dictionary<Type, string> _customTableNames = new Dictionary<Type, string>();
public void UpdateCustomTable(Type rowType, string tableName)
{
if (string.IsNullOrEmpty(tableName))
throw new ArgumentNullException("TableName");
_customTableNames[rowType] = tableName;
}
protected override MetaModel CreateModel(Type dataContextType)
{
MetaModel oldmodel = mapping.GetModel(dataContextType);
CustomMetaModel newmodel = new CustomMetaModel(oldmodel, _customTableNames);
return newmodel;
}
}
internal class CustomMetaModel : MetaModel
{
MetaModel _orgmodel;
Dictionary<Type, MetaTable> _customtables = new Dictionary<Type, MetaTable>();
Dictionary<Type, string> _tableNames = new Dictionary<Type, string>();
public CustomMetaModel(MetaModel orgmodel, Dictionary<Type, string> tableNames)
{
_orgmodel = orgmodel;
_tableNames = tableNames;
}
public override MetaType GetMetaType(Type type)
{
MetaTable metaTable;
if (_customtables.TryGetValue(type, out metaTable))
return metaTable.RowType;
else
return _orgmodel.GetMetaType(type);
}
public override MetaTable GetTable(Type rowType)
{
MetaTable customMetaTable;
if (_customtables.TryGetValue(rowType, out customMetaTable))
return customMetaTable;
if (_tableNames.ContainsKey(rowType))
{
MetaTable orgtable = _orgmodel.GetTable(rowType);
MetaType orgrowtype = orgtable.RowType;
CustomMetaType newRowType = new CustomMetaType(orgrowtype, this);
_customtables.Add(rowType, new CustomMetaTable(orgtable, this, newRowType, _tableNames[rowType]));
newRowType.MetaTable = _customtables[rowType];
return newRowType.MetaTable;
}
return _orgmodel.GetTable(rowType);
}
#region MetaModel Forwards
public override Type ContextType { get { return _orgmodel.ContextType; } }
public override string DatabaseName { get { return _orgmodel.DatabaseName; } }
public override MappingSource MappingSource { get { return _orgmodel.MappingSource; } }
public override Type ProviderType { get { return _orgmodel.ProviderType; } }
public override MetaFunction GetFunction(System.Reflection.MethodInfo method) { return _orgmodel.GetFunction(method); }
public override IEnumerable<MetaFunction> GetFunctions() { return _orgmodel.GetFunctions(); }
public override IEnumerable<MetaTable> GetTables() { return _orgmodel.GetTables(); }
#endregion
}
internal class CustomMetaTable : MetaTable
{
MetaTable _orgtable;
MetaModel _metamodel;
MetaType _rowtype;
string _tableName;
public CustomMetaTable(MetaTable orgtable, MetaModel metamodel, MetaType rowtype, string tableName)
{
_orgtable = orgtable;
_metamodel = metamodel;
_rowtype = rowtype;
_tableName = tableName;
}
public override MetaModel Model { get { return _metamodel; } }
public override MetaType RowType { get { return _rowtype; } }
public override string TableName { get { return _tableName; } }
#region MetaTable Forwards
public override System.Reflection.MethodInfo DeleteMethod { get { return _orgtable.DeleteMethod; } }
public override System.Reflection.MethodInfo InsertMethod { get { return _orgtable.InsertMethod; } }
public override System.Reflection.MethodInfo UpdateMethod { get { return _orgtable.UpdateMethod; } }
#endregion
}
internal class CustomMetaType : MetaType
{
MetaType _orgtype;
MetaModel _metamodel;
public MetaTable MetaTable { get; set; }
public CustomMetaType(MetaType orgtype, MetaModel metamodel)
{
_orgtype = orgtype;
_metamodel = metamodel;
}
public override MetaTable Table { get { return MetaTable; } }
public override MetaModel Model { get { return _metamodel; } }
#region MetaType Forwards
public override System.Collections.ObjectModel.ReadOnlyCollection<MetaAssociation> Associations { get { return _orgtype.Associations; } }
public override bool CanInstantiate { get { return _orgtype.CanInstantiate; } }
public override System.Collections.ObjectModel.ReadOnlyCollection<MetaDataMember> DataMembers { get { return _orgtype.DataMembers; } }
public override MetaDataMember DBGeneratedIdentityMember { get { return _orgtype.DBGeneratedIdentityMember; } }
public override System.Collections.ObjectModel.ReadOnlyCollection<MetaType> DerivedTypes { get { return _orgtype.DerivedTypes; } }
public override MetaDataMember Discriminator { get { return _orgtype.Discriminator; } }
public override bool HasAnyLoadMethod { get { return _orgtype.HasAnyLoadMethod; } }
public override bool HasAnyValidateMethod { get { return _orgtype.HasAnyValidateMethod; } }
public override bool HasInheritance { get { return _orgtype.HasInheritance; } }
public override bool HasInheritanceCode { get { return _orgtype.HasInheritanceCode; } }
public override bool HasUpdateCheck { get { return _orgtype.HasUpdateCheck; } }
public override System.Collections.ObjectModel.ReadOnlyCollection<MetaDataMember> IdentityMembers { get { return _orgtype.IdentityMembers; } }
public override MetaType InheritanceBase { get { return _orgtype.InheritanceBase; } }
public override object InheritanceCode { get { return _orgtype.InheritanceCode; } }
public override MetaType InheritanceDefault { get { return _orgtype.InheritanceDefault; } }
public override MetaType InheritanceRoot { get { return _orgtype.InheritanceRoot; } }
public override System.Collections.ObjectModel.ReadOnlyCollection<MetaType> InheritanceTypes { get { return _orgtype.InheritanceTypes; } }
public override bool IsEntity { get { return _orgtype.IsEntity; } }
public override bool IsInheritanceDefault { get { return _orgtype.IsInheritanceDefault; } }
public override string Name { get { return _orgtype.Name; } }
public override System.Reflection.MethodInfo OnLoadedMethod { get { return _orgtype.OnLoadedMethod; } }
public override System.Reflection.MethodInfo OnValidateMethod { get { return _orgtype.OnValidateMethod; } }
public override System.Collections.ObjectModel.ReadOnlyCollection<MetaDataMember> PersistentDataMembers { get { return _orgtype.PersistentDataMembers; } }
public override Type Type { get { return _orgtype.Type; } }
public override MetaDataMember VersionMember { get { return _orgtype.VersionMember; } }
public override MetaDataMember GetDataMember(System.Reflection.MemberInfo member) { return _orgtype.GetDataMember(member); }
public override MetaType GetInheritanceType(Type type) { return _orgtype.GetInheritanceType(type); }
public override MetaType GetTypeForInheritanceCode(object code) { return _orgtype.GetTypeForInheritanceCode(code); }
#endregion
}
The answers of Brian and Gert are possible solutions for this problem. But changing the mapping at run-time is just not possible (as Brian mentioned).
As our project progressed we decided that if we would need another company name (which would mean that that build is for a different customer) we would just rebuild the project. That decision made the following solution possible:
We commented out the original mappings generated by Visual Studio:
//[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.[Comfort 0601$Contact]")]
public partial class Contact : INotifyPropertyChanging, INotifyPropertyChanged
{
...
Because we have our own partial class for every Linq class, we added the modified mapping at the top of our own class:
[global::System.Data.Linq.Mapping.TableAttribute(Name = "dbo.["+SPResources.Database.Company+"$Contact]")]
public partial class Contact
{
where our company will be:
public const string Company = "Comfort 0601";
If a rebuild is needed for a different company we simply change the Company variable.
Note: This only works properly if you don't change your *.dbml file in VS. If you do, VS will automatically undo your changes.
Edit: As time has passed and I started to look more into the Entity Framework I found a more specific and suitable solution for this problem: http://www.codeproject.com/Articles/421643/How-to-Use-MVC-Net-on-the-Dynamics-NAV-Database-St
You could use SQLMetal to custom generate the code for each entities. You cannot apply attributes dynamically at runtime, so this would be the best option.
If you'd work code-first you could use fluent mapping you can use the ToTable() method to configure the database table at runtime (see under Changing the Database Table Name). E.g. in a OnModelCreating() override of your DbContext derivative:
string company = "Comfort 0601";
modelBuilder.Entity<Contact>().ToTable(company + "Contact");
modelBuilder.Entity<....
modelBuilder.Entity<....
You could take the company name from a config file.

Making Validation Generic

I have the following C# code. Here the validations are kept outside the class to satisfy Open – Closed Principle. This is working fine. But the challenge is – the validations are not generic. It is specific to employee class (E.g DateOfBirthRuleForEmployee). How do I make the validations generic for all objects (DateOfBirthRuleForAnyObject).
Note: Make Generic <==> Make Type-Independent
Note: I have NameLengthRuleForEmployee validation also. New validation may come in future.
EDIT
Generic Method Example: Using “OfType” in LINQ
CODE
class Program
{
static void Main(string[] args)
{
Employee employee = new Employee();
employee.DateOfBirth = DateTime.Now;
employee.Name = "Lijo";
DateOfBirthRuleForEmployee dobRule = new
DateOfBirthRuleForEmployee();
NameLengthRuleForEmployee nameRule = new
NameLengthRuleForEmployee();
EmployeeManager employeeManager = new EmployeeManager();
employeeManager.AddRules(dobRule);
employeeManager.AddRules(nameRule);
bool result = employeeManager.validateEntity(employee);
Console.WriteLine(result);
Console.ReadLine();
}
}
public interface IEntity
{
}
public interface IRule<TEntity>
{
bool IsValid(TEntity entity);
}
public class DateOfBirthRuleForEmployee : IRule<Employee>
{
public bool IsValid(Employee entity)
{
return (entity.DateOfBirth.Year <= 1975);
}
}
public class NameLengthRuleForEmployee : IRule<Employee>
{
public bool IsValid(Employee employee)
{
return (employee.Name.Length < 5);
}
}
public class Employee : IEntity
{
private DateTime dateOfBirth;
private string name;
public DateTime DateOfBirth
{
get
{
return dateOfBirth;
}
set
{
dateOfBirth = value;
}
}
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
}
public class EmployeeManager
{
RulesEngine<Employee> engine = new RulesEngine<Employee>();
public void AddRules(IRule<Employee> rule)
{
engine.AddRules(rule);
//engine.AddRules(new NameLengthRuleForEmployee());
}
public bool validateEntity(Employee employee)
{
List<IRule<Employee>> rulesList = engine.GetRulesList();
//No need for type checking. Overcame Invariance problem
bool status = true;
foreach (IRule<Employee> theRule in rulesList)
{
if (!theRule.IsValid(employee))
{
status = false;
break;
}
}
return status;
}
}
public class RulesEngine<TEntity> where TEntity : IEntity
{
private List<IRule<TEntity>> ruleList = new
List<IRule<TEntity>>();
public void AddRules(IRule<TEntity> rule)
{
//invariance is the key term
ruleList.Add(rule);
}
public List<IRule<TEntity>> GetRulesList()
{
return ruleList;
}
}
The challange is for your rules to know which property of what type to validate. You can either provide this by implementing an interface that provides just that as suggested by SLaks or by quessing it dynamically or by providing a concrete rule class with a bit more information on how to access the given property, e.g.:
class NameRule<T> : IRule<T>
{
private Func<T, string> _nameAccessor;
public NameRule(Func<T, string> nameAccessor)
{
_nameAccessor = nameAccessor;
}
public bool IsValid(T instance)
{
return _nameAccessor(instance).Length > 10;
}
}
this ofcourse can be used in the following way:
NameRule<Employee> employeeNameRule = new NameRule<Employee>(x => x.name);
employeeManager.addRule(employeeNameRule);

NHibernate and State Pattern persistance - a good implementation?

Below is my implementation of the state pattern. In order to persist the State object to my database with NHibernate, I am assigning each state class an enum value. This is stored as a private field on the entity, and mapped to a integer field in my database table.
I want to know whether this is a good implementation as I will be using the state pattern throughout my application and want to get it right the first time. Thanks
public class Order
{
private OrderStatusEnum _statusId;
public virtual Guid Id { get; set; }
private OrderState _status;
public virtual OrderState Status {
get
{
if (_status == null)
_status = GetState(_statusId);
return _status;
}
set
{
_status = value;
_statusId = _status.Id;
}
}
private OrderState GetState(OrderStatusEnum status)
{
switch (_statusId) {
case OrderStatusEnum.Pending:
return new Submitted(this);
case OrderStatusEnum.Completed:
return new Completed(this);
default:
return new NewOrder(this);
}
}
}
public abstract class OrderState
{
private readonly Order _order;
public OrderState(Order order) {
_order = order;
}
internal Order Order { get { return _order; } }
public abstract OrderStatusEnum Id { get; }
public virtual void Submit() {
throw new InvalidOperationException(
string.Format("Can't Submit a {0} Order", this.GetType().Name)
);
}
public virtual void Complete() {
throw new InvalidOperationException(
string.Format(string.Format("Can't Cancel a {0} Order", this.GetType().Name))
);
}
protected internal void _Submit() {
Order.Status = new Submitted(Order);
}
protected internal void _Complete() {
Order.Status = new Completed(Order);
}
}
public class NewOrder : OrderState
{
public NewOrder(Order order) : base(order) { }
public override OrderStatusEnum Id {
get { return OrderStatusEnum.New; }
}
public override void Submit() {
_Submit();
}
}
public class Submitted : OrderState
{
public Submitted(Order order) : base(order) { }
public override OrderStatusEnum Id {
get { return OrderStatusEnum.Pending; }
}
public override void Complete() {
_Complete();
}
}
public class Completed : OrderState
{
public Completed(Order order) : base(order) { }
public override OrderStatusEnum Id {
get { return OrderStatusEnum.Completed; }
}
}
public enum OrderStatusEnum {
New = 1,
Pending = 2,
Completed = 3
}
Not sure whether to answer or add a comment, but your approach worked very well for me in a similar situation.
I also experimented with the approach described here using the Tarantino framework, but I found it easier to extend from your code.

Categories

Resources