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.
Related
I want to create builder for my purpose, with such call chain:
User user = new CommonBuilder(new UserNode()).Root //generic parameter, currently is User
.Group.Group.Folder.Build();
Here is the code, which I use:
public abstract class AbstractNode
{
public Guid Id { get; } = Guid.NewGuid();
}
public abstract class AbstractNode<T> where T : AbstractNode<T>
{
}
public class CommonBuilder<T> where T : AbstractNode<T>
{
public T Root { get; private set; }
public CommonBuilder(T root)
{
Root = root;
}
}
public class UserNode : AbstractNode<UserNode>
{
private GroupNode _group;
public GroupNode Group
{
get
{
if (_group is null)
{
_group = new GroupNode();
}
return _group;
}
}
}
public class GroupNode : AbstractNode<GroupNode>
{
private GroupNode _group;
public GroupNode Group
{
get
{
if (_group is null)
{
_group = new GroupNode();
}
return _group;
}
}
private FolderNode _folder;
public FolderNode Folder
{
get
{
if (_folder is null)
{
_folder = new FolderNode();
}
return _folder;
}
}
}
public class FolderNode : AbstractNode<FolderNode>
{
}
The problem is in the Build() method, which need to return Root from CommonBuilder, not the File.
Where must I place Build() method, which must be always called at the end of a chain, which returns Root of a builder?
In case when it's required to make a chain the same object should be returned, even as another interface check first and second examples of implementation Builder with Fluent intefaces
I've tried to implement your case to fit the role, check if it will fits your requirements:
public interface IGroup<T>
{
IGroup<T> Group { get; }
IFolder<T> Folder { get; }
}
public interface IFolder<T>
{
T Build();
}
Builder implements all required interfaces. And returns itself in each call. In general you can put Build method in the builder itself and call it separately after the end of chain execution.
public class CommonBuilder<T> : IGroup<T>, IFolder<T> where T: INode, new()
{
private T _root = new T();
public T Build()
{
return _root;
}
public IGroup<T> Group
{
get
{
_root.MoveToGroup();
return this;
}
}
public IFolder<T> Folder
{
get
{
_root.MoveToFolder();
return this;
}
}
}
Because of generics it's required to set some limitations on generic parameter which is done with INode interface
public interface INode
{
void MoveToGroup();
void MoveToFolder();
}
Testing user object
public class User : INode
{
public StringBuilder Path { get; } = new StringBuilder();
public void MoveToFolder()
{
Path.AppendLine("Folder");
}
public void MoveToGroup()
{
Path.AppendLine("Group");
}
public override string ToString()
{
return Path.ToString();
}
}
And the call will looks like
var user = new CommonBuilder<User>().Group.Group.Folder.Build();
EDIT
Maybe as a the first stage it makes sence to get rid of Fluent interfaces and implement logic using just a Builder:
public class FolderNode : INode<Folder>
{
private readonly Folder _folder = new Folder();
public Folder Build()
{
return _folder;
}
public void AppendGroup()
{
_folder.Path.AppendLine("Folder Group");
}
public void AppendFolder()
{
_folder.Path.AppendLine("Folder Folder");
}
}
public class UserNode : INode<User>
{
private readonly User _user = new User();
public User Build()
{
return _user;
}
public void AppendGroup()
{
_user.Path.AppendLine("Group");
}
public void AppendFolder()
{
_user.Path.AppendLine("Folder");
}
}
public class CommonBuilder<T, TNode> where TNode : INode<T>
{
private readonly TNode _root;
public CommonBuilder(TNode root)
{
_root = root;
}
public T Build()
{
return _root.Build();
}
public CommonBuilder<T, TNode> Group {
get
{
_root.AppendGroup();
return this;
}
}
public CommonBuilder<T, TNode> Folder {
get
{
_root.AppendFolder();
return this;
}
}
}
public interface INode<out T>
{
T Build();
void AppendGroup();
void AppendFolder();
}
public class Folder
{
public StringBuilder Path { get; } = new StringBuilder();
public override string ToString()
{
return Path.ToString();
}
}
public class User
{
public StringBuilder Path { get; } = new StringBuilder();
public override string ToString()
{
return Path.ToString();
}
}
Usage:
var user = new CommonBuilder<User, UserNode>(new UserNode()).Group.Group.Folder.Build();
var folder = new CommonBuilder<Folder, FolderNode>(new FolderNode()).Group.Folder.Group.Folder.Build();
I am currently having an issue where I am trying to do a check in a workflow before saving the custom form content item as a submission. Once the CreateAndPublish workflow is hit, it actually creates the item, but the field values aren't being saved properly.
Here is my workflow:
public class CreateAndPublishActivity : Task {
private readonly IContentManager _contentManager;
public CreateAndPublishActivity(IContentManager contentManager) {
_contentManager = contentManager;
}
public Localizer T { get; set; }
public override bool CanExecute(WorkflowContext workflowContext, ActivityContext activityContext) {
return true;
}
public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext workflowContext, ActivityContext activityContext) {
return new[] { T("Done") };
}
public override IEnumerable<LocalizedString> Execute(WorkflowContext workflowContext, ActivityContext activityContext) {
_contentManager.Create(workflowContext.Content.ContentItem, VersionOptions.Published);
yield return T("Done");
}
public override string Name {
get { return "CreateAndPublish"; }
}
public override LocalizedString Category {
get { return T("Content Items"); }
}
public override LocalizedString Description {
get { return T("Create and Publish the content item."); }
}
}
The content item shows up under submissions, but none of the field values are there.
UPDATED
I am updating this post because I did some more reading and decided to re-implement my solution.
Original Problem: I have a class with static properties and one Property that is a dynamic collection of properties (via a dictionary). I want to databind my class to a wpf datagrid where each static property should be a column and each dictionary entry should be a column in the grid.
After doing some more research, I decided to implement a PropertyBag class that will contain my Dictionary of properties and values. Almost everything is working now. I have my grid being displayed with all the correct columns and the static property values are being applied correctly.
However, now I am not able to get any of the values from the dictionary to be applied to the grid, and I am not sure where to go from here.
More info:
My database has 3 tables, a plate, a category, and a categoryplateassociation table. Each plate can have 0 to many categories. For now, I am populating each plate with all the categories and setting the strings to empty. Then, when an association is returned (between a plate and category), I am setting the real value on the specific category name. This all happens before the grid is created.
Property Bag:
public class PropertyBag
{
private readonly Dictionary<string, string> values = new Dictionary<string, string>();
public string this[string key]
{
get
{
string value;
values.TryGetValue(key, out value);
return value;
}
set
{
if (value == null) values.Remove(key);
else values[key] = value;
}
}
}
Revised Plate class
[TypeDescriptionProvider(typeof(PlateTypeDescriptionProvider))]
public class Plate : INotifyPropertyChanged
{
public int ID;
private string name;
private string status;
private string creator;
private Uri location;
private string description;
public Plate()
{
CustomCategories = new PropertyBag();
}
public PropertyBag CustomCategories { get; set; }
public string Name
{
get { return name;}
set
{
name = value;
NotifyPropertyChanged("Name");
}
}
public string Status
{
get { return status; }
set
{
status = value;
NotifyPropertyChanged("Status");
}
}
public string Creator
{
get { return creator; }
set
{
creator = value;
NotifyPropertyChanged("Creator");
}
}
public Uri Location
{
get { return location; }
set
{
location = value;
NotifyPropertyChanged("Location");
}
}
public string Description
{
get { return description; }
set
{
description = value;
NotifyPropertyChanged("Description");
}
}
public static Plate ConvertDataPlateToBusinessPlate(TestPlate dataPlate)
{
var plate = new Plate
{
Name = dataPlate.Name,
Status = dataPlate.Status,
Creator = dataPlate.Creator,
Description = dataPlate.Description,
Location = new Uri(dataPlate.Location)
};
return plate;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Revised CustomTypeDescriptor:
public override PropertyDescriptorCollection GetProperties()
{
return GetProperties(null);
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = new ArrayList();
foreach (PropertyDescriptor propertyDescriptor in base.GetProperties(attributes))
{
if(propertyDescriptor.PropertyType.Equals(typeof(PropertyBag)))
{
//Static list of all category names
var categoryNames = Categories.GetAll();
foreach (var categoryName in categoryNames)
{
properties.Add(new PropertyBagPropertyDescriptor(categoryName));
}
}
else
{
properties.Add(propertyDescriptor);
}
}
var props = (PropertyDescriptor[])properties.ToArray(typeof(PropertyDescriptor));
return new PropertyDescriptorCollection(props);
}
Revised PropertyDescriptor
public class PropertyBagPropertyDescriptor : PropertyDescriptor
{
public PropertyBagPropertyDescriptor(string name) : base(name, null)
{}
public override bool CanResetValue(object component)
{
return true;
}
public override object GetValue(object component)
{
return ((PropertyBag) component)[Name];
}
public override void ResetValue(object component)
{
((PropertyBag)component)[Name] = null;
}
public override void SetValue(object component, object value)
{
((PropertyBag) component)[Name] = (string) value;
}
public override bool ShouldSerializeValue(object component)
{
return ((PropertyBag)component)[Name] != null;
}
public override Type ComponentType
{
get { return typeof(PropertyBag); }
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type PropertyType
{
get { return typeof(string); }
}
}
simple ViewModel
public TestPlateAdministratorViewModel()
{
CommandAggregator = new TestPlateAdministratorCommandAggregator(this);
LoadData();
}
public static TestPlateAdministratorCommandAggregator CommandAggregator { get; set; }
public ObservableCollection<Plate> TestPlates{ get; set; }
private static void LoadData()
{
CommandAggregator.LoadPlatesCommand.Execute(null);
CommandAggregator.LoadCategoriesCommand.Execute(null);
}
}
does your Dictionary in your PropertyBag has a fixed size or does the keys are known?
you did not post your xaml for your datagrid, but the binding from the propertybag to one column could look like this:
<DataGridTextColumn Header="Col4TestKey" Binding="{Binding CustomCategories[test]}"/>
i really dont know wether your PropertyBag setter will work with binding. all in all this just would work if you have a know set of keys for your dictionary.
btw, i use flat datatables in my projects for such dynamic stuff, there are really easy to handle.
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.
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.