Autofac - to instantiate and to populate injected objects into a foreach constructor - autofac

i've created(instantiated) a hierarchical class structure by autofac:
order
|
--------> customerPersonDatails
| |
| ----------------->name
| |
| ------------------>surname
|--------> customerBillingDetail
| |
| ----------------->currency
| |
| ------------------>bank
|
|
--------->
what i want to do is "recursively" create the the order object and populate its property
var builder = new ContainerBuilder();
//register components
builder.RegisterType<order>().PropertiesAutowired().OnActivated(order_Init); //<-- onactivated method will be used to populate properties
builder.RegisterType<customerPersonDatails>().PropertiesAutowired();
builder.RegisterType<customerBillingDetail>().PropertiesAutowired();
public static Action<IActivatedEventArgs<order>> order_Init = (c) =>
{
c.Instance.customerPersonDatails.name = //<-- how to pass the current value provided from the foreach
c.Instance.customerPersonDatails.surname =
c.Instance.customerBillingDetail.currency =
c.Instance.customerBillingDetail.bank =
};
//iteration through my orders and create recursively the object
foreach(string currentOrder in Orders)
{
using (var scope = Container.BeginLifetimeScope())
{
//each time "resolve" is called i get a new istance of the order object with all its properties instatiated and the OnActivated method is correctly fired
//how can i pass into that method the currentOrder values in order to complete/populate the order structure with my values (currentOrder.name, currentOrder.surname, ... )
var ord = scope.Resolve<order>();
//here i have to pass currentOrder's value in some way into "order_Init"(how to do it?)
//do others stuff
ord.serialize();
}
}
the question is: how to pass the current value(currentOrder.name etc etc) into the function order_Init
i've noticed the "c" parameter of the function "order_Init" has some properties like Parameters/Context/component.... can i use one of them? how?

Here is full working example, which should help you to modify your solution
public void Test()
{
var builder = new ContainerBuilder();
builder.RegisterType<order>().PropertiesAutowired();
//builder.RegisterType<customerPersonDatails>().PropertiesAutowired();
//builder.RegisterType<customerBillingDetail>().PropertiesAutowired();
var container = builder.Build();
var Orders = new[] { "test" };
foreach (string currentOrder in Orders)
{
using (var scope = container.BeginLifetimeScope())
{
var ordFactory = scope.Resolve<order.Factory>(); //<------changed from "Resolve<order>" to "Resolve<order.Factory>"
var ord = ordFactory.Invoke(currentOrder); //<------ added in order to pass the data
}
}
}
public class order
{
//added delegate
public delegate order Factory(string currentOrder);
//added constructor
public order(string currentOrder)
{
//use the constructor parameter to populate the class property, is it correct?
this.orderCode = currentOrder;
Debug.WriteLine("I am in order constructor with currentOrder = " + currentOrder);
}
public string orderCode { get; set; }
And debug output is as expected
I am in order constructor with currentOrder = test

To achieve such possibility you should redesign your solution and invent some factory methods or Factory delegates which are supported by Autofac Delegate Factories

Related

Modifying web api 2 odata get query result

I have an Odata controller get method like bellow:
public class ProductController : ApiController
{
[MyEnableQuery(PageSize = 48, AllowedQueryOptions = AllowedQueryOptions.OrderBy | AllowedQueryOptions.Top | AllowedQueryOptions.Skip | AllowedQueryOptions.InlineCount | AllowedQueryOptions.Filter, AllowedFunctions = AllowedFunctions.SubstringOf | AllowedFunctions.ToLower)]
public IQueryable<tbDefine_Products> GetProducts([FromODataUri] int CategoryID)
{
ProductHandler _handler = new ProductHandler();
IQueryable<tbDefine_Products> _list =_handler.GetProductActiveList(CategoryID);
return _list;
}
}
Now i wants to modify my query result before sending it to clinet... i want to something like _list.Tolist() and then iterating through the result array
List<tbDefine_Products> _list2 = _list.ToList<tbDefine_Products>();
for (int i = 0; i < _list2.Count; i++)
{
/ *some code here to modify result */
}
I have read a little about ActionFilterAttribute and ActionFilterAttribute.OnActionExecuted and
HttpActionExecutedContext Classes but i dont know how implement my ideas
Seems that you have already have a implementation about EnableQuery Attribute : MyEnableQuery, you should override the method:
public virtual IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
get the query result first and then filter the result:
var result = base.ApplyQuery(queryable, queryOptions);
// filter the result.
return result;

Retrieving Navigation Properties changes context

I have a repository class with the following code to update entities that have changed.
However I get an error
"The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects."
However I cant see that the context has changed since it is a property of my repository.
Inside the repository I have
protected void InnerUpdate(params T[] items)
{
DbSet<T> dbSet = ((DbContext)this.context).Set<T>();
foreach (T item in items)
{
object id1 = item.GetProperty("Id");
T originalEntity = dbSet.Find(id1);
((DbContext)this.context).Entry(originalEntity).CurrentValues.SetValues(item);
var navProps = GetNavigationProperties(originalEntity);
foreach (var navProp in navProps)
{
//Set originalEntity prop value to modifiedEntity value
navProp.SetValue(originalEntity, navProp.GetValue(item));
}
}
}
this.context.SaveChanges(); // Error occurs here
}
public List<PropertyInfo> GetNavigationProperties(T entity )
{
var t = entity.GetType();
ObjectContext objectContex = ((IObjectContextAdapter)((DbContext)this.context)).ObjectContext;
var elementType = objectContex.CreateObjectSet<T>().EntitySet.ElementType;
var properties = new List<PropertyInfo>();
var entityType = entity.GetType();
foreach (var navigationProperty in elementType.NavigationProperties)
{
properties.Add(entityType.GetProperty(navigationProperty.Name));
}
return properties;
}
I wonder if the problem is due to calling CreateObjectSet ? Is there a different way to do this?
I am using EF6.1
I changed the code as follows and got it working
var navProps = GetNavigationProperties(originalEntity);
foreach (var navProp in navProps)
{
//Set originalEntity prop value to modifiedEntity value
var newval = (LoggedEntity) navProp.GetValue(item);
object entity = null;
if (newval != null)
{
var tp = navProp.PropertyType;
var entities = ((DbContext)this.context).Set(tp);
entity = entities.Find(newval.Id);
}
navProp.SetValue(originalEntity, entity);
}

