I have a simple class that looks like this:
public class Item {
// some properties
public virtual IDictionary<string, Detail> Details { get; private set; }
}
and then I have a map that looks like this:
map.HasMany(x => x.Details).AsMap<string>("Name").AsIndexedCollection<string>("Name", c => c.GetIndexMapping()).Cascade.All().KeyColumn("Item_Id"))
with this map I get the following error and I don't know how to solve it?
The type or method has 2 generic parameter(s), but 1 generic argument(s) were provided. A generic argument must be provided for each generic parameter.
I found a workaround for this. Basically I'm preventing the automapper from attempting to map an IDictionary. It forces me to have to map it manually in an override but at least it works.
I'm using an AutomappingConfiguration derived from DefaultAutomappingConfiguration.
public override bool ShouldMap(Member member)
{
if ( member.PropertyType.IsGenericType )
{
if (member.PropertyType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IDictionary<,>))
return false;
}
return base.ShouldMap(member);
}
And here's a couple of sample classes and the associated mapping that I'm using to make this happen:
public class ComponentA
{
public virtual string Name { get; set; }
}
public class EntityF : Entity
{
private IDictionary<string, ComponentA> _components = new Dictionary<string, ComponentA>();
public IDictionary<string, ComponentA> Components
{
get { return _components; }
set { _components = value; }
}
}
public class EntityFMap : IAutoMappingOverride<EntityF>
{
public void Override(AutoMapping<EntityF> mapping)
{
mapping.HasMany<ComponentA>(x => x.Components)
.AsMap<string>("IndexKey")
.KeyColumn("EntityF_Id")
.Table("EntityF_Components")
.Component(x =>
{
x.Map(c => c.Name);
})
.Cascade.AllDeleteOrphan();
}
}
I've just spent several hours to make this work, so I hope this saves someone else an evening of hair-pulling.
Related
I have a database that has multiple tables that are objects in my application, Member, Employer, Invoice etc.
I want to create a generic method that will allow me to retrieve a single object from any of the tables. ex. public Object GetRow(ClassType type, ClassKey key, object valueToFind)
so the Member method right now would be
Member member = _manager.Members.Where(m => m.MemberKey == valueToFind).FirstOrDefauilt();
Employer employer = _manager.Employers.Where(e => e.EmployerKey == valueToFind).FirstOrDefault();
Invoice invoice = _manager.Invoices.Where(i => i.InvoiceKey == valueToFind).FirstOrDefault();
How do i write a generic method that would handle all 3 cases?
I am assuming you are using entity framework. In such case you can write a method like:
public T RetrieveFirst<T>( Expression<Func<T, bool>> filter)
{
return _manager.Set<T>().FirstOrDefault(filter);
}
You can then use it as follows:
var member = RetrieveFirst<Member>(m => m. MemberKey = valueToFind );
If you wanted to unify the filtering, the entities would all have to implement an interface and have the same name of the key property.
I think #ajawad987 has written the perfect answer for you :-)
You could implement an interface that all your entities (employee, manager, etc.) inherit and then implement a generic method that works with that interface.
Basically you define a simple interface that defines the common Key property:
public interface IHasKeyProperty
{
int Key { get; set; }
}
public class Manager : IHasKeyProperty
{
public int Key { get; set; }
// Rest of manager code...
}
public class Employee : IHasKeyProperty
{
public int Key { get; set; }
// Rest of employee code...
}
And then you can write a generic query like this:
public TEntity GetByKey<TEntity>(int key)
where TEntity : IHasKeyProperty, class
{
return this._dbContext.Set<TEntity>().FirstOrDefault(x => x.Key == key);
}
I'm assuming you're using Entity Framework Core, hence the _dbContext variable in my snippet above.
Using the method would look like this:
var myEmployee = GetByKey<Employee>(207);
var myManager = GetByKey<Manager>(101);
To extend #ajawad987's answer, if you need to support different key types you could do:
public interface IHasKeyProperty<TId>
{
TId Key { get; set; }
}
public class Manager : IHasKeyProperty<int>
{
public int Key { get; set; }
// Rest of manager code...
}
public class Employee : IHasKeyProperty<Guid>
{
public Guid Key { get; set; }
// Rest of employee code...
}
public TEntity GetByKey<TEntity, TId>(TId key) where TEntity : IHasKeyProperty<TId>
{
return this._dbContext.Set<TEntity>().FirstOrDefault(x => x.Id == key);
}
I have a class that contains a list of parameters. For example:
public class Container
{
public List<Parameter> Parameters { get; set; }
}
public class Parameter
{
puplic string Name {get; set;}
}
Class Сontainer obtained from the database through Entity Framework. Many classes contain Container. I need to ensure that all classes that contain Сontainer and also retrieved from the database containing the sorted list of Parameters. That is, the Container must sort Parameters or request step or immediately thereafter.
How this can be achieved?
Maybe write to the configuration
internal class ContainerConfiguration : EntityTypeConfiguration<Container>
{
public ContainerConfiguration()
{
ToTable("Container");
HasKey(p => p.Id);
... ???
}
}
Or wright in dataSet
protected override IQueryable<Container> DataSet(DbContext db)
{
return db.Set<ProcessMeasurer>()
.Include(it => it.Parameters.Select(p => p.Parameter));
}
Another option for solving the problem:
Create your attribute and specify which field to use for sorting by default:
public class DefaultOrderFieldAttribute : Attribute
{
public DefaultOrderFieldAttribute()
{
}
public string FieldName { get; set; }
}
[DefaultOrderField(FieldName = "ParameterName")]
public partial class Parameter
{
}
Write a Visitor, which in the case of detection of our attribute modifies select:
public class DefaultOrderVisitor : DefaultExpressionVisitor
{
public override DbExpression Visit(DbScanExpression expression)
{
const string NAMESPACE = "OrderTest";
var type =
Assembly.GetExecutingAssembly().GetType(string.Format("{0}.{1}", NAMESPACE, expression.Target.Name));
var attribute =
type.GetCustomAttributes(typeof (DefaultOrderFieldAttribute)).SingleOrDefault() as
DefaultOrderFieldAttribute;
if (attribute != null)
return expression.OrderBy(ex => ex.Property(attribute.FieldName));
return expression;
}
}
Put in our Visitor Interceptor:
public class DefaultOrderInterceptor : IDbCommandTreeInterceptor
{
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace == DataSpace.SSpace)
{
var queryCommand = interceptionContext.Result as DbQueryCommandTree;
if (queryCommand != null)
{
var newQuery = queryCommand.Query.Accept(new DefaultOrderVisitor());
interceptionContext.Result = new DbQueryCommandTree(queryCommand.MetadataWorkspace,
queryCommand.DataSpace, newQuery);
}
}
}
}
and register it in the configuration (this class just has to be in the same assembly as the model):
public class EntityFrameworkConfiguration : DbConfiguration
{
public EntityFrameworkConfiguration()
{
AddInterceptor(new DefaultOrderInterceptor());
}
}
Need to work with the entity class.
If we want to sort the collection was in all the elements that comprise it, we have to change the appropriate property.
Obvious variant - creating property setter.
private List<Parameter> _parameters;
public List<Parameter> Parameters
{
get { return _parameters; }
set { _parameters = value.OrderBy(...).ToList();
}
But the behavior of the compiler (call the setter once, and the multiple callin to the getter) gave me a reason to assume that the target collection is not put in a property all at once. The items in the query is gradually added to the collection. Therefore, sorting in setter does not always work.
Therefore, we must carry out sorting the return value
get
{
if(_parameters == null) return null;
_parameters = _parameters.OrderBy(...).ToList();
return _parameters;
}
It works. But the problem is that an appeal to the getter, and hence sorting, will be carried out when EntityFramework inserts each value. This affects the performance.
The best variant that I know at the moment is to inherit all entities from the interface with the function Prepare
public interface IEntity
{
void Prepare();
}
and implement it in each class model. Models that comprise other models cause a method to prepare, for each desired properties.
public class SomeModel : IEntity
{
public CustomType SomeProperty { get; set; }
public OneMoreCustomType AnotherProrerty { get; set; }
public void Prepare()
{
SomeProperty.Prepare();
AnotherProperty.Prepare();
}
}
For the respective classes it will take appropriate action. Including sorting.
Сall a method to prepare the Сontainer (in this case) you before using.
For example, in the Business Logic (MVPVM).
I've been trying to learn EF codefirst. One of the first things is that it won't enforce unique... So... I've tried to solve the problem by exposing a readonly IEnumerble property that forces me to use the AddProp method if I want to add anything to the collection...
When I try to do this (and this is just a "Throw Away" example below) I get the error.
Error 1 The type arguments for method 'System.Data.Entity.ModelConfiguration.EntityTypeConfiguration.HasMany(System.Linq.Expressions.Expression>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. C:\Users\User\Documents\Visual Studio 2010\Projects\ConsoleApplication3\ConsoleApplication3\Program.cs 39 9 ConsoleApplication3
any reason why?
class Program
{
static void Main(string[] args)
{
using (DC _db = new DC())
{
PrimeA p = new PrimeA { Name = "BlahGEEEER" };
p.AddProp(new Prop { comment = "Blah HI!" });
p.AddProp(new Prop { comment = "Blah HI!" });
Console.ReadLine();
_db.PrimeAs.Add(p);
_db.SaveChanges();
}
}
}
public class DC : DbContext
{
public DbSet<PrimeA> PrimeAs { get; set; }
public DbSet<PrimeB> PrimeBs { get; set; }
public DbSet<Prop> Props { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<PrimeA>()
.HasMany(p => p.Props) // <---- FAILS HERE
.WithMany();
base.OnModelCreating(modelBuilder);
}
}
public class PrimeA
{
private List<Prop> m_Props = new List<Prop>();
public int PrimeAID { get; set; }
public string Name { get; set; }
public virtual IEnumerable<Prop> Props
{
get
{
return m_Props;
}
}
public bool AddProp(Prop prop)
{
bool ret = false;
var existingResult =
from p in m_Props
where p.comment.ToLower() == prop.comment.ToLower()
select p;
if (existingResult.Count() == 0)
{
m_Props.Add(prop);
ret = true;
}
return ret;
}
}
As you can see in MSDN, EntityTypeConfiguration.HasMany expects an ICollection<TTargetEntity>. So you have to change Props in
public virtual ICollection<Prop> Props
Try using ICollection instead of IEnumerable for your Props property. That should make the error go away.
Here are a couple of posts that help explain why you want to use IList or ICollection instead of IEnumerable.
ICollection Vs List in Entity Framework
Why does the entity framework need an ICollection for lazy loading?
I would also recommend using a HashSet for your private property for Props instead of a List
Generic functions have type arguments and they try to "guess"/"infer" the type arguments, but sometimes it is confused and you must specify them explicitly. I don't know the reason why it can't infer in this case, but in your shoes I would try something like, because in this case I think it wants to know the type of the target collection.
.HasMany<Prop>(p => p.Props)
I want to declare a generic collection of objects and be able to access them through the indexer either by a key string value or by index. How do I do this? Is there is an out of the box .Net class that doesn't require sub-classing?
class Program
{
static void Main(string[] args)
{
System.Collections.Generic.WhatKindOfCollection<PageTab> myPageTabs
= new System.Collections.Generic.WhatKindOfCollection<PageTab>();
PageTab pageTab1 = new PageTab();
pageTab1.ID = "tab1";
myPageTabs.Add(pageTab1);
myPageTabs.Add(new PageTab("tab2"));
myPageTabs[0].label = "First Tab";
myPageTabs["tab2"].label = "Second Tab";
}
public class PageTab
{
public PageTab(string id)
{
this.ID = id;
}
public PageTab() { }
//Can I define ID to get the key property by default?
public string ID { get; set; }
public string label { get; set; }
public bool visible { get; set; }
}
}
It looks like you're looking for something derived from System.Collections.ObjectModel.KeyedCollections.
I don't think that the specific class you're looking for exists in the .NET framework, so you'll probably have to subclass it yourself.
KeyedCollection is a base class for objects where the key is part of the object. This means that when you access it with an integer index, you'll get back the original object instead of a KeyValueCollection.
It's been a while since I've used it, but I don't remember it being too difficult.
Edit: Another code option for you. It was easier than I remember:
public class MyKeyedCollection<TKey, TItem> : KeyedCollection<TKey, TItem>
{
public MyKeyedCollection(Func<TItem, TKey> keyFunction)
{
_keyFunction = keyFunction;
}
private Func<TItem, TKey> _keyFunction;
protected override TKey GetKeyForItem(TItem item)
{
return _keyFunction(item);
}
}
To use:
var myPageTabs = new MyKeyedCollection<String, PageTab>(i => i.ID);
Or pre-LINQ:
public class MyKeyedCollection<TKey, TItem> : KeyedCollection<TKey, TItem>
{
public MyKeyedCollection(String keyProperty)
{
_keyProperty = keyProperty;
}
private String _keyProperty;
protected override TKey GetKeyForItem(TItem item)
{
return (TKey)item.GetType().GetProperty(_keyProperty).GetValue(item, null);
}
}
and
MyKeyedCollection<String, PageTab> myPageTabs = new MyKeyedCollection<String, PageTab>("ID");
This is effectively the OrderedDictionary class. However, it is, unfortunately, not a generic class, so you'd have to include casts, or wrap it in your own collection type.
There is no generic equivelent in the base class libraries, though KeyedCollection<T,U> provides the base class infrastructure to implement your own version.
The simplest alternative is just to maintain two collections - a Dictionary<string, PageTab> and a List<PageTab>. When you create your items, you can add it to both collections, and access via the appropriate one. Since PageTab is a class, the extra overhead is minimal (since you're just storing object references). This could also be easily wrapped into a class:
public class IndexedDictionary<T, U>
{
private Dictionary<T,U> dictionary = new Dictionary<T,U>();
private List<U> list = new List<U>();
public void Add(T key, U value)
{
list.Add(value);
dictionary.Add(key, value);
}
public U this[int index]
{
get { return list[index]; }
}
public U this[T key]
{
get { return dictionary[key]; }
}
}
Granted, you'd potentially want to implement some appropriate interfaces as well (such as IEnumerable<U>), but the above would accomplish your goals as listed in the question.
Why don't you use a dictionary?
Dictionary<string, PageTab> myDictionary = new Dictionary<string, PageTab>();
myDictionary.Add("tab1", new PageTab("tab1"));
PageTab myPageTab = myDictionary["tab1"];
EDIT
To avoid typing the key twice (once as dictionary key and once in the constructor) you could extend the dictionary. Create the following class in the toplevel of your namespace:
public static class Extensions
{
public static void AddPageTab(this Dictionary<string, PageTab> mydict, PageTab pt)
{
mydict.Add(pt.ID, pt);
}
}
And you call simple add the PageTab like this:
myDictionary.AddPageTab(new PageTab("tab1"));
I have a class MyDatabaseContext that has a series of DbSet collection properties:
public DbSet<EntityA> EntitiesA { get; set; }
public DbSet<EntityB> EntitiesB { get; set; }
public DbSet<EntityC> EntitiesC { get; set; }
I need to get the name of the collection given the type of the entity.
For example, I have "EntityB" and want to get as a result "EntitiesB".
I really wanted to avoid switch-case statements, since MyDatabaseContext is generated automatically (T4 templates).
if you just want the name of the property here you go. I would just refine the answer given by hunter. You can use the same method with string as return type.
public string GetEntitiName<T>() where T : class
{
PropertyInfo propInfo = typeof(MyDatabaseContext).GetProperties().Where(p => p.PropertyType == typeof(DbSet<T>)).FirstOrDefault();
string propertyName = propInfo.Name; //The string has the property name ..
return propertyName;
}
I tried a sample similar to your situation. Try replacing List with DbSet.
class Program
{
public static void GetEntities<T>() where T : class
{
var info = typeof(TestClass1).GetProperties().Where(p => p.PropertyType == typeof(List<T>));
Console.WriteLine(info.FirstOrDefault().Name);
}
static void Main(string[] args)
{
GetEntities<int>();
Console.ReadLine();
}
}
public class TestClass1
{
public List<int> IntTest { get; set; }
public List<double> DoubleTest { get; set; }
public List<string> IStringTest { get; set; }
}
This sample works.
I know this is old page, But my answer maybe useful for other guys referring here. (like me)
I think you want to accessing EntitiesB to run a query on it, like EntitiesB.Where(a=>a.bla=="blabla"). If I'm right or another visitor of this page needs something like this, just easily use the following code:
using System.Data.Entity.Infrastructure;
using System.Data.Objects;
((IObjectContextAdapter)_dbContext).ObjectContext.CreateObjectSet<EntityB>()
Description:
_dbContext is Context class inherting from DbContext.
EntitiesB is DbSet<EntityB> defined in Context class.
Example:
Ilist result = ((IObjectContextAdapter)_dbContext).ObjectContext.CreateObjectSet<EntityB>().Where(b=>b.bla=="blabla").ToList();
Your generated file is a partial class, you could create a new file and declare a class with same name using the keyword partial, then make a method which will return the desired Collection...
I haven't actually done this myself, but it sounds like what you want to do is to use reflection to locate the property of type "DbSet" that has the appropriate generic type parameter. The following pseudo-C# should get you started:
foreach ( FieldInfo field in this.GetType() )
{
if ( field.FieldType.IsGenericType )
{
foreach ( Type param in field.FieldType.GetGenericArguments() )
{
if ( param.Name == soughtType )
{
return field.Name;
}
}
}
}