How can I update my DTO's ID when inserting multiple new entities

I'm using EF4. I'm adding a series of new entities from a list of DTOs, and I'm not saving changes until after all of them are added. I'm wanting to set the IDs of the DTOs to what the new entities' IDs are. How on earth do I do this? Does EF provide a mechanism for this?
With a single entity I would do this:
public void InsertMyDto(MyDto a_dto)
{
var newEntity = new MyEntity
{
Name = a_dto.Name,
Type = a_dto.Type.ToString(),
Price = a_dto.Price
};
_dataContext.MyEntities.AddObject(newEntity);
_dataContext.SaveChanges();
a_dto.ID = newEntity.ID;
}
This works fine, but what do I do in this case?
public void InsertMyDtos(IEnumerable<MyDto> a_dtos)
{
foreach (var myDto in a_dtos)
{
var newEntity = new MyEntity
{
Name = myDto.Name,
Type = myDto.Type.ToString(),
Price = myDto.Price
};
// Does some validation logic against the database that might fail.
_dataContext.MyEntities.AddObject(newEntity);
}
_dataContext.SaveChanges();
// ???
}
I want to save all at once, because I have validation work (not shown above) that is done against the database and fails before it gets to SaveChanges, and if it fails I want it to fail as a whole transaction (i.e. rollback).
I don't think that EF can help you here. It even can't help you for a single instance which forces you to write a_dto.ID = newEntity.ID. The counterpart of this code for multiple entites is to keep track of the pairs of dtos and new entities:
public void InsertMyDtos(IEnumerable<MyDto> a_dtos)
{
Dictionary<MyDto, MyEntity> dict = new Dictionary<MyDto, MyEntity>();
foreach (var myDto in a_dtos)
{
var newEntity = new MyEntity
{
Name = myDto.Name,
Type = myDto.Type.ToString(),
Price = myDto.Price
};
dict.Add(myDto, newEntity);
// Does some validation logic against the database that might fail.
_dataContext.MyEntities.AddObject(newEntity);
}
_dataContext.SaveChanges();
foreach (var item in dict)
item.Key.ID = item.Value.ID; // Key is MyDto, Value is MyEntity
}

Exception when saving an object with related object EF 3.5

I get an error telling me that: "The EntityKey property can only be set when the current value of the property is null." when I try to save an object with related object.
Here's my code:
public partial class Cat{
public bool Save()
{
try
{
using (var context = new PhonebookEntities())
{
if (this.ParentCat != null)
{
if (this.ParentCat.CategoryID == 0)
context.AddToCats(this.ParentCat);
}
context.AddToCats(this);
context.SaveChanges();
}
return true;
}
catch (System.Exception)
{
return false;
}
}
And here I create a Cat object and connect it to a relatet parent Cat object and then call the save method:
var cat = new Cat()
{
CatName = "Test",
ParentCat = Cat.GetById(1)
};
cat.Save();
Let me guess - the Cat.GetById(1) looks like:
public static Cat GetById(int id)
{
using (var context = new PhonebookEntities())
{
return context.Cats.Single(c => c.Id == id);
}
}
You are using two different contexts - that is the source of the issue. The first context loads the Cat and fills it EntityKey but the second context doesn't know this instance so once you call AddToCats it will add both new Cat and ParentCat as well but it fails because new entity cannot have filled EntityKey (I know that it is not a new entity but for the new instance of the context it is!).
Add based operations always add all unknown entities in the object graph. The entity is known by the context only if the same context loaded the entity or if you called .Attach for that entity.
Because of that, this is also incorrect:
if (this.ParentCat != null)
{
if (this.ParentCat.CategoryID == 0)
context.AddToCats(this.ParentCat);
}
Unknown ParentCat will be added automatically together with the current Cat. If you call this it will also add the current Cat but the next call will try to add it again => you will probably get an exception.
This whole can be solved by two ways:
Load the ParentCat on the same context instance as you save Cat
Don't load the ParentCat and either use dummy class or try to set EntityKey
Dummy class approach:
var parentCat = new Cat() { Id = 1 };
context.Cats.Attach(parentCat); // Use correct entity set name
var cat = new Cat()
{
CatName = "Test",
ParentCat = parentCat
};
cat.Save();
EntityKey aproach (this is more like a guess):
var cat = new Cat()
{
CatName = "Test",
// I hope ParentCatReference will be initialized
ParentCatReference.EntityKey = new EntityKey("Cats", "Id", 1) // Use correct entity set name
};
cat.Save();

Using DataAnnotations (DisplayColumn) in WCF RIA Services

I have created an entity framework 4.0 (DB-First) model, added my partial classes and used DataAnnotations on them to have a perfect UI on the client.
I have some relations between my tables and used DisplayColumn on top my classes. e.g. I have a User class that has [DataColumn("UserName")] attribute on top of the class. And a Message class which has "public User Sender" which has [Include] attribute on top of the property.
Also, I have used .Include("User") in my DomainService to load the User who's related to a message.
But in my datagrid, I see User : (UserID) (UserID=Key property of User entity) instead of UserName that I have specified. I looked in the generated code in my SL project and it correctly decorated my User class with DisplayColumn attribute. But still, I cannot see UserName in my grid.
Any help would be greatly appreciated.
Update: Here's my question in code:
As I have mentioned, Owner, UserName, MessageId, UserId have been defined in my auto-generated model. UserMeta class has nothing special.
[MetadataType(typeof(MessageMeta))]
public partial class Message
{
}
public class MessageMeta
{
[Include()]
[Display(Name = "Belongs to", Order = 4)]
[Association("Message_User","MessageId","UserId",IsForeignKey = true)]
public virtual User Owner { get; set; }
}
[MetadataType(typeof(UserMeta))]
[DisplayColumn("UserName")]
public partial class User
{
}
In my DomainService:
public IQueryable<Message> GetMessages()
{
return this.ObjectContext.Messages.Include("Owner");
}
At last, I had to use Reflection. For DataGrid:
private void OnAutoGenerateColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
//Need to get an array, but should always just have a single DisplayColumnAttribute
var atts = e.PropertyType.GetCustomAttributes(typeof(DisplayColumnAttribute),true);
foreach (DisplayColumnAttribute d in atts)
{
DataGridTextColumn col = (DataGridTextColumn)e.Column;
//Make sure that we always have the base path
if(col.Binding.Path.Path!="")
{
col.Binding = new Binding()
{
Path = new PropertyPath(col.Binding.Path.Path + "." + d.DisplayColumn)
};
}
//Only do the first one, just in case we have more than one in metadata
break;
}
}
And for Telerik RadGridView:
var column = e.Column as GridViewDataColumn;
if (column == null)
{
return;
}
// Need to get an array, but should always just have a single DisplayColumnAttribute
var atts = column.DataType.GetCustomAttributes(typeof(DisplayColumnAttribute), true);
foreach (DisplayColumnAttribute d in atts)
{
// Make sure that we always have the base path
if (column.DataMemberBinding.Path.Path != "")
{
column.DataMemberBinding = new Binding()
{
Path = new PropertyPath(column.DataMemberBinding.Path.Path + "." + d.DisplayColumn)
};
}
// Only do the first one, just in case we have more than one in metadata
break;
